Organize custom scripts and Shared under Assets/Scripts, and delete assembly definition files
This commit is contained in:
263
Assets/Scripts/Baba_yaga/Network/FusionClientMovementBridge.cs
Normal file
263
Assets/Scripts/Baba_yaga/Network/FusionClientMovementBridge.cs
Normal file
@@ -0,0 +1,263 @@
|
||||
using UnityEngine;
|
||||
using Fusion;
|
||||
using Opsive.Shared.Events;
|
||||
using Opsive.UltimateCharacterController.Networking;
|
||||
using Opsive.UltimateCharacterController.Networking.Character;
|
||||
using Opsive.UltimateCharacterController.Character;
|
||||
using Opsive.UltimateCharacterController.Character.Abilities;
|
||||
using Opsive.UltimateCharacterController.Character.Abilities.Items;
|
||||
using Opsive.UltimateCharacterController.Camera;
|
||||
using Opsive.UltimateCharacterController.Input;
|
||||
using Baba_yaga;
|
||||
using Opsive.UltimateCharacterController.Game;
|
||||
namespace Baba_yaga.Network
|
||||
{
|
||||
// Ensure Opsive components load before this
|
||||
[DefaultExecutionOrder(100)]
|
||||
[UnityEngine.Scripting.APIUpdating.MovedFrom(true, sourceNamespace: "Hallucinate.Network", sourceAssembly: "Opsive.UltimateCharacterController")]
|
||||
public class FusionClientMovementBridge : NetworkBehaviour, INetworkInfo, INetworkCharacter, ILookSource
|
||||
{
|
||||
public static FusionClientMovementBridge Local { get; private set; }
|
||||
|
||||
[Networked]
|
||||
public PlayerInputData SyncInput { get; set; }
|
||||
|
||||
private UltimateCharacterLocomotion m_CharacterLocomotion;
|
||||
private UltimateCharacterLocomotionHandler m_LocoHandler;
|
||||
private Opsive.UltimateCharacterController.Input.PlayerInput m_PlayerInput;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
m_CharacterLocomotion = GetComponent<UltimateCharacterLocomotion>();
|
||||
m_LocoHandler = GetComponent<UltimateCharacterLocomotionHandler>();
|
||||
m_PlayerInput = GetComponent<Opsive.UltimateCharacterController.Input.PlayerInput>();
|
||||
}
|
||||
|
||||
public override void Spawned()
|
||||
{
|
||||
// Because we passed State Authority to the client in BasicSpawner,
|
||||
// HasStateAuthority is true ONLY for the local player.
|
||||
// Check HasInputAuthority as well so the client's character correctly activates input!
|
||||
bool isLocal = Object.HasStateAuthority || Object.HasInputAuthority;
|
||||
|
||||
if (isLocal)
|
||||
{
|
||||
Local = this;
|
||||
}
|
||||
|
||||
// 1. Isolate Input: Only the local player should read keyboard/mouse inputs.
|
||||
if (m_LocoHandler != null) m_LocoHandler.enabled = isLocal;
|
||||
|
||||
var activeInput = GetComponent<UnityInput>(); // Corrected class reference
|
||||
if (activeInput != null) activeInput.enabled = isLocal;
|
||||
|
||||
// 2. Isolate Camera: Only attach the camera if this is the local player.
|
||||
if (isLocal)
|
||||
{
|
||||
var cameraController = UnityEngine.Object.FindFirstObjectByType<CameraController>();
|
||||
if (cameraController != null)
|
||||
{
|
||||
cameraController.Character = gameObject;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For remote players, register this bridge as the LookSource
|
||||
EventHandler.ExecuteEvent<ILookSource>(gameObject, "OnCharacterAttachLookSource", this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Despawned(NetworkRunner runner, bool hasState)
|
||||
{
|
||||
if (Local == this)
|
||||
{
|
||||
Local = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void FixedUpdateNetwork()
|
||||
{
|
||||
// ONLY attempt to get input if we are the Host (StateAuthority) or the owning Client (InputAuthority).
|
||||
// Calling GetInput on a Proxy (someone else's character) is what causes the GetTypeKey exception!
|
||||
if (Object.HasStateAuthority || Object.HasInputAuthority)
|
||||
{
|
||||
if (GetInput<PlayerInputData>(out var input))
|
||||
{
|
||||
// Apply input locally so the Client's character actually moves predicted!
|
||||
if (m_CharacterLocomotion != null)
|
||||
{
|
||||
m_CharacterLocomotion.InputVector = input.InputVector;
|
||||
m_CharacterLocomotion.RawInputVector = input.RawInputVector;
|
||||
m_CharacterLocomotion.DeltaRotation = input.DeltaRotation;
|
||||
}
|
||||
|
||||
if (Object.HasStateAuthority)
|
||||
{
|
||||
SyncInput = input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we do NOT have StateAuthority AND we do NOT have InputAuthority, it's a Proxy (remote player).
|
||||
if (Object.IsProxy)
|
||||
{
|
||||
// Ensure look source is attached for remote players
|
||||
if (m_CharacterLocomotion != null && m_CharacterLocomotion.LookSource != (ILookSource)this)
|
||||
{
|
||||
EventHandler.ExecuteEvent<ILookSource>(gameObject, "OnCharacterAttachLookSource", this);
|
||||
}
|
||||
|
||||
// Sync the movement inputs to KinematicObjectManager so it moves the remote player character
|
||||
if (m_CharacterLocomotion != null && m_CharacterLocomotion.KinematicObjectIndex != -1)
|
||||
{
|
||||
KinematicObjectManager.SetCharacterMovementInput(
|
||||
m_CharacterLocomotion.KinematicObjectIndex,
|
||||
SyncInput.Direction.x,
|
||||
SyncInput.Direction.y
|
||||
);
|
||||
}
|
||||
|
||||
if (m_CharacterLocomotion != null)
|
||||
{
|
||||
m_CharacterLocomotion.InputVector = SyncInput.InputVector;
|
||||
m_CharacterLocomotion.RawInputVector = SyncInput.RawInputVector;
|
||||
m_CharacterLocomotion.DeltaRotation = SyncInput.DeltaRotation;
|
||||
|
||||
// Sync remote abilities based on state
|
||||
UpdateRemoteAbility<SpeedChange>(SyncInput.sprint);
|
||||
UpdateRemoteAbility<Jump>(SyncInput.jump);
|
||||
UpdateRemoteAbility<HeightChange>(SyncInput.crouch);
|
||||
UpdateRemoteAbility<Aim>(SyncInput.aim);
|
||||
UpdateRemoteAbility<Use>(SyncInput.use);
|
||||
UpdateRemoteAbility<Reload>(SyncInput.reload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRemoteAbility<T>(bool shouldBeActive) where T : Ability
|
||||
{
|
||||
if (m_CharacterLocomotion == null) return;
|
||||
var ability = m_CharacterLocomotion.GetAbility<T>();
|
||||
if (ability != null)
|
||||
{
|
||||
if (shouldBeActive && !ability.IsActive)
|
||||
{
|
||||
m_CharacterLocomotion.TryStartAbility(ability, true);
|
||||
}
|
||||
else if (!shouldBeActive && ability.IsActive)
|
||||
{
|
||||
m_CharacterLocomotion.TryStopAbility(ability, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerInputData GetLocalInputData()
|
||||
{
|
||||
var data = new PlayerInputData();
|
||||
if (m_CharacterLocomotion == null) return data;
|
||||
|
||||
if (m_PlayerInput != null)
|
||||
{
|
||||
data.Direction = new Vector2(m_PlayerInput.GetAxisRaw("Horizontal"), m_PlayerInput.GetAxisRaw("Vertical"));
|
||||
|
||||
// Sync abilities active states or buttons
|
||||
data.sprint = IsAbilityActive<SpeedChange>() || m_PlayerInput.GetButton("Change Speeds");
|
||||
data.jump = IsAbilityActive<Jump>() || m_PlayerInput.GetButton("Jump");
|
||||
data.crouch = IsAbilityActive<HeightChange>() || m_PlayerInput.GetButton("Crouch");
|
||||
data.aim = IsAbilityActive<Aim>() || m_PlayerInput.GetButton("Aim");
|
||||
data.use = IsAbilityActive<Use>() || m_PlayerInput.GetButton("Fire1");
|
||||
data.reload = IsAbilityActive<Reload>() || m_PlayerInput.GetButton("Reload");
|
||||
}
|
||||
|
||||
// Sync locomotion internal state parameters
|
||||
data.InputVector = m_CharacterLocomotion.InputVector;
|
||||
data.RawInputVector = m_CharacterLocomotion.RawInputVector;
|
||||
data.DeltaRotation = m_CharacterLocomotion.DeltaRotation;
|
||||
data.rot = transform.rotation;
|
||||
|
||||
// Sync Look direction and look pitch
|
||||
var lookSource = m_CharacterLocomotion.LookSource;
|
||||
if (lookSource != null)
|
||||
{
|
||||
data.LookPitch = lookSource.Pitch;
|
||||
data.LookDirection = lookSource.LookDirection(true);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private bool IsAbilityActive<T>() where T : Ability
|
||||
{
|
||||
if (m_CharacterLocomotion == null) return false;
|
||||
var ability = m_CharacterLocomotion.GetAbility<T>();
|
||||
return ability != null && ability.IsActive;
|
||||
}
|
||||
|
||||
// --- ILookSource Implementation ---
|
||||
public GameObject GameObject => gameObject;
|
||||
public Transform Transform => transform;
|
||||
public float LookDirectionDistance => 1f;
|
||||
public float Pitch => SyncInput.LookPitch;
|
||||
|
||||
public Vector3 LookPosition()
|
||||
{
|
||||
var animator = GetComponent<Animator>();
|
||||
if (animator != null)
|
||||
{
|
||||
var head = animator.GetBoneTransform(HumanBodyBones.Head);
|
||||
if (head != null) return head.position;
|
||||
}
|
||||
return transform.position + Vector3.up * 1.5f;
|
||||
}
|
||||
|
||||
public Vector3 LookDirection(bool characterLookDirection)
|
||||
{
|
||||
return SyncInput.LookDirection == Vector3.zero ? transform.forward : SyncInput.LookDirection;
|
||||
}
|
||||
|
||||
public Vector3 LookDirection(Vector3 lookPosition, bool characterLookDirection, int layerMask, bool useRecoil)
|
||||
{
|
||||
return LookDirection(characterLookDirection);
|
||||
}
|
||||
|
||||
// --- INetworkInfo Implementation ---
|
||||
|
||||
public bool IsLocalPlayer() => Object.HasStateAuthority || Object.HasInputAuthority;
|
||||
public bool IsServer() => Runner.IsServer;
|
||||
|
||||
// Return FALSE because we are doing Client-Authoritative movement!
|
||||
public bool IsServerAuthoritative() => false;
|
||||
|
||||
|
||||
// --- INetworkCharacter Implementation ---
|
||||
// Since we are using Fusion's NetworkTransform to sync position,
|
||||
// we can leave these methods empty. Opsive will handle the local
|
||||
// movement, and Fusion's NetworkTransform will drag the remote players.
|
||||
|
||||
public void SetPosition(Vector3 position, bool snapAnimator) { }
|
||||
public void SetRotation(Quaternion rotation, bool snapAnimator) { }
|
||||
public void SetPositionAndRotation(Vector3 position, Quaternion rotation, bool snapAnimator) { }
|
||||
public void ResetRotationPosition() { }
|
||||
public void SetActive(bool active, bool uiEvent) { }
|
||||
|
||||
public void LoadDefaultLoadout() { }
|
||||
public void EquipUnequipItem(uint itemID, int slotID, bool equip) { }
|
||||
public void ItemIdentifierPickup(uint id, int amount, int slot, bool immediate, bool force) { }
|
||||
public void RemoveAllItems() { }
|
||||
|
||||
public void Fire(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction, float strength) { }
|
||||
public void StartItemReload(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction) { }
|
||||
public void ReloadItem(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction, bool fullClip) { }
|
||||
public void ItemReloadComplete(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction, bool success, bool immediateReload) { }
|
||||
public void MeleeHitCollider(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction, int hitboxIndex, RaycastHit raycastHit, GameObject hitGameObject, UltimateCharacterLocomotion hitCharacterLocomotion) { }
|
||||
|
||||
public void ThrowItem(Opsive.UltimateCharacterController.Items.Actions.ItemAction action) { }
|
||||
public void EnableThrowableObjectMeshRenderers(Opsive.UltimateCharacterController.Items.Actions.ItemAction action) { }
|
||||
public void StartStopBeginEndMagicActions(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, bool begin, bool start) { }
|
||||
public void MagicCast(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, int index, uint castID, Vector3 dir, Vector3 target) { }
|
||||
public void MagicImpact(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, uint castID, GameObject source, GameObject target, Vector3 pos, Vector3 norm) { }
|
||||
public void StopMagicCast(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, int index, uint castID) { }
|
||||
public void ToggleFlashlight(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, bool active) { }
|
||||
public void PushRigidbody(Rigidbody rb, Vector3 force, Vector3 point) { }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user