update di chuyển debug jump
This commit is contained in:
@@ -127,7 +127,19 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public void OnJump(InputAction.CallbackContext context)
|
||||
{
|
||||
if (context.performed) OnJumpEvent?.Invoke();
|
||||
if (context.performed)
|
||||
{
|
||||
wasJumpPressed = true;
|
||||
OnJumpEvent?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private bool wasJumpPressed;
|
||||
public bool ConsumeJumpInput()
|
||||
{
|
||||
bool val = wasJumpPressed;
|
||||
wasJumpPressed = false;
|
||||
return val;
|
||||
}
|
||||
|
||||
public void OnDodgeOrThrust(InputAction.CallbackContext context)
|
||||
|
||||
@@ -9,46 +9,153 @@ namespace OnlyScove.Scripts
|
||||
[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 bool hasSpeedParam;
|
||||
private bool hasVelocityXParam;
|
||||
private bool hasVelocityZParam;
|
||||
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)
|
||||
{
|
||||
if (param.name == speedParamName) hasSpeedParam = true;
|
||||
if (param.name == velocityXParamName) hasVelocityXParam = true;
|
||||
if (param.name == velocityZParamName) hasVelocityZParam = true;
|
||||
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);
|
||||
}
|
||||
|
||||
public void UpdateAnimator(float speed, Vector2 moveInput, float deltaTime)
|
||||
private bool wasGroundedInAnimator;
|
||||
|
||||
public void UpdateAnimator(float speed, Vector2 moveInput, bool isGrounded, float deltaTime)
|
||||
{
|
||||
if (anim == null) return;
|
||||
|
||||
if (hasSpeedParam) anim.SetFloat(speedHash, speed, animationDamping, deltaTime);
|
||||
if (hasVelocityXParam) anim.SetFloat(velocityXHash, moveInput.x * speed, animationDamping, deltaTime);
|
||||
if (hasVelocityZParam) anim.SetFloat(velocityZHash, moveInput.y * speed, animationDamping, deltaTime);
|
||||
// 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 && hasSpeedParam) anim.SetFloat(speedHash, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace OnlyScove.Scripts
|
||||
{
|
||||
dashTimer = dashDuration;
|
||||
|
||||
// Fire the Dash animation trigger
|
||||
stateMachine.Anim.SetTrigger(dashHash);
|
||||
// Safely fire the Dash animation trigger
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(dashHash);
|
||||
|
||||
stateMachine.Input.OnJumpEvent += OnJump;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace OnlyScove.Scripts
|
||||
public override void Enter()
|
||||
{
|
||||
dodgeTimer = dodgeDuration;
|
||||
stateMachine.Anim.SetTrigger(dodgeHash);
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(dodgeHash);
|
||||
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
if (input != Vector2.zero)
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.Anim.SetTrigger(fallHash);
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(fallHash);
|
||||
stateMachine.Input.OnDodgeEvent += OnThrustPressed;
|
||||
stateMachine.Input.OnSprintEvent += OnAirDash;
|
||||
}
|
||||
@@ -50,6 +50,12 @@ namespace OnlyScove.Scripts
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -4,27 +4,21 @@ namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerIdleState : PlayerBaseState
|
||||
{
|
||||
private readonly int speedHash = Animator.StringToHash("Speed");
|
||||
|
||||
public PlayerIdleState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
// Tạm thời bỏ ResetTrigger nếu chúng không tồn tại trong Animator của bạn
|
||||
// stateMachine.Anim.ResetTrigger("Jump");
|
||||
// stateMachine.Anim.ResetTrigger("Fall");
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Jump");
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Fall");
|
||||
stateMachine.AnimationHandler.ForceLocomotion();
|
||||
|
||||
stateMachine.Input.OnJumpEvent += OnJump;
|
||||
stateMachine.Input.OnDodgeEvent += OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent += OnCrouch;
|
||||
stateMachine.Input.OnInteractEvent += OnInteract;
|
||||
}
|
||||
private readonly int speedXHash = Animator.StringToHash("Velocity X");
|
||||
private readonly int speedZHash = Animator.StringToHash("Velocity Z");
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
// Cập nhật trọng lực
|
||||
if (stateMachine.IsGrounded && stateMachine.VelocityY < 0)
|
||||
{
|
||||
stateMachine.VelocityY = -2f;
|
||||
@@ -34,14 +28,11 @@ namespace OnlyScove.Scripts
|
||||
stateMachine.VelocityY += stateMachine.Gravity * deltaTime;
|
||||
}
|
||||
|
||||
// Sử dụng hàm Move tập trung để đồng bộ Animator và Vị trí
|
||||
stateMachine.Move(new Vector3(0, stateMachine.VelocityY, 0), 0f, deltaTime);
|
||||
|
||||
// QUAN TRỌNG: Đọc dữ liệu đã đồng bộ từ mạng (stateMachine.MoveInput)
|
||||
if (stateMachine.MoveInput != Vector2.zero)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerMoveState(stateMachine));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,28 +40,11 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnJumpEvent -= OnJump;
|
||||
stateMachine.Input.OnDodgeEvent -= OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent -= OnCrouch;
|
||||
stateMachine.Input.OnInteractEvent -= OnInteract;
|
||||
}
|
||||
|
||||
private void OnJump()
|
||||
{
|
||||
if (stateMachine.IsGrounded)
|
||||
{
|
||||
if (stateMachine.Scanner != null)
|
||||
{
|
||||
var hitData = stateMachine.Scanner.ObstacleCheck();
|
||||
if (hitData.forwardHitFound)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerParkourState(stateMachine));
|
||||
return;
|
||||
}
|
||||
}
|
||||
stateMachine.SwitchState(new PlayerJumpState(stateMachine, stateMachine.WalkSpeed));
|
||||
}
|
||||
}
|
||||
private void OnDodge() => stateMachine.SwitchState(new PlayerDodgeState(stateMachine));
|
||||
private void OnCrouch() => stateMachine.SwitchState(new PlayerCrouchState(stateMachine));
|
||||
private void OnInteract() => stateMachine.SwitchState(new PlayerInteractState(stateMachine));
|
||||
|
||||
@@ -25,14 +25,14 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
// Sử dụng dữ liệu đồng bộ
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
Debug.Log("<color=yellow>[PlayerJumpState]</color> Entered Jump State");
|
||||
|
||||
stateMachine.Anim.ResetTrigger(jumpHash);
|
||||
stateMachine.Anim.SetTrigger(jumpHash);
|
||||
stateMachine.AnimationHandler.SafeResetTrigger(jumpHash);
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(jumpHash);
|
||||
stateMachine.AnimationHandler.SafeSetBool("IsJumping", true);
|
||||
|
||||
// Physic formula: v = sqrt(h * -2 * g)
|
||||
stateMachine.VelocityY = Mathf.Sqrt(stateMachine.JumpHeight * -2f * stateMachine.Gravity);
|
||||
// Sử dụng hàm Jump tập trung để đồng bộ vật lý và cooldown
|
||||
stateMachine.Movement.Jump();
|
||||
|
||||
stateMachine.Input.OnSprintEvent += OnAirDash;
|
||||
}
|
||||
|
||||
@@ -4,23 +4,21 @@ namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerMoveState : PlayerBaseState
|
||||
{
|
||||
private readonly int speedHash = Animator.StringToHash("Speed");
|
||||
|
||||
public PlayerMoveState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.Input.OnJumpEvent += OnJump;
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Jump");
|
||||
stateMachine.AnimationHandler.SafeResetTrigger("Fall");
|
||||
stateMachine.AnimationHandler.ForceLocomotion();
|
||||
|
||||
stateMachine.Input.OnDodgeEvent += OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent += OnCrouch;
|
||||
stateMachine.Input.OnInteractEvent += OnInteract;
|
||||
}
|
||||
private readonly int speedXHash = Animator.StringToHash("Velocity X");
|
||||
private readonly int speedZHash = Animator.StringToHash("Velocity Z");
|
||||
|
||||
public override void Tick(float deltaTime)
|
||||
{
|
||||
// QUAN TRỌNG: Đọc trực tiếp từ stateMachine (Dữ liệu đã đồng bộ mạng)
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
float moveAmount = Mathf.Clamp01(Mathf.Abs(input.x) + Mathf.Abs(input.y));
|
||||
|
||||
@@ -53,7 +51,6 @@ namespace OnlyScove.Scripts
|
||||
}
|
||||
velocity.y = stateMachine.VelocityY;
|
||||
|
||||
// Sử dụng hàm Move tập trung (0.7f là giá trị Speed cho Animator khi đi bộ)
|
||||
stateMachine.Move(velocity, 0.7f, deltaTime);
|
||||
stateMachine.Rotate(moveDirection, deltaTime);
|
||||
}
|
||||
@@ -62,29 +59,11 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnJumpEvent -= OnJump;
|
||||
stateMachine.Input.OnDodgeEvent -= OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent -= OnCrouch;
|
||||
stateMachine.Input.OnInteractEvent -= OnInteract;
|
||||
}
|
||||
|
||||
private void OnJump()
|
||||
{
|
||||
if (stateMachine.IsGrounded)
|
||||
{
|
||||
if (stateMachine.Scanner != null)
|
||||
{
|
||||
var hitData = stateMachine.Scanner.ObstacleCheck();
|
||||
if (hitData.forwardHitFound)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerParkourState(stateMachine));
|
||||
return;
|
||||
}
|
||||
}
|
||||
stateMachine.SwitchState(new PlayerJumpState(stateMachine, stateMachine.WalkSpeed));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDodge() => stateMachine.SwitchState(new PlayerDodgeState(stateMachine));
|
||||
private void OnCrouch() => stateMachine.SwitchState(new PlayerCrouchState(stateMachine));
|
||||
private void OnInteract() => stateMachine.SwitchState(new PlayerInteractState(stateMachine));
|
||||
|
||||
@@ -22,25 +22,86 @@ namespace OnlyScove.Scripts
|
||||
[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; }
|
||||
|
||||
[Networked] public bool IsGrounded { get; set; }
|
||||
[Networked] public bool WasGrounded { get; set; }
|
||||
[Networked] public float VelocityY { get; set; }
|
||||
[Networked] public Vector3 NetworkedPosition { get; set; }
|
||||
// Local backing fields for Offline mode
|
||||
private bool _isGroundedLocal;
|
||||
private bool _wasGroundedLocal;
|
||||
private float _velocityYLocal;
|
||||
|
||||
private CharacterController controller;
|
||||
// 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 void Initialize(CharacterController controller)
|
||||
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)
|
||||
{
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public void CheckGround(Transform playerTransform)
|
||||
{
|
||||
if (Object == null || (!Object.HasStateAuthority && !Object.HasInputAuthority)) return;
|
||||
if (jumpCooldown > 0)
|
||||
{
|
||||
jumpCooldown -= deltaTime;
|
||||
WasGrounded = IsGrounded;
|
||||
IsGrounded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
WasGrounded = IsGrounded;
|
||||
IsGrounded = Physics.CheckSphere(playerTransform.TransformPoint(GroundCheckOffset), GroundCheckRadius, GroundMask);
|
||||
|
||||
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)
|
||||
@@ -48,10 +109,6 @@ namespace OnlyScove.Scripts
|
||||
if (controller != null && controller.enabled)
|
||||
{
|
||||
controller.Move(velocity * deltaTime);
|
||||
if (Object != null && Object.HasStateAuthority)
|
||||
{
|
||||
NetworkedPosition = transform.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace OnlyScove.Scripts
|
||||
|
||||
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)
|
||||
|
||||
@@ -4,22 +4,20 @@ namespace OnlyScove.Scripts
|
||||
{
|
||||
public class PlayerRunState : PlayerBaseState
|
||||
{
|
||||
private readonly int speedHash = Animator.StringToHash("Speed");
|
||||
private readonly int speedXHash = Animator.StringToHash("Velocity X");
|
||||
private readonly int speedZHash = Animator.StringToHash("Velocity Z");
|
||||
|
||||
public PlayerRunState(PlayerStateMachine stateMachine) : base(stateMachine) {}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.Input.OnJumpEvent += OnJump;
|
||||
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)
|
||||
{
|
||||
// ĐÚNG: Sử dụng dữ liệu đã đồng bộ qua mạng
|
||||
Vector2 input = stateMachine.MoveInput;
|
||||
float moveAmount = Mathf.Clamp01(Mathf.Abs(input.x) + Mathf.Abs(input.y));
|
||||
|
||||
@@ -52,7 +50,6 @@ namespace OnlyScove.Scripts
|
||||
}
|
||||
velocity.y = stateMachine.VelocityY;
|
||||
|
||||
// Sử dụng hàm Move tập trung (1.0f là giá trị Speed cho Animator khi chạy)
|
||||
stateMachine.Move(velocity, 1.0f, deltaTime);
|
||||
stateMachine.Rotate(moveDirection, deltaTime);
|
||||
}
|
||||
@@ -61,28 +58,10 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
stateMachine.Input.OnJumpEvent -= OnJump;
|
||||
stateMachine.Input.OnDodgeEvent -= OnDodge;
|
||||
stateMachine.Input.OnCrouchEvent -= OnCrouch;
|
||||
}
|
||||
|
||||
private void OnJump()
|
||||
{
|
||||
if (stateMachine.IsGrounded)
|
||||
{
|
||||
if (stateMachine.Scanner != null)
|
||||
{
|
||||
var hitData = stateMachine.Scanner.ObstacleCheck();
|
||||
if (hitData.forwardHitFound)
|
||||
{
|
||||
stateMachine.SwitchState(new PlayerParkourState(stateMachine));
|
||||
return;
|
||||
}
|
||||
}
|
||||
stateMachine.SwitchState(new PlayerJumpState(stateMachine, stateMachine.SprintSpeed));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDodge() => stateMachine.SwitchState(new PlayerDodgeState(stateMachine));
|
||||
private void OnCrouch() => stateMachine.SwitchState(new PlayerCrouchState(stateMachine));
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public float VelocityY
|
||||
{
|
||||
get => (Object != null && Object.IsValid && Movement != null) ? Movement.VelocityY : 0f;
|
||||
set { if (Object != null && Object.IsValid && Movement != null) Movement.VelocityY = value; }
|
||||
get => (Movement != null) ? Movement.VelocityY : 0f;
|
||||
set { if (Movement != null) Movement.VelocityY = value; }
|
||||
}
|
||||
|
||||
public bool IsGrounded => (Object != null && Object.IsValid && Movement != null) ? Movement.IsGrounded : true;
|
||||
public bool WasGrounded => (Object != null && Object.IsValid && Movement != null) ? Movement.WasGrounded : true;
|
||||
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;
|
||||
@@ -106,6 +106,7 @@ namespace OnlyScove.Scripts
|
||||
if (isOffline || (Object != null && Object.HasInputAuthority))
|
||||
{
|
||||
Local = this;
|
||||
|
||||
CameraController cameraController = GameObject.FindAnyObjectByType<CameraController>();
|
||||
if (cameraController != null)
|
||||
{
|
||||
@@ -161,7 +162,9 @@ namespace OnlyScove.Scripts
|
||||
bool isNetworked = Runner != null && Runner.IsRunning && Object != null && Object.IsValid;
|
||||
float speedValue = (!isNetworked || Object.HasInputAuthority) ? localAnimatorSpeed : NetworkedSpeed;
|
||||
Vector2 inputVector = (!isNetworked || Object.HasInputAuthority) ? MoveInput : NetworkedMoveInput;
|
||||
AnimationHandler.UpdateAnimator(speedValue, inputVector, deltaTime);
|
||||
|
||||
// Pass IsGrounded to handle air/ground transitions
|
||||
AnimationHandler.UpdateAnimator(speedValue, inputVector, IsGrounded, deltaTime);
|
||||
}
|
||||
|
||||
public override void FixedUpdateNetwork()
|
||||
@@ -169,38 +172,64 @@ namespace OnlyScove.Scripts
|
||||
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);
|
||||
Movement.CheckGround(transform, deltaTime);
|
||||
Interaction.UpdateInteractables();
|
||||
currentState?.Tick(isRunning ? Runner.DeltaTime : Time.fixedDeltaTime);
|
||||
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 && !Object.HasInputAuthority)
|
||||
if (isRunning && Object != null && Object.IsValid)
|
||||
{
|
||||
// Smooth interpolation for proxies
|
||||
if (Movement.NetworkedPosition != Vector3.zero)
|
||||
if (!Object.HasInputAuthority)
|
||||
{
|
||||
transform.position = Vector3.Lerp(transform.position, Movement.NetworkedPosition, Runner.DeltaTime * 15f);
|
||||
// Proxies
|
||||
if (Movement.NetworkedPosition != Vector3.zero)
|
||||
{
|
||||
transform.position = Vector3.Lerp(transform.position, Movement.NetworkedPosition, Runner.DeltaTime * 20f);
|
||||
}
|
||||
}
|
||||
UpdateAnimator(Runner.DeltaTime);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
stateMachine.Anim.SetTrigger(thrustHash);
|
||||
stateMachine.AnimationHandler.SafeSetTrigger(thrustHash);
|
||||
|
||||
// Immediately set a massive downward velocity
|
||||
stateMachine.VelocityY = stateMachine.ThrustDownwardForce;
|
||||
|
||||
Reference in New Issue
Block a user