remove obstacle
This commit is contained in:
@@ -37,7 +37,7 @@ namespace Invector.vCharacterController
|
||||
// public GenericInput rollInput = new GenericInput("Q", "B", "B");
|
||||
|
||||
[Header("New Input System")]
|
||||
public OnlyScove.Scripts.InputReader inputReader;
|
||||
public InputReader inputReader;
|
||||
|
||||
protected bool _lockInput = false;
|
||||
[HideInInspector] public virtual bool lockInput { get { return _lockInput; } set { _lockInput = value; } }
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 396f655b903645b69c30bb8c579f08cb
|
||||
timeCreated: 1780541958
|
||||
@@ -1,12 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Game_Definition
|
||||
{
|
||||
[CreateAssetMenu(fileName = "ParkourAction", menuName = "Parkour System/New Parkour Action")]
|
||||
public class ParkourAction : ScriptableObject
|
||||
{
|
||||
[SerializeField] string animationName;
|
||||
[SerializeField] private float minHeight;
|
||||
[SerializeField] private float maxHeight;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 985468dedaf32f44191b2cc29c813c8c
|
||||
@@ -1,49 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerAirDashState : PlayerBaseState
|
||||
{
|
||||
private float dashDuration = 0.3f;
|
||||
private float dashTimer;
|
||||
private Vector3 dashDirection;
|
||||
|
||||
public PlayerAirDashState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
dashTimer = dashDuration;
|
||||
stateMachine.Anim.SetTrigger("Dash");
|
||||
|
||||
// ĐÚNG: Sử dụng MoveInput đã đồng bộ
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
if (input != Vector2.zero)
|
||||
{
|
||||
dashDirection = new Vector3(input.x, 0, input.y).normalized;
|
||||
dashDirection = stateMachine.CameraRotation * dashDirection;
|
||||
}
|
||||
else
|
||||
{
|
||||
dashDirection = stateMachine.transform.forward;
|
||||
}
|
||||
dashDirection.y = 0;
|
||||
dashDirection.Normalize();
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
dashTimer -= deltaTime;
|
||||
|
||||
// Sử dụng hàm Move tập trung
|
||||
stateMachine.Move(dashDirection * stateMachine.DashForce, 1.0f, deltaTime);
|
||||
|
||||
if (dashTimer <= 0)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerFallState(stateMachine));
|
||||
}
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
public override void Exit() {}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43c3e5e400fea604fb432f84d5dd9ce1
|
||||
@@ -1,161 +0,0 @@
|
||||
using UnityEngine;
|
||||
using Fusion;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerAnimationHandler : NetworkBehaviour
|
||||
{
|
||||
[Header("Animator Settings")]
|
||||
[SerializeField] private string speedParamName = "Speed";
|
||||
[SerializeField] private string velocityXParamName = "Velocity X";
|
||||
[SerializeField] private string velocityZParamName = "Velocity Z";
|
||||
[SerializeField] private string groundedParamName = "Grounded";
|
||||
[SerializeField] private float animationDamping = 0.2f;
|
||||
|
||||
[Header("Visual Correction")]
|
||||
[SerializeField] private float visualOffsetY = 0f;
|
||||
[SerializeField] private Transform modelTransform;
|
||||
private CharacterController controller;
|
||||
|
||||
private Animator anim;
|
||||
private int speedHash;
|
||||
private int velocityXHash;
|
||||
private int velocityZHash;
|
||||
private int groundedHash;
|
||||
|
||||
private readonly System.Collections.Generic.HashSet<int> parameterHashes = new System.Collections.Generic.HashSet<int>();
|
||||
|
||||
public void Initialize(Animator animator)
|
||||
{
|
||||
this.anim = animator;
|
||||
this.controller = GetComponentInParent<CharacterController>();
|
||||
|
||||
// Auto-assign modelTransform if null
|
||||
if (modelTransform == null && anim != null) modelTransform = anim.transform;
|
||||
|
||||
if (anim != null)
|
||||
{
|
||||
Debug.Log($"<color=green>[AnimationHandler]</color> Animator found on: {anim.gameObject.name}");
|
||||
parameterHashes.Clear();
|
||||
foreach (AnimatorControllerParameter param in anim.parameters)
|
||||
{
|
||||
parameterHashes.Add(param.nameHash);
|
||||
}
|
||||
|
||||
int speedHashCheck = Animator.StringToHash(speedParamName);
|
||||
if (!parameterHashes.Contains(speedHashCheck) && parameterHashes.Contains(Animator.StringToHash("Blend")))
|
||||
{
|
||||
speedParamName = "Blend";
|
||||
speedHash = Animator.StringToHash(speedParamName);
|
||||
Debug.Log($"<color=yellow>[AnimationHandler]</color> 'Speed' not found, using 'Blend' instead.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("<color=red>[AnimationHandler]</color> FAILED to find Animator! Please check your Prefab.");
|
||||
}
|
||||
|
||||
speedHash = Animator.StringToHash(speedParamName);
|
||||
velocityXHash = Animator.StringToHash(velocityXParamName);
|
||||
velocityZHash = Animator.StringToHash(velocityZParamName);
|
||||
groundedHash = Animator.StringToHash(groundedParamName);
|
||||
}
|
||||
|
||||
private bool wasGroundedInAnimator;
|
||||
|
||||
public void UpdateAnimator(float speed, Vector2 moveInput, bool isGrounded, float deltaTime)
|
||||
{
|
||||
if (anim == null) return;
|
||||
|
||||
// Snap to zero if speed is very low to force Idle
|
||||
float targetSpeed = speed < 0.05f ? 0f : speed;
|
||||
float damping = (targetSpeed == 0f || targetSpeed > 0.9f) ? 0f : animationDamping;
|
||||
|
||||
if (parameterHashes.Contains(speedHash)) anim.SetFloat(speedHash, targetSpeed, damping, deltaTime);
|
||||
if (parameterHashes.Contains(velocityXHash)) anim.SetFloat(velocityXHash, moveInput.x * targetSpeed, damping, deltaTime);
|
||||
if (parameterHashes.Contains(velocityZHash)) anim.SetFloat(velocityZHash, moveInput.y * targetSpeed, damping, deltaTime);
|
||||
|
||||
// Quan trọng: Cập nhật biến Grounded cho Animator
|
||||
if (parameterHashes.Contains(groundedHash)) anim.SetBool(groundedHash, isGrounded);
|
||||
|
||||
// Nếu đang ở trên mặt đất, đảm bảo reset các trạng thái nhảy/rơi
|
||||
if (isGrounded)
|
||||
{
|
||||
SafeSetBool("IsJumping", false);
|
||||
SafeSetBool("IsFalling", false);
|
||||
|
||||
var stateInfo = anim.GetCurrentAnimatorStateInfo(0);
|
||||
if (stateInfo.IsName("Jump") || stateInfo.IsName("Fall") || stateInfo.IsName("Airborne"))
|
||||
{
|
||||
anim.CrossFadeInFixedTime("Locomotion Ground", 0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
wasGroundedInAnimator = isGrounded;
|
||||
}
|
||||
|
||||
public void SafeSetTrigger(string name)
|
||||
{
|
||||
if (anim == null) return;
|
||||
int hash = Animator.StringToHash(name);
|
||||
if (parameterHashes.Contains(hash)) anim.SetTrigger(hash);
|
||||
}
|
||||
|
||||
public void SafeSetTrigger(int hash)
|
||||
{
|
||||
if (anim == null) return;
|
||||
if (parameterHashes.Contains(hash)) anim.SetTrigger(hash);
|
||||
}
|
||||
|
||||
public void SafeResetTrigger(string name)
|
||||
{
|
||||
if (anim == null) return;
|
||||
int hash = Animator.StringToHash(name);
|
||||
if (parameterHashes.Contains(hash)) anim.ResetTrigger(hash);
|
||||
}
|
||||
|
||||
public void SafeResetTrigger(int hash)
|
||||
{
|
||||
if (anim == null) return;
|
||||
if (parameterHashes.Contains(hash)) anim.ResetTrigger(hash);
|
||||
}
|
||||
|
||||
public void SafeSetBool(string name, bool value)
|
||||
{
|
||||
if (anim == null) return;
|
||||
int hash = Animator.StringToHash(name);
|
||||
if (parameterHashes.Contains(hash)) anim.SetBool(hash, value);
|
||||
}
|
||||
|
||||
public void SafeSetFloat(int hash, float value, float dampTime, float deltaTime)
|
||||
{
|
||||
if (anim == null) return;
|
||||
if (parameterHashes.Contains(hash)) anim.SetFloat(hash, value, dampTime, deltaTime);
|
||||
}
|
||||
|
||||
public void SetSpeed(float speed)
|
||||
{
|
||||
if (anim != null && parameterHashes.Contains(speedHash))
|
||||
anim.SetFloat(speedHash, speed);
|
||||
}
|
||||
|
||||
public void ForceLocomotion()
|
||||
{
|
||||
if (anim != null) anim.CrossFadeInFixedTime("Locomotion Ground", 0.1f);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (modelTransform != null && controller != null)
|
||||
{
|
||||
// Automatically snap mesh to the bottom of the CharacterController capsule
|
||||
// This fixes hovering/tiptoeing issues regardless of animation offsets
|
||||
Vector3 targetPos = modelTransform.localPosition;
|
||||
float bottomY = controller.center.y - (controller.height / 2f);
|
||||
targetPos.y = bottomY + visualOffsetY;
|
||||
|
||||
modelTransform.localPosition = targetPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 965ec86f3b9695640bdc85e624d0e9e7
|
||||
@@ -1,25 +0,0 @@
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public abstract class PlayerBaseState
|
||||
{
|
||||
protected PlayerStateMachine stateMachine;
|
||||
|
||||
// Constructor to pass the state machine reference
|
||||
public PlayerBaseState(PlayerStateMachine stateMachine)
|
||||
{
|
||||
this.stateMachine = stateMachine;
|
||||
}
|
||||
|
||||
// Called once when entering the state
|
||||
public abstract void Enter();
|
||||
|
||||
// Called every frame (equivalent to Update)
|
||||
public abstract void Tick(float deltaTime);
|
||||
|
||||
// Called every physics frame (equivalent to FixedUpdate)
|
||||
public abstract void PhysicsTick(float fixedDeltaTime);
|
||||
|
||||
// Called once before switching to a new state
|
||||
public abstract void Exit();
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af622221f1750284885366b02b8bfbba
|
||||
@@ -1,97 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerCrouchState : PlayerBaseState
|
||||
{
|
||||
private readonly int isCrouchingHash = Animator.StringToHash("IsCrouching");
|
||||
private readonly int crouchSpeedHash = Animator.StringToHash("CrouchSpeed");
|
||||
private const float AnimatorDampTime = 0.1f;
|
||||
|
||||
public PlayerCrouchState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.Anim.SetBool(isCrouchingHash, true);
|
||||
stateMachine.Input.OnCrouchEvent += OnCrouchToggle;
|
||||
stateMachine.Input.OnDodgeEvent += OnDodge;
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
Vector2 moveInput = stateMachine.Input.MoveInput;
|
||||
float currentSpeed = 0f;
|
||||
float animValue = 0f;
|
||||
|
||||
if (moveInput == Vector2.zero)
|
||||
{
|
||||
animValue = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stateMachine.Input.IsSprintHeld)
|
||||
{
|
||||
currentSpeed = stateMachine.SneakSpeed;
|
||||
animValue = 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSpeed = stateMachine.WalkSpeed;
|
||||
animValue = 1.0f;
|
||||
}
|
||||
|
||||
Vector3 moveDirection = new Vector3(moveInput.x, 0f, moveInput.y).normalized;
|
||||
|
||||
if (stateMachine.Cam != null)
|
||||
{
|
||||
moveDirection = stateMachine.Cam.PlanarRotation * moveDirection;
|
||||
}
|
||||
|
||||
// Apply horizontal movement
|
||||
Vector3 movement = moveDirection * currentSpeed;
|
||||
|
||||
// Apply gravity
|
||||
if (stateMachine.Controller.isGrounded)
|
||||
{
|
||||
stateMachine.VelocityY = -2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
stateMachine.VelocityY += stateMachine.Gravity * deltaTime;
|
||||
}
|
||||
|
||||
movement.y = stateMachine.VelocityY;
|
||||
|
||||
stateMachine.Controller.Move(movement * deltaTime);
|
||||
|
||||
Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
|
||||
stateMachine.transform.rotation = Quaternion.Slerp(
|
||||
stateMachine.transform.rotation,
|
||||
targetRotation,
|
||||
deltaTime * 10f
|
||||
);
|
||||
}
|
||||
|
||||
stateMachine.Anim.SetFloat(crouchSpeedHash, animValue, AnimatorDampTime, deltaTime);
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Anim.SetBool(isCrouchingHash, false);
|
||||
stateMachine.Input.OnCrouchEvent -= OnCrouchToggle;
|
||||
stateMachine.Input.OnDodgeEvent -= OnDodge;
|
||||
}
|
||||
|
||||
private void OnCrouchToggle()
|
||||
{
|
||||
if (stateMachine.Input.MoveInput == Vector2.zero)
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
else
|
||||
stateMachine.SwitchState(new PlayerMoveState(stateMachine));
|
||||
}
|
||||
|
||||
private void OnDodge() => stateMachine.SwitchState(new PlayerDodgeState(stateMachine));
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82b04241d720e444a937973caed8eb42
|
||||
@@ -1,84 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerDashState : PlayerBaseState
|
||||
{
|
||||
private readonly int dashHash = Animator.StringToHash("Dash"); // Trigger parameter
|
||||
private float dashDuration = 0.25f; // How long the burst lasts (tweak as needed)
|
||||
private float dashTimer;
|
||||
private Vector3 dashDirection;
|
||||
|
||||
public PlayerDashState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
dashTimer = dashDuration;
|
||||
|
||||
// Safely fire the Dash animation trigger
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(dashHash);
|
||||
|
||||
stateMachine.Input.OnJumpEvent += OnJump;
|
||||
|
||||
// ĐÚNG: Sử dụng MoveInput đã đồng bộ mạng (Quan trọng để Client không bị phụ thuộc vào phím của Host)
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
if (input != Vector2.zero)
|
||||
{
|
||||
dashDirection = new Vector3(input.x, 0f, input.y).normalized;
|
||||
dashDirection = stateMachine.CameraRotation * dashDirection;
|
||||
dashDirection.y = 0;
|
||||
dashDirection.Normalize();
|
||||
// Instantly snap rotation to face the dash direction
|
||||
stateMachine.transform.rotation = Quaternion.LookRotation(dashDirection);
|
||||
}
|
||||
else
|
||||
{
|
||||
dashDirection = stateMachine.transform.forward;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
dashTimer -= deltaTime;
|
||||
|
||||
// Apply high speed for the dash (Burst speed)
|
||||
// Sử dụng Move tập trung để đồng bộ mạng
|
||||
stateMachine.Move(dashDirection * stateMachine.SprintSpeed, 1.0f, deltaTime);
|
||||
|
||||
// When the dash finishes, decide the next state
|
||||
if (dashTimer <= 0f)
|
||||
{
|
||||
if (stateMachine.IsSprintHeld && stateMachine.MoveInput != Vector2.zero)
|
||||
{
|
||||
// Kept holding Shift -> Go to Sprint
|
||||
stateMachine.SwitchState(new PlayerRunState(stateMachine));
|
||||
}
|
||||
else if (stateMachine.MoveInput != Vector2.zero)
|
||||
{
|
||||
// Released Shift but still moving -> Go to Walk
|
||||
stateMachine.SwitchState(new PlayerMoveState(stateMachine));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stopped moving -> Go to Idle
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnJumpEvent -= OnJump;
|
||||
}
|
||||
|
||||
private void OnJump()
|
||||
{
|
||||
if (stateMachine.IsGrounded)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerJumpState(stateMachine, stateMachine.SprintSpeed));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03721c0a319be8c4093b616e2f46afe8
|
||||
@@ -1,51 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerDodgeState : PlayerBaseState
|
||||
{
|
||||
private readonly int dodgeHash = Animator.StringToHash("Dodge");
|
||||
private float dodgeDuration = 0.5f;
|
||||
private float dodgeTimer;
|
||||
private Vector3 dodgeDirection;
|
||||
|
||||
public PlayerDodgeState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
dodgeTimer = dodgeDuration;
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(dodgeHash);
|
||||
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
if (input != Vector2.zero)
|
||||
{
|
||||
dodgeDirection = new Vector3(input.x, 0, input.y).normalized;
|
||||
dodgeDirection = stateMachine.CameraRotation * dodgeDirection;
|
||||
}
|
||||
else
|
||||
{
|
||||
dodgeDirection = -stateMachine.transform.forward;
|
||||
}
|
||||
dodgeDirection.y = 0;
|
||||
dodgeDirection.Normalize();
|
||||
|
||||
stateMachine.transform.rotation = Quaternion.LookRotation(dodgeDirection);
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
dodgeTimer -= deltaTime;
|
||||
|
||||
// Sử dụng hàm Move tập trung
|
||||
stateMachine.Move(dodgeDirection * (stateMachine.DashForce * 0.8f), 1.0f, deltaTime);
|
||||
|
||||
if (dodgeTimer <= 0)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
}
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
public override void Exit() {}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71bce3b0373df774c8b1598487bcd4ee
|
||||
@@ -1,97 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerFallState : PlayerBaseState
|
||||
{
|
||||
private readonly int fallHash = Animator.StringToHash("Fall");
|
||||
private readonly int speedHash = Animator.StringToHash("Speed");
|
||||
private readonly int speedXHash = Animator.StringToHash("Velocity X");
|
||||
private readonly int speedZHash = Animator.StringToHash("Velocity Z");
|
||||
|
||||
private float fallSpeed;
|
||||
|
||||
public PlayerFallState(PlayerStateMachine stateMachine, float fallSpeed = -1f) : base(stateMachine)
|
||||
{
|
||||
if (fallSpeed < 0)
|
||||
{
|
||||
this.fallSpeed = stateMachine.WalkSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.fallSpeed = fallSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(fallHash);
|
||||
stateMachine.Input.OnDodgeEvent += OnThrustPressed;
|
||||
stateMachine.Input.OnSprintEvent += OnAirDash;
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
stateMachine.VelocityY += stateMachine.Gravity * deltaTime;
|
||||
|
||||
// ĐÚNG: Sử dụng MoveInput đã đồng bộ mạng
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
Vector3 inputDir = new Vector3(input.x, 0, input.y).normalized;
|
||||
Vector3 moveDirection = stateMachine.CameraRotation * inputDir;
|
||||
moveDirection.y = 0;
|
||||
moveDirection.Normalize();
|
||||
|
||||
Vector3 velocity = moveDirection * fallSpeed;
|
||||
velocity.y = stateMachine.VelocityY;
|
||||
|
||||
// Sử dụng hàm Move tập trung của StateMachine để đồng bộ
|
||||
stateMachine.Move(velocity, 0.5f, deltaTime);
|
||||
stateMachine.Rotate(moveDirection, deltaTime);
|
||||
|
||||
if (stateMachine.IsGrounded)
|
||||
{
|
||||
// Landing Reset Animator
|
||||
stateMachine.AnimationHandler.SafeSetBool("IsJumping", false);
|
||||
stateMachine.AnimationHandler.SafeSetBool("IsFalling", false);
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Jump");
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Fall");
|
||||
|
||||
// Landing Shake
|
||||
if (!stateMachine.WasGrounded && stateMachine.VelocityY < -1f)
|
||||
{
|
||||
if (stateMachine.Cam != null)
|
||||
{
|
||||
stateMachine.Cam.Shake(0.2f, 0.15f);
|
||||
}
|
||||
}
|
||||
|
||||
stateMachine.VelocityY = -2f;
|
||||
|
||||
if (input == Vector2.zero)
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
else
|
||||
{
|
||||
if (stateMachine.IsSprintHeld)
|
||||
stateMachine.SwitchState(new PlayerRunState(stateMachine));
|
||||
else
|
||||
stateMachine.SwitchState(new PlayerMoveState(stateMachine));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThrustPressed() => stateMachine.SwitchState(new PlayerThrustState(stateMachine));
|
||||
|
||||
private void OnAirDash()
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerAirDashState(stateMachine));
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnDodgeEvent -= OnThrustPressed;
|
||||
stateMachine.Input.OnSprintEvent -= OnAirDash;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2b239715e32b6a4791f677e0159b862
|
||||
@@ -1,52 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerIdleState : PlayerBaseState
|
||||
{
|
||||
public PlayerIdleState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Jump");
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Fall");
|
||||
stateMachine.AnimationHandler.ForceLocomotion();
|
||||
|
||||
stateMachine.Input.OnDodgeEvent += OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent += OnCrouch;
|
||||
stateMachine.Input.OnInteractEvent += OnInteract;
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
if (stateMachine.IsGrounded && stateMachine.VelocityY < 0)
|
||||
{
|
||||
stateMachine.VelocityY = -2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
stateMachine.VelocityY += stateMachine.Gravity * deltaTime;
|
||||
}
|
||||
|
||||
stateMachine.Move(new Vector3(0, stateMachine.VelocityY, 0), 0f, deltaTime);
|
||||
|
||||
if (stateMachine.MoveInput != Vector2.zero)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerMoveState(stateMachine));
|
||||
}
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnDodgeEvent -= OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent -= OnCrouch;
|
||||
stateMachine.Input.OnInteractEvent -= OnInteract;
|
||||
}
|
||||
|
||||
private void OnDodge() => stateMachine.SwitchState(new PlayerDodgeState(stateMachine));
|
||||
private void OnCrouch() => stateMachine.SwitchState(new PlayerCrouchState(stateMachine));
|
||||
private void OnInteract() => stateMachine.SwitchState(new PlayerInteractState(stateMachine));
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd83e34f11c7aaa4ab144ee59db04e8d
|
||||
@@ -1,38 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerInteractState : PlayerBaseState
|
||||
{
|
||||
public PlayerInteractState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
// Lấy vật thể đang được chọn (Index hiện tại)
|
||||
IInteractable interactable = stateMachine.GetInteractable();
|
||||
|
||||
if (interactable != null)
|
||||
{
|
||||
Debug.Log($"[Interaction] Interacting with: {interactable.InteractionPrompt}");
|
||||
interactable.OnInteract(stateMachine);
|
||||
|
||||
// Bạn có thể phát animation tương tác ở đây
|
||||
// stateMachine.Anim.CrossFadeInFixedTime("Interact", 0.1f);
|
||||
}
|
||||
|
||||
// Chuyển về trạng thái di chuyển hoặc đứng yên ngay lập tức
|
||||
if (stateMachine.Input.MoveInput == Vector2.zero)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
}
|
||||
else
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerMoveState(stateMachine));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime) { }
|
||||
public override void PhysicsTick(float fixedDeltaTime) { }
|
||||
public override void Exit() { }
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b772d1c0c26c634fad5e71b43db9385
|
||||
@@ -1,75 +0,0 @@
|
||||
using UnityEngine;
|
||||
using Fusion;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerInteraction : NetworkBehaviour
|
||||
{
|
||||
[Header("Interaction Settings")]
|
||||
[SerializeField] public float InteractionRange = 2f;
|
||||
[SerializeField] public LayerMask InteractionMask;
|
||||
|
||||
public event Action<IInteractable> OnInteractableTargetChanged;
|
||||
|
||||
private List<IInteractable> interactablesNearby = new List<IInteractable>();
|
||||
private int currentInteractableIndex = 0;
|
||||
private EnvironmentScanner scanner;
|
||||
|
||||
public void Initialize(EnvironmentScanner scanner)
|
||||
{
|
||||
this.scanner = scanner;
|
||||
}
|
||||
|
||||
public void UpdateInteractables()
|
||||
{
|
||||
if (scanner == null) return;
|
||||
|
||||
interactablesNearby.Clear();
|
||||
IInteractable target = scanner.ScanForInteractable(InteractionRange, InteractionMask);
|
||||
|
||||
if (target != null) interactablesNearby.Add(target);
|
||||
OnInteractableTargetChanged?.Invoke(target);
|
||||
|
||||
if (Object != null && Object.HasInputAuthority)
|
||||
{
|
||||
// UI Placeholder: Interaction UI
|
||||
// Example: UI.UIEventBus.TriggerInteractionPrompt(target?.InteractionPrompt);
|
||||
// Example: UI.UIEventBus.TriggerInteractionVisibility(target != null);
|
||||
}
|
||||
|
||||
currentInteractableIndex = 0;
|
||||
}
|
||||
|
||||
public void NextInteract()
|
||||
{
|
||||
if (interactablesNearby.Count <= 1) return;
|
||||
currentInteractableIndex = (currentInteractableIndex + 1) % interactablesNearby.Count;
|
||||
NotifyTargetChanged();
|
||||
}
|
||||
|
||||
public void PreviousInteract()
|
||||
{
|
||||
if (interactablesNearby.Count <= 1) return;
|
||||
currentInteractableIndex = (currentInteractableIndex - 1 + interactablesNearby.Count) % interactablesNearby.Count;
|
||||
NotifyTargetChanged();
|
||||
}
|
||||
|
||||
private void NotifyTargetChanged()
|
||||
{
|
||||
IInteractable target = GetInteractable();
|
||||
OnInteractableTargetChanged?.Invoke(target);
|
||||
if (Object != null && Object.HasInputAuthority)
|
||||
{
|
||||
// UI Placeholder: Update Prompt
|
||||
// Example: UI.UIEventBus.TriggerInteractionPrompt(target?.InteractionPrompt);
|
||||
}
|
||||
}
|
||||
|
||||
public IInteractable GetInteractable()
|
||||
{
|
||||
return (interactablesNearby == null || interactablesNearby.Count == 0) ? null : interactablesNearby[currentInteractableIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9dcff2851697b4f4c8c25ef8381665ba
|
||||
@@ -1,75 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerJumpState : PlayerBaseState
|
||||
{
|
||||
private readonly int jumpHash = Animator.StringToHash("Jump");
|
||||
private readonly int speedHash = Animator.StringToHash("Speed");
|
||||
private readonly int speedXHash = Animator.StringToHash("Velocity X");
|
||||
private readonly int speedZHash = Animator.StringToHash("Velocity Z");
|
||||
|
||||
private float jumpSpeed;
|
||||
|
||||
public PlayerJumpState(PlayerStateMachine stateMachine, float jumpSpeed = -1f) : base(stateMachine)
|
||||
{
|
||||
if (jumpSpeed < 0)
|
||||
{
|
||||
this.jumpSpeed = stateMachine.WalkSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.jumpSpeed = jumpSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Debug.Log("<color=yellow>[PlayerJumpState]</color> Entered Jump State");
|
||||
|
||||
stateMachine.AnimationHandler.SafeResetTrigger(jumpHash);
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(jumpHash);
|
||||
stateMachine.AnimationHandler.SafeSetBool("IsJumping", true);
|
||||
|
||||
// Sử dụng hàm Jump tập trung để đồng bộ vật lý và cooldown
|
||||
stateMachine.Movement.Jump();
|
||||
|
||||
stateMachine.Input.OnSprintEvent += OnAirDash;
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
stateMachine.VelocityY += stateMachine.Gravity * deltaTime;
|
||||
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
Vector3 inputDir = new Vector3(input.x, 0, input.y).normalized;
|
||||
Vector3 moveDirection = stateMachine.CameraRotation * inputDir;
|
||||
moveDirection.y = 0;
|
||||
moveDirection.Normalize();
|
||||
|
||||
Vector3 velocity = moveDirection * jumpSpeed;
|
||||
velocity.y = stateMachine.VelocityY;
|
||||
|
||||
// Đồng bộ di chuyển và xoay
|
||||
stateMachine.Move(velocity, 1.0f, deltaTime);
|
||||
stateMachine.Rotate(moveDirection, deltaTime);
|
||||
|
||||
if (stateMachine.VelocityY <= 0f)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerFallState(stateMachine, jumpSpeed));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAirDash()
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerAirDashState(stateMachine));
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnSprintEvent -= OnAirDash;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ab5bb519df10fe45a5af5f45111ed4d
|
||||
@@ -1,71 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerMoveState : PlayerBaseState
|
||||
{
|
||||
public PlayerMoveState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Jump");
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Fall");
|
||||
stateMachine.AnimationHandler.ForceLocomotion();
|
||||
|
||||
stateMachine.Input.OnDodgeEvent += OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent += OnCrouch;
|
||||
stateMachine.Input.OnInteractEvent += OnInteract;
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
float moveAmount = Mathf.Clamp01(Mathf.Abs(input.x) + Mathf.Abs(input.y));
|
||||
|
||||
if (moveAmount <= 0.01f)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateMachine.IsSprintHeld)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerDashState(stateMachine));
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 inputDir = new Vector3(input.x, 0, input.y).normalized;
|
||||
Vector3 moveDirection = stateMachine.CameraRotation * inputDir;
|
||||
moveDirection.y = 0;
|
||||
moveDirection.Normalize();
|
||||
|
||||
Vector3 velocity = moveDirection * stateMachine.WalkSpeed;
|
||||
|
||||
if (stateMachine.IsGrounded && stateMachine.VelocityY < 0)
|
||||
{
|
||||
stateMachine.VelocityY = -2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
stateMachine.VelocityY += stateMachine.Gravity * deltaTime;
|
||||
}
|
||||
velocity.y = stateMachine.VelocityY;
|
||||
|
||||
stateMachine.Move(velocity, 0.7f, deltaTime);
|
||||
stateMachine.Rotate(moveDirection, deltaTime);
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnDodgeEvent -= OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent -= OnCrouch;
|
||||
stateMachine.Input.OnInteractEvent -= OnInteract;
|
||||
}
|
||||
|
||||
private void OnDodge() => stateMachine.SwitchState(new PlayerDodgeState(stateMachine));
|
||||
private void OnCrouch() => stateMachine.SwitchState(new PlayerCrouchState(stateMachine));
|
||||
private void OnInteract() => stateMachine.SwitchState(new PlayerInteractState(stateMachine));
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a11b0f0549f3dbd45bbdfd8114586a43
|
||||
@@ -1,128 +0,0 @@
|
||||
using UnityEngine;
|
||||
using Fusion;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerMovement : NetworkBehaviour
|
||||
{
|
||||
[field: Header("Movement Settings")]
|
||||
[field: SerializeField] public float WalkSpeed { get; private set; } = 3f;
|
||||
[field: SerializeField] public float RunSpeed { get; private set; } = 6f;
|
||||
[field: SerializeField] public float SprintSpeed { get; private set; } = 9f;
|
||||
[field: SerializeField] public float SneakSpeed { get; private set; } = 1.5f;
|
||||
[field: SerializeField] public float DashForce { get; private set; } = 10f;
|
||||
[field: SerializeField] public float RotationSpeed { get; private set; } = 500f;
|
||||
|
||||
[field: Header("Airborne Settings")]
|
||||
[field: SerializeField] public float JumpHeight { get; private set; } = 2f;
|
||||
[field: SerializeField] public float Gravity { get; private set; } = -15f;
|
||||
[field: SerializeField] public float ThrustDownwardForce { get; private set; } = -20f;
|
||||
|
||||
[field: Header("Ground Check")]
|
||||
[field: SerializeField] public float GroundCheckRadius { get; private set; } = 0.2f;
|
||||
[field: SerializeField] public Vector3 GroundCheckOffset { get; private set; }
|
||||
[field: SerializeField] public LayerMask GroundMask { get; private set; }
|
||||
// Networked shadow properties
|
||||
[Networked] private NetworkBool _isGroundedNet { get; set; }
|
||||
[Networked] private NetworkBool _wasGroundedNet { get; set; }
|
||||
[Networked] private float _velocityYNet { get; set; }
|
||||
[Networked] public Vector3 NetworkedPosition { get; set; }
|
||||
|
||||
// Local backing fields for Offline mode
|
||||
private bool _isGroundedLocal;
|
||||
private bool _wasGroundedLocal;
|
||||
private float _velocityYLocal;
|
||||
|
||||
// Public wrappers that handle both Online and Offline
|
||||
public bool IsGrounded
|
||||
{
|
||||
get => (Object != null && Object.IsValid) ? (bool)_isGroundedNet : _isGroundedLocal;
|
||||
set { if (Object != null && Object.IsValid) _isGroundedNet = value; _isGroundedLocal = value; }
|
||||
}
|
||||
|
||||
public bool WasGrounded
|
||||
{
|
||||
get => (Object != null && Object.IsValid) ? (bool)_wasGroundedNet : _wasGroundedLocal;
|
||||
set { if (Object != null && Object.IsValid) _wasGroundedNet = value; _wasGroundedLocal = value; }
|
||||
}
|
||||
|
||||
public float VelocityY
|
||||
{
|
||||
get => (Object != null && Object.IsValid) ? _velocityYNet : _velocityYLocal;
|
||||
set { if (Object != null && Object.IsValid) _velocityYNet = value; _velocityYLocal = value; }
|
||||
}
|
||||
|
||||
private CharacterController controller;
|
||||
private float jumpCooldown = 0f;
|
||||
|
||||
public void Initialize(CharacterController controller)
|
||||
{
|
||||
this.controller = controller;
|
||||
_isGroundedLocal = true; // Safe local initialization
|
||||
}
|
||||
|
||||
public override void Spawned()
|
||||
{
|
||||
// Initialize networked state once spawned (only on State Authority)
|
||||
if (Object.HasStateAuthority)
|
||||
{
|
||||
_isGroundedNet = true;
|
||||
_wasGroundedNet = true;
|
||||
_velocityYNet = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckGround(Transform playerTransform, float deltaTime)
|
||||
{
|
||||
if (jumpCooldown > 0)
|
||||
{
|
||||
jumpCooldown -= deltaTime;
|
||||
WasGrounded = IsGrounded;
|
||||
IsGrounded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
WasGrounded = IsGrounded;
|
||||
|
||||
bool sphereCheck = Physics.CheckSphere(playerTransform.TransformPoint(GroundCheckOffset), GroundCheckRadius, GroundMask);
|
||||
bool ccCheck = (controller != null) && controller.isGrounded;
|
||||
|
||||
IsGrounded = sphereCheck || ccCheck;
|
||||
|
||||
if (Object != null && Object.IsValid && Object.HasStateAuthority)
|
||||
{
|
||||
// State authority updates the networked position for reconciliation
|
||||
NetworkedPosition = playerTransform.position;
|
||||
}
|
||||
}
|
||||
|
||||
public void Jump()
|
||||
{
|
||||
// Physic formula: v = sqrt(h * -2 * g)
|
||||
VelocityY = Mathf.Sqrt(JumpHeight * -2f * Gravity);
|
||||
IsGrounded = false;
|
||||
jumpCooldown = 0.15f; // Ngăn không cho dính đất trong 0.15s đầu
|
||||
}
|
||||
|
||||
public void Move(CharacterController controller, Vector3 velocity, float deltaTime)
|
||||
{
|
||||
if (controller != null && controller.enabled)
|
||||
{
|
||||
controller.Move(velocity * deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void Rotate(Transform playerTransform, Vector3 moveDirection, float deltaTime)
|
||||
{
|
||||
if (moveDirection == Vector3.zero) return;
|
||||
Quaternion targetRot = Quaternion.LookRotation(moveDirection);
|
||||
playerTransform.rotation = Quaternion.RotateTowards(playerTransform.rotation, targetRot, RotationSpeed * deltaTime);
|
||||
}
|
||||
|
||||
public void SetGroundCheck(float radius, Vector3 offset)
|
||||
{
|
||||
GroundCheckRadius = radius;
|
||||
GroundCheckOffset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79bbcfd4d37b7834ebe0d61bb649714d
|
||||
@@ -1,55 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerParkourState : PlayerBaseState
|
||||
{
|
||||
private readonly int parkourHash = Animator.StringToHash("Step Up");
|
||||
private float animationDuration;
|
||||
private float timer;
|
||||
|
||||
public PlayerParkourState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Debug.Log("<color=orange>[PlayerParkourState]</color> Entered Parkour State");
|
||||
if (stateMachine.Anim != null && stateMachine.Anim.layerCount > 0)
|
||||
{
|
||||
// Play the Parkour animation (Step Up)
|
||||
// Use the overload with layer index 0 to be explicit
|
||||
stateMachine.Anim.CrossFadeInFixedTime(parkourHash, 0.1f, 0);
|
||||
}
|
||||
|
||||
timer = 0f;
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
timer += deltaTime;
|
||||
|
||||
// Simple way to wait for animation: check normalized time of the current state
|
||||
var stateInfo = stateMachine.Anim.GetCurrentAnimatorStateInfo(0);
|
||||
if (stateInfo.shortNameHash == parkourHash && stateInfo.normalizedTime >= 1f)
|
||||
{
|
||||
if (stateMachine.Input.MoveInput == Vector2.zero)
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
else
|
||||
stateMachine.SwitchState(new PlayerMoveState(stateMachine));
|
||||
}
|
||||
|
||||
// Safety timeout if animation doesn't play or something
|
||||
if (timer > 2f)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
}
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime)
|
||||
{
|
||||
// Usually during parkour we disable gravity or handle it specially
|
||||
stateMachine.VelocityY = 0;
|
||||
}
|
||||
|
||||
public override void Exit() {}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9b73ee252f157e49a08f8f7566dc6ac
|
||||
@@ -1,68 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerRunState : PlayerBaseState
|
||||
{
|
||||
public PlayerRunState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Jump");
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Fall");
|
||||
stateMachine.AnimationHandler.ForceLocomotion();
|
||||
|
||||
stateMachine.Input.OnDodgeEvent += OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent += OnCrouch;
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
float moveAmount = Mathf.Clamp01(Mathf.Abs(input.x) + Mathf.Abs(input.y));
|
||||
|
||||
if (moveAmount <= 0.01f)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stateMachine.IsSprintHeld)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerMoveState(stateMachine));
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 inputDir = new Vector3(input.x, 0, input.y).normalized;
|
||||
Vector3 moveDirection = stateMachine.CameraRotation * inputDir;
|
||||
moveDirection.y = 0;
|
||||
moveDirection.Normalize();
|
||||
|
||||
Vector3 velocity = moveDirection * stateMachine.SprintSpeed;
|
||||
|
||||
if (stateMachine.IsGrounded && stateMachine.VelocityY < 0)
|
||||
{
|
||||
stateMachine.VelocityY = -2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
stateMachine.VelocityY += stateMachine.Gravity * deltaTime;
|
||||
}
|
||||
velocity.y = stateMachine.VelocityY;
|
||||
|
||||
stateMachine.Move(velocity, 1.0f, deltaTime);
|
||||
stateMachine.Rotate(moveDirection, deltaTime);
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnDodgeEvent -= OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent -= OnCrouch;
|
||||
}
|
||||
|
||||
private void OnDodge() => stateMachine.SwitchState(new PlayerDodgeState(stateMachine));
|
||||
private void OnCrouch() => stateMachine.SwitchState(new PlayerCrouchState(stateMachine));
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef096feea261a8d449384a68f71470b3
|
||||
@@ -1,271 +0,0 @@
|
||||
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 != null) ? Movement.VelocityY : 0f;
|
||||
set { if (Movement != null) Movement.VelocityY = value; }
|
||||
}
|
||||
|
||||
public bool IsGrounded => (Movement != null) ? Movement.IsGrounded : true;
|
||||
public bool WasGrounded => (Movement != null) ? Movement.WasGrounded : true;
|
||||
|
||||
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 && Object.IsValid) 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;
|
||||
}
|
||||
|
||||
if (Input != null)
|
||||
{
|
||||
Input.OnNextInteractEvent -= Interaction.NextInteract;
|
||||
Input.OnNextInteractEvent += Interaction.NextInteract;
|
||||
Input.OnPreviousInteractEvent -= Interaction.PreviousInteract;
|
||||
Input.OnPreviousInteractEvent += Interaction.PreviousInteract;
|
||||
}
|
||||
|
||||
if (Controller != null) Controller.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Input != null && Interaction != null)
|
||||
{
|
||||
Input.OnNextInteractEvent -= Interaction.NextInteract;
|
||||
Input.OnPreviousInteractEvent -= Interaction.PreviousInteract;
|
||||
}
|
||||
}
|
||||
|
||||
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 != null && Object.IsValid && (Object.HasInputAuthority || Runner.IsServer));
|
||||
if (!canMove) return;
|
||||
|
||||
Movement.Move(Controller, velocity, deltaTime);
|
||||
|
||||
localAnimatorSpeed = animatorSpeed;
|
||||
if (Object != null && Object.IsValid && Object.HasStateAuthority)
|
||||
{
|
||||
NetworkedSpeed = animatorSpeed;
|
||||
NetworkedMoveInput = MoveInput;
|
||||
}
|
||||
UpdateAnimator(deltaTime);
|
||||
}
|
||||
|
||||
private void UpdateAnimator(float deltaTime)
|
||||
{
|
||||
bool isNetworked = Runner != null && Runner.IsRunning && Object != null && Object.IsValid;
|
||||
float speedValue = (!isNetworked || Object.HasInputAuthority) ? localAnimatorSpeed : NetworkedSpeed;
|
||||
Vector2 inputVector = (!isNetworked || Object.HasInputAuthority) ? MoveInput : NetworkedMoveInput;
|
||||
|
||||
// Pass IsGrounded to handle air/ground transitions
|
||||
AnimationHandler.UpdateAnimator(speedValue, inputVector, IsGrounded, deltaTime);
|
||||
}
|
||||
|
||||
public override void FixedUpdateNetwork()
|
||||
{
|
||||
bool isRunning = Runner != null && Runner.IsRunning;
|
||||
if (isRunning && (Object == null || !Object.IsValid)) return;
|
||||
|
||||
float deltaTime = isRunning ? Runner.DeltaTime : Time.fixedDeltaTime;
|
||||
|
||||
if (GetInput(out PlayerInputData data))
|
||||
{
|
||||
MoveInput = data.Direction;
|
||||
IsSprintHeld = (bool)data.sprint;
|
||||
if (isRunning) NetworkedCameraRotation = data.rot;
|
||||
|
||||
if (data.jump) TriggerJump();
|
||||
}
|
||||
else if (!isRunning)
|
||||
{
|
||||
MoveInput = new Vector2(UnityEngine.Input.GetAxisRaw("Horizontal"), UnityEngine.Input.GetAxisRaw("Vertical"));
|
||||
IsSprintHeld = UnityEngine.Input.GetKey(KeyCode.LeftShift);
|
||||
if (Input.ConsumeJumpInput()) TriggerJump();
|
||||
}
|
||||
|
||||
if (!isRunning || (Object != null && Object.IsValid && (Object.HasInputAuthority || Runner.IsServer)))
|
||||
{
|
||||
if (hasControl)
|
||||
{
|
||||
Movement.CheckGround(transform, deltaTime);
|
||||
Interaction.UpdateInteractables();
|
||||
currentState?.Tick(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TriggerJump()
|
||||
{
|
||||
if (!IsGrounded) return;
|
||||
|
||||
if (Scanner != null)
|
||||
{
|
||||
var hitData = Scanner.ObstacleCheck();
|
||||
if (hitData.forwardHitFound && hitData.heightHitFound)
|
||||
{
|
||||
SwitchState(new PlayerParkourState(this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float jumpMoveSpeed = (IsSprintHeld) ? Movement.SprintSpeed : Movement.WalkSpeed;
|
||||
SwitchState(new PlayerJumpState(this, jumpMoveSpeed));
|
||||
}
|
||||
|
||||
public override void Render()
|
||||
{
|
||||
bool isRunning = Runner != null && Runner.IsRunning;
|
||||
if (isRunning && Object != null && Object.IsValid)
|
||||
{
|
||||
if (!Object.HasInputAuthority)
|
||||
{
|
||||
// Proxies
|
||||
if (Movement.NetworkedPosition != Vector3.zero)
|
||||
{
|
||||
transform.position = Vector3.Lerp(transform.position, Movement.NetworkedPosition, Runner.DeltaTime * 20f);
|
||||
}
|
||||
}
|
||||
UpdateAnimator(Runner.DeltaTime);
|
||||
}
|
||||
else if (!isRunning)
|
||||
{
|
||||
UpdateAnimator(Time.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 848ad6fdeb60b254497391392419b063
|
||||
@@ -1,71 +0,0 @@
|
||||
using UnityEngine;
|
||||
using Fusion;
|
||||
using System;
|
||||
using Hallucinate.Network;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerStats : NetworkBehaviour
|
||||
{
|
||||
[Header("Player Stats")]
|
||||
[Networked, OnChangedRender(nameof(OnHealthChangedRender))] public float Health { get; set; } = 100f;
|
||||
[Networked, OnChangedRender(nameof(OnStaminaChangedRender))] public float Stamina { get; set; } = 100f;
|
||||
[Networked, OnChangedRender(nameof(OnNoiseLevelChangedRender))] public float NoiseLevel { get; set; } = 0f;
|
||||
|
||||
public event Action<float> OnHealthChanged;
|
||||
public event Action<float> OnStaminaChanged;
|
||||
public event Action<float> OnNoiseLevelChanged;
|
||||
|
||||
public override void Spawned()
|
||||
{
|
||||
// Initial UI sync placeholder
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
void OnHealthChangedRender()
|
||||
{
|
||||
OnHealthChanged?.Invoke(Health);
|
||||
|
||||
if (Health <= 0 && Object.HasStateAuthority)
|
||||
{
|
||||
// Find the other player as winner
|
||||
foreach (var player in Runner.ActivePlayers)
|
||||
{
|
||||
if (player != Object.InputAuthority)
|
||||
{
|
||||
MatchResultManager.Instance?.ProcessMatchEnd(player);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.HasInputAuthority)
|
||||
{
|
||||
// UI Placeholder: Trigger Health UI Change
|
||||
}
|
||||
}
|
||||
|
||||
void OnStaminaChangedRender()
|
||||
{
|
||||
OnStaminaChanged?.Invoke(Stamina);
|
||||
if (Object.HasInputAuthority)
|
||||
{
|
||||
// UI Placeholder: Trigger Stamina UI Change
|
||||
}
|
||||
}
|
||||
|
||||
void OnNoiseLevelChangedRender()
|
||||
{
|
||||
OnNoiseLevelChanged?.Invoke(NoiseLevel);
|
||||
}
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
if (Object.HasInputAuthority)
|
||||
{
|
||||
OnHealthChangedRender();
|
||||
OnStaminaChangedRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1494b79b59fcdf4d9f7956dde31bc42
|
||||
@@ -1,43 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerThrustState : PlayerBaseState
|
||||
{
|
||||
private readonly int thrustHash = Animator.StringToHash("Thrust");
|
||||
|
||||
public PlayerThrustState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(thrustHash);
|
||||
|
||||
// Immediately set a massive downward velocity
|
||||
stateMachine.VelocityY = stateMachine.ThrustDownwardForce;
|
||||
}
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
// Keep applying heavy gravity just in case
|
||||
stateMachine.VelocityY += (stateMachine.Gravity * 2f) * deltaTime;
|
||||
|
||||
// Move the player straight down (no horizontal movement allowed during thrust)
|
||||
Vector3 fallMovement = new Vector3(0f, stateMachine.VelocityY, 0f);
|
||||
stateMachine.Controller.Move(fallMovement * deltaTime);
|
||||
|
||||
// When we smash into the ground...
|
||||
if (stateMachine.Controller.isGrounded)
|
||||
{
|
||||
stateMachine.VelocityY = -2f;
|
||||
|
||||
// TODO: Add impact effects, screen shake, or damage area here!
|
||||
|
||||
// Return to idle after landing
|
||||
stateMachine.SwitchState(new PlayerIdleState(stateMachine));
|
||||
}
|
||||
}
|
||||
|
||||
public override void PhysicsTick(float fixedDeltaTime) {}
|
||||
public override void Exit() {}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e76cfd208cc6f44b8b954147ee1defb
|
||||
Reference in New Issue
Block a user