214 lines
8.2 KiB
C#
214 lines
8.2 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Fusion;
|
|
|
|
namespace OnlyScove.Scripts
|
|
{
|
|
[RequireComponent(typeof(CharacterController), typeof(InputReader), typeof(Animator))]
|
|
[RequireComponent(typeof(PlayerStats), typeof(PlayerInteraction), typeof(PlayerMovement))]
|
|
[RequireComponent(typeof(PlayerAnimationHandler))]
|
|
public class PlayerStateMachine : NetworkBehaviour
|
|
{
|
|
[field: Header("References")]
|
|
[field: SerializeField] public CharacterController Controller { get; private set; }
|
|
[field: SerializeField] public virtual InputReader Input { get; private set; }
|
|
[field: SerializeField] public Animator Anim { get; private set; }
|
|
[field: SerializeField] public EnvironmentScanner Scanner { get; private set; }
|
|
public CameraController Cam { get; private set; }
|
|
|
|
[Header("Modules")]
|
|
public PlayerStats Stats;
|
|
public PlayerInteraction Interaction;
|
|
public PlayerMovement Movement;
|
|
public PlayerAnimationHandler AnimationHandler;
|
|
|
|
[Networked] public Quaternion NetworkedCameraRotation { get; set; }
|
|
[Networked] public Vector2 NetworkedMoveInput { get; set; }
|
|
[Networked] public float NetworkedSpeed { get; set; }
|
|
|
|
// Pass-through properties for State Compatibility
|
|
public Vector2 MoveInput { get; private set; }
|
|
public bool IsSprintHeld { get; private set; }
|
|
public float VelocityY { get => Movement.VelocityY; set => Movement.VelocityY = value; }
|
|
public bool IsGrounded => Movement.IsGrounded;
|
|
public bool WasGrounded => Movement.WasGrounded;
|
|
|
|
public float WalkSpeed => Movement.WalkSpeed;
|
|
public float RunSpeed => Movement.RunSpeed;
|
|
public float SprintSpeed => Movement.SprintSpeed;
|
|
public float SneakSpeed => Movement.SneakSpeed;
|
|
public float DashForce => Movement.DashForce;
|
|
public float JumpHeight => Movement.JumpHeight;
|
|
public float ThrustDownwardForce => Movement.ThrustDownwardForce;
|
|
public float Gravity => Movement.Gravity;
|
|
|
|
public float InteractionRange => Interaction.InteractionRange;
|
|
public LayerMask InteractionMask => Interaction.InteractionMask;
|
|
|
|
public static PlayerStateMachine Local { get; private set; }
|
|
public string CurrentStateName => currentState != null ? currentState.GetType().Name : "None";
|
|
|
|
public Quaternion CameraRotation
|
|
{
|
|
get
|
|
{
|
|
if (Runner != null && Runner.IsRunning && Object != null) return NetworkedCameraRotation;
|
|
return Cam != null ? Cam.PlanarRotation : transform.rotation;
|
|
}
|
|
}
|
|
|
|
private PlayerBaseState currentState;
|
|
private bool hasControl = true;
|
|
private float localAnimatorSpeed;
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
Controller = GetComponent<CharacterController>();
|
|
Input = GetComponent<InputReader>();
|
|
Anim = GetComponentInChildren<Animator>();
|
|
Scanner = GetComponent<EnvironmentScanner>();
|
|
|
|
Stats = GetComponent<PlayerStats>();
|
|
Interaction = GetComponent<PlayerInteraction>();
|
|
Movement = GetComponent<PlayerMovement>();
|
|
AnimationHandler = GetComponent<PlayerAnimationHandler>();
|
|
|
|
AnimationHandler.Initialize(Anim);
|
|
Movement.Initialize(Controller);
|
|
Interaction.Initialize(Scanner);
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
if (Runner == null || !Runner.IsRunning) InitializePlayer();
|
|
}
|
|
|
|
public override void Spawned()
|
|
{
|
|
InitializePlayer();
|
|
if (Object != null && !Object.HasInputAuthority && Runner.IsClient)
|
|
{
|
|
if (Controller != null) Controller.enabled = false;
|
|
}
|
|
}
|
|
|
|
private void InitializePlayer()
|
|
{
|
|
if (currentState == null) SwitchState(new PlayerIdleState(this));
|
|
|
|
bool isOffline = Runner == null || !Runner.IsRunning;
|
|
if (isOffline || (Object != null && Object.HasInputAuthority))
|
|
{
|
|
Local = this;
|
|
CameraController cameraController = GameObject.FindAnyObjectByType<CameraController>();
|
|
if (cameraController != null)
|
|
{
|
|
Cam = cameraController;
|
|
Cam.followTarget = transform;
|
|
Cam.inputReader = Input;
|
|
}
|
|
Input.OnNextInteractEvent += Interaction.NextInteract;
|
|
Input.OnPreviousInteractEvent += Interaction.PreviousInteract;
|
|
if (Controller != null) Controller.enabled = true;
|
|
}
|
|
}
|
|
|
|
public void Rotate(Vector3 moveDirection, float deltaTime)
|
|
{
|
|
Movement.Rotate(transform, moveDirection, deltaTime);
|
|
}
|
|
|
|
public void Move(Vector3 velocity, float animatorSpeed, float deltaTime)
|
|
{
|
|
bool canMove = (Runner == null || !Runner.IsRunning) || Object.HasInputAuthority || Runner.IsServer;
|
|
if (!canMove) return;
|
|
|
|
Movement.Move(Controller, velocity, deltaTime);
|
|
|
|
localAnimatorSpeed = animatorSpeed;
|
|
if (Object != null && Object.HasStateAuthority)
|
|
{
|
|
NetworkedSpeed = animatorSpeed;
|
|
NetworkedMoveInput = MoveInput;
|
|
}
|
|
UpdateAnimator(deltaTime);
|
|
}
|
|
|
|
private void UpdateAnimator(float deltaTime)
|
|
{
|
|
float speedValue = (Runner == null || !Runner.IsRunning || Object.HasInputAuthority) ? localAnimatorSpeed : NetworkedSpeed;
|
|
Vector2 inputVector = (Runner == null || !Runner.IsRunning || Object.HasInputAuthority) ? MoveInput : NetworkedMoveInput;
|
|
AnimationHandler.UpdateAnimator(speedValue, inputVector, deltaTime);
|
|
}
|
|
|
|
public override void FixedUpdateNetwork()
|
|
{
|
|
bool isRunning = Runner != null && Runner.IsRunning;
|
|
if (Object == null && isRunning) return;
|
|
|
|
// Proxy Sync
|
|
if (isRunning && Movement.NetworkedPosition != Vector3.zero && !Object.HasInputAuthority)
|
|
{
|
|
Controller.enabled = false;
|
|
transform.position = Movement.NetworkedPosition;
|
|
Controller.enabled = true;
|
|
}
|
|
|
|
if (GetInput(out PlayerInputData data))
|
|
{
|
|
MoveInput = data.Direction;
|
|
IsSprintHeld = data.sprint;
|
|
if (isRunning) NetworkedCameraRotation = data.rot;
|
|
}
|
|
else if (!isRunning)
|
|
{
|
|
MoveInput = new Vector2(UnityEngine.Input.GetAxisRaw("Horizontal"), UnityEngine.Input.GetAxisRaw("Vertical"));
|
|
IsSprintHeld = UnityEngine.Input.GetKey(KeyCode.LeftShift);
|
|
}
|
|
|
|
if (!isRunning || Object.HasInputAuthority || Runner.IsServer)
|
|
{
|
|
if (hasControl)
|
|
{
|
|
Movement.CheckGround(transform);
|
|
Interaction.UpdateInteractables();
|
|
currentState?.Tick(isRunning ? Runner.DeltaTime : Time.fixedDeltaTime);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UpdateAnimator(Runner.DeltaTime);
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (Runner == null || !Runner.IsRunning) FixedUpdateNetwork();
|
|
}
|
|
|
|
public IInteractable GetInteractable() => Interaction.GetInteractable();
|
|
public void SetGroundCheck(float radius, Vector3 offset) => Movement.SetGroundCheck(radius, offset);
|
|
|
|
public void SwitchState(PlayerBaseState newState)
|
|
{
|
|
currentState?.Exit();
|
|
currentState = newState;
|
|
currentState?.Enter();
|
|
}
|
|
|
|
public void SetControl(bool control)
|
|
{
|
|
hasControl = control;
|
|
if (Controller != null) Controller.enabled = control;
|
|
if (!control) AnimationHandler.SetSpeed(0f);
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
if (Movement == null) return;
|
|
Gizmos.color = new Color(0, 1, 0, 0.5f);
|
|
Gizmos.DrawSphere(transform.TransformPoint(Movement.GroundCheckOffset), Movement.GroundCheckRadius);
|
|
}
|
|
}
|
|
}
|