remove obstacle

This commit is contained in:
2026-06-04 11:50:09 +07:00
parent 162deaf34f
commit 5526341041
50 changed files with 399 additions and 2097 deletions

View File

@@ -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; } }

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 396f655b903645b69c30bb8c579f08cb
timeCreated: 1780541958

View File

@@ -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;
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 985468dedaf32f44191b2cc29c813c8c

View File

@@ -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() {}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 43c3e5e400fea604fb432f84d5dd9ce1

View File

@@ -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;
}
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 965ec86f3b9695640bdc85e624d0e9e7

View File

@@ -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();
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: af622221f1750284885366b02b8bfbba

View File

@@ -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));
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 82b04241d720e444a937973caed8eb42

View File

@@ -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));
}
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 03721c0a319be8c4093b616e2f46afe8

View File

@@ -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() {}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 71bce3b0373df774c8b1598487bcd4ee

View File

@@ -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;
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: e2b239715e32b6a4791f677e0159b862

View File

@@ -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));
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: fd83e34f11c7aaa4ab144ee59db04e8d

View File

@@ -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() { }
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 0b772d1c0c26c634fad5e71b43db9385

View File

@@ -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];
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 9dcff2851697b4f4c8c25ef8381665ba

View File

@@ -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;
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 7ab5bb519df10fe45a5af5f45111ed4d

View File

@@ -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));
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: a11b0f0549f3dbd45bbdfd8114586a43

View File

@@ -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;
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 79bbcfd4d37b7834ebe0d61bb649714d

View File

@@ -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() {}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: d9b73ee252f157e49a08f8f7566dc6ac

View File

@@ -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));
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: ef096feea261a8d449384a68f71470b3

View File

@@ -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);
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 848ad6fdeb60b254497391392419b063

View File

@@ -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();
}
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: a1494b79b59fcdf4d9f7956dde31bc42

View File

@@ -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() {}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 4e76cfd208cc6f44b8b954147ee1defb