remove obstacle
This commit is contained in:
@@ -6,7 +6,7 @@ namespace OnlyScove.Scripts
|
||||
public class PlayerDebugProvider : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private PlayerStateMachine stateMachine;
|
||||
// [SerializeField] private PlayerStateMachine stateMachine;
|
||||
[Tooltip("Kéo cái Canvas (World Space) bạn đã thiết kế vào đây")]
|
||||
[SerializeField] private GameObject debugCanvas;
|
||||
|
||||
@@ -16,36 +16,36 @@ namespace OnlyScove.Scripts
|
||||
[SerializeField] private bool lookAtCamera = true;
|
||||
|
||||
// Các thuộc tính Public để bạn truy cập từ Script UI của bạn
|
||||
public string CurrentState => stateMachine != null ? stateMachine.CurrentStateName : "N/A";
|
||||
public string GroundedStatus => (stateMachine != null && stateMachine.IsGrounded) ? "YES" : "NO";
|
||||
public float HorizontalSpeed => stateMachine != null ? new Vector3(stateMachine.Controller.velocity.x, 0, stateMachine.Controller.velocity.z).magnitude : 0f;
|
||||
public float VerticalSpeed => stateMachine != null ? stateMachine.VelocityY : 0f;
|
||||
// public string CurrentState => stateMachine != null ? stateMachine.CurrentStateName : "N/A";
|
||||
// public string GroundedStatus => (stateMachine != null && stateMachine.IsGrounded) ? "YES" : "NO";
|
||||
// public float HorizontalSpeed => stateMachine != null ? new Vector3(stateMachine.Controller.velocity.x, 0, stateMachine.Controller.velocity.z).magnitude : 0f;
|
||||
// public float VerticalSpeed => stateMachine != null ? stateMachine.VelocityY : 0f;
|
||||
//
|
||||
// // Sửa lỗi truy cập InputReader từ StateMachine
|
||||
// public Vector2 MoveInput => (stateMachine != null && stateMachine.Input != null) ? stateMachine.Input.MoveInput : Vector2.zero;
|
||||
// public bool IsSprinting => (stateMachine != null && stateMachine.Input != null) ? stateMachine.Input.IsSprintHeld : false;
|
||||
//
|
||||
// public string TargetInteractable => stateMachine != null ? (stateMachine.GetInteractable()?.InteractionPrompt ?? "None") : "N/A";
|
||||
//
|
||||
// public IInteractable GetActiveInteractable() => stateMachine?.GetInteractable();
|
||||
|
||||
// Sửa lỗi truy cập InputReader từ StateMachine
|
||||
public Vector2 MoveInput => (stateMachine != null && stateMachine.Input != null) ? stateMachine.Input.MoveInput : Vector2.zero;
|
||||
public bool IsSprinting => (stateMachine != null && stateMachine.Input != null) ? stateMachine.Input.IsSprintHeld : false;
|
||||
|
||||
public string TargetInteractable => stateMachine != null ? (stateMachine.GetInteractable()?.InteractionPrompt ?? "None") : "N/A";
|
||||
|
||||
public IInteractable GetActiveInteractable() => stateMachine?.GetInteractable();
|
||||
|
||||
public Vector3 GetInteractionPoint()
|
||||
{
|
||||
if (stateMachine == null || stateMachine.Scanner == null) return Vector3.zero;
|
||||
return stateMachine.Scanner.GetLastInteractionPoint(stateMachine.InteractionRange, stateMachine.InteractionMask);
|
||||
}
|
||||
// public Vector3 GetInteractionPoint()
|
||||
// {
|
||||
// if (stateMachine == null || stateMachine.Scanner == null) return Vector3.zero;
|
||||
// return stateMachine.Scanner.GetLastInteractionPoint(stateMachine.InteractionRange, stateMachine.InteractionMask);
|
||||
// }
|
||||
|
||||
private Vector3 currentVelocity;
|
||||
private Transform cameraTransform;
|
||||
private bool isVisible = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (stateMachine == null) stateMachine = GetComponent<PlayerStateMachine>();
|
||||
cameraTransform = Camera.main?.transform;
|
||||
|
||||
if (debugCanvas != null) debugCanvas.SetActive(isVisible);
|
||||
}
|
||||
// private void Awake()
|
||||
// {
|
||||
// if (stateMachine == null) stateMachine = GetComponent<PlayerStateMachine>();
|
||||
// cameraTransform = Camera.main?.transform;
|
||||
//
|
||||
// if (debugCanvas != null) debugCanvas.SetActive(isVisible);
|
||||
// }
|
||||
|
||||
private void Update()
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace OnlyScove.Scripts.GameSetup
|
||||
public void ApplyAutoSetup()
|
||||
{
|
||||
CharacterController controller = GetComponent<CharacterController>();
|
||||
PlayerStateMachine stateMachine = GetComponent<PlayerStateMachine>();
|
||||
// PlayerStateMachine stateMachine = GetComponent<PlayerStateMachine>();
|
||||
Animator animator = GetComponentInChildren<Animator>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -97,19 +97,19 @@ namespace OnlyScove.Scripts.GameSetup
|
||||
|
||||
// 6. GROUND CHECK (STATE MACHINE)
|
||||
sb.AppendLine("<color=#757575>------------------------------------------------------------</color>");
|
||||
if (stateMachine != null)
|
||||
{
|
||||
float groundCheckRadius = finalRadius * 0.6f;
|
||||
float groundCheckOffsetY = finalHeight / 24f;
|
||||
Vector3 groundCheckOffset = new Vector3(0, groundCheckOffsetY, zCenterOffset);
|
||||
|
||||
stateMachine.SetGroundCheck(groundCheckRadius, groundCheckOffset);
|
||||
|
||||
sb.AppendLine("<b>7. GROUND CHECK SETUP:</b>");
|
||||
sb.AppendLine(string.Format(" - Radius: {0:F3}m (Radius) [x 0.60] ➔ <color=#81C784>{1:F3}m</color>", finalRadius, groundCheckRadius));
|
||||
sb.AppendLine(string.Format(" - Offset Y: {0:F3}m (Height) [/ 24] ➔ <color=#81C784>{1:F3}m</color>", finalHeight, groundCheckOffsetY));
|
||||
sb.AppendLine(string.Format(" - Offset Z: [Sync Center Z] ➔ <color=#81C784>{0:F3}m</color>", zCenterOffset));
|
||||
}
|
||||
// if (stateMachine != null)
|
||||
// {
|
||||
// float groundCheckRadius = finalRadius * 0.6f;
|
||||
// float groundCheckOffsetY = finalHeight / 24f;
|
||||
// Vector3 groundCheckOffset = new Vector3(0, groundCheckOffsetY, zCenterOffset);
|
||||
//
|
||||
// stateMachine.SetGroundCheck(groundCheckRadius, groundCheckOffset);
|
||||
//
|
||||
// sb.AppendLine("<b>7. GROUND CHECK SETUP:</b>");
|
||||
// sb.AppendLine(string.Format(" - Radius: {0:F3}m (Radius) [x 0.60] ➔ <color=#81C784>{1:F3}m</color>", finalRadius, groundCheckRadius));
|
||||
// sb.AppendLine(string.Format(" - Offset Y: {0:F3}m (Height) [/ 24] ➔ <color=#81C784>{1:F3}m</color>", finalHeight, groundCheckOffsetY));
|
||||
// sb.AppendLine(string.Format(" - Offset Z: [Sync Center Z] ➔ <color=#81C784>{0:F3}m</color>", zCenterOffset));
|
||||
// }
|
||||
|
||||
sb.AppendLine("<color=#757575>------------------------------------------------------------</color>");
|
||||
|
||||
|
||||
@@ -10,28 +10,28 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public virtual string InteractionPrompt => interactionData != null ? interactionData.promptText : "Interact";
|
||||
|
||||
public virtual void OnInteract(PlayerStateMachine player)
|
||||
{
|
||||
if (Time.time < lastInteractTime + (interactionData != null ? interactionData.interactionCooldown : 0f))
|
||||
return;
|
||||
|
||||
lastInteractTime = Time.time;
|
||||
|
||||
// Play sound if assigned
|
||||
if (interactionData != null && interactionData.interactionSound != null)
|
||||
{
|
||||
AudioSource.PlayClipAtPoint(interactionData.interactionSound, transform.position);
|
||||
}
|
||||
|
||||
// Spawn VFX if assigned
|
||||
if (interactionData != null && interactionData.interactionVFX != null)
|
||||
{
|
||||
Instantiate(interactionData.interactionVFX, transform.position, Quaternion.identity);
|
||||
}
|
||||
|
||||
PerformInteraction(player);
|
||||
}
|
||||
|
||||
protected abstract void PerformInteraction(PlayerStateMachine player);
|
||||
// public virtual void OnInteract(PlayerStateMachine player)
|
||||
// {
|
||||
// if (Time.time < lastInteractTime + (interactionData != null ? interactionData.interactionCooldown : 0f))
|
||||
// return;
|
||||
//
|
||||
// lastInteractTime = Time.time;
|
||||
//
|
||||
// // Play sound if assigned
|
||||
// if (interactionData != null && interactionData.interactionSound != null)
|
||||
// {
|
||||
// AudioSource.PlayClipAtPoint(interactionData.interactionSound, transform.position);
|
||||
// }
|
||||
//
|
||||
// // Spawn VFX if assigned
|
||||
// if (interactionData != null && interactionData.interactionVFX != null)
|
||||
// {
|
||||
// Instantiate(interactionData.interactionVFX, transform.position, Quaternion.identity);
|
||||
// }
|
||||
//
|
||||
// PerformInteraction(player);
|
||||
// }
|
||||
//
|
||||
// protected abstract void PerformInteraction(PlayerStateMachine player);
|
||||
}
|
||||
}
|
||||
@@ -60,31 +60,31 @@ namespace OnlyScove.Scripts
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PerformInteraction(PlayerStateMachine player)
|
||||
{
|
||||
Debug.Log($"[Interaction] PerformInteraction CALLED on {gameObject.name}!");
|
||||
|
||||
// 1. Ưu tiên script của Door Pack (Wood Door Script)
|
||||
if (woodDoorScript != null)
|
||||
{
|
||||
Debug.Log($"[Interaction] Calling woodDoorScript.OpenDoor() on {gameObject.name}. Previous state: {woodDoorScript.open}");
|
||||
woodDoorScript.OpenDoor();
|
||||
isOpen = woodDoorScript.open;
|
||||
Debug.Log($"[Interaction] New state: {woodDoorScript.open}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Nếu không có script Pack mới dùng Animator
|
||||
if (animator != null)
|
||||
{
|
||||
isOpen = !isOpen;
|
||||
animator.SetBool(boolParameterName, isOpen);
|
||||
Debug.Log($"[Interaction] Triggered Animator: {boolParameterName} = {isOpen}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[Interaction] FAILED: No woodDoorScript or animator found on {gameObject.name}");
|
||||
}
|
||||
}
|
||||
// protected override void PerformInteraction(PlayerStateMachine player)
|
||||
// {
|
||||
// Debug.Log($"[Interaction] PerformInteraction CALLED on {gameObject.name}!");
|
||||
//
|
||||
// // 1. Ưu tiên script của Door Pack (Wood Door Script)
|
||||
// if (woodDoorScript != null)
|
||||
// {
|
||||
// Debug.Log($"[Interaction] Calling woodDoorScript.OpenDoor() on {gameObject.name}. Previous state: {woodDoorScript.open}");
|
||||
// woodDoorScript.OpenDoor();
|
||||
// isOpen = woodDoorScript.open;
|
||||
// Debug.Log($"[Interaction] New state: {woodDoorScript.open}");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 2. Nếu không có script Pack mới dùng Animator
|
||||
// if (animator != null)
|
||||
// {
|
||||
// isOpen = !isOpen;
|
||||
// animator.SetBool(boolParameterName, isOpen);
|
||||
// Debug.Log($"[Interaction] Triggered Animator: {boolParameterName} = {isOpen}");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Debug.LogError($"[Interaction] FAILED: No woodDoorScript or animator found on {gameObject.name}");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -44,14 +44,14 @@ namespace OnlyScove.Scripts
|
||||
UpdateLightState();
|
||||
}
|
||||
|
||||
protected override void PerformInteraction(PlayerStateMachine player)
|
||||
{
|
||||
isOn = !isOn;
|
||||
UpdateLightState();
|
||||
|
||||
// Log cực mạnh để bạn kiểm tra Console
|
||||
Debug.LogWarning($"<color=yellow>[Lamp]</color> Đèn đã chuyển sang: {(isOn ? "BẬT" : "TẮT")}");
|
||||
}
|
||||
// protected override void PerformInteraction(PlayerStateMachine player)
|
||||
// {
|
||||
// isOn = !isOn;
|
||||
// UpdateLightState();
|
||||
//
|
||||
// // Log cực mạnh để bạn kiểm tra Console
|
||||
// Debug.LogWarning($"<color=yellow>[Lamp]</color> Đèn đã chuyển sang: {(isOn ? "BẬT" : "TẮT")}");
|
||||
// }
|
||||
|
||||
private void UpdateLightState()
|
||||
{
|
||||
|
||||
@@ -3,6 +3,6 @@ namespace OnlyScove.Scripts
|
||||
public interface IInteractable
|
||||
{
|
||||
string InteractionPrompt { get; }
|
||||
void OnInteract(PlayerStateMachine player);
|
||||
// void OnInteract(PlayerStateMachine player);
|
||||
}
|
||||
}
|
||||
@@ -350,16 +350,16 @@ namespace Hallucinate.UI
|
||||
|
||||
public void OnInput(NetworkRunner runner, NetworkInput input)
|
||||
{
|
||||
var data = new PlayerInputData();
|
||||
if (PlayerStateMachine.Local != null && PlayerStateMachine.Local.Input != null)
|
||||
{
|
||||
data.Direction = PlayerStateMachine.Local.Input.MoveInput;
|
||||
data.sprint = PlayerStateMachine.Local.Input.IsSprintHeld;
|
||||
data.jump = PlayerStateMachine.Local.Input.ConsumeJumpInput();
|
||||
if (PlayerStateMachine.Local.Cam != null)
|
||||
data.rot = PlayerStateMachine.Local.Cam.PlanarRotation;
|
||||
}
|
||||
input.Set(data);
|
||||
// var data = new PlayerInputData();
|
||||
// if (PlayerStateMachine.Local != null && PlayerStateMachine.Local.Input != null)
|
||||
// {
|
||||
// data.Direction = PlayerStateMachine.Local.Input.MoveInput;
|
||||
// data.sprint = PlayerStateMachine.Local.Input.IsSprintHeld;
|
||||
// data.jump = PlayerStateMachine.Local.Input.ConsumeJumpInput();
|
||||
// if (PlayerStateMachine.Local.Cam != null)
|
||||
// data.rot = PlayerStateMachine.Local.Cam.PlanarRotation;
|
||||
// }
|
||||
// input.Set(data);
|
||||
}
|
||||
|
||||
public void OnConnectedToServer(NetworkRunner runner) { }
|
||||
|
||||
@@ -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
|
||||
@@ -1,229 +1,229 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OnlyScove.Scripts
|
||||
{
|
||||
public class SukunaDomainController : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
public PlayerStateMachine playerStateMachine;
|
||||
public GameObject slashPrefab;
|
||||
public GameObject shrinePrefab;
|
||||
public VolumeProfile domainVolumeProfile;
|
||||
public Transform cinematicCameraPoint;
|
||||
|
||||
[Header("Domain Settings")]
|
||||
public float domainRadius = 15f;
|
||||
public float domainDuration = 10f;
|
||||
|
||||
[Tooltip("Số lượng vệt chém tạo ra mỗi giây")]
|
||||
public float slashRate = 15f;
|
||||
|
||||
public float shrineRiseHeight = 7f; // Độ sâu bắt đầu của miếu
|
||||
|
||||
[Tooltip("Khoảng cách từ Pivot của Miếu đến mặt sàn nơi Player đứng")]
|
||||
public float shrineFloorOffset = 0.5f;
|
||||
|
||||
public float camMoveSpeed = 4f;
|
||||
|
||||
private bool isActive = false;
|
||||
private List<GameObject> activeSlashes = new List<GameObject>();
|
||||
private Volume localVolume;
|
||||
private GameObject spawnedShrine;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (playerStateMachine == null)
|
||||
playerStateMachine = GetComponent<PlayerStateMachine>();
|
||||
|
||||
if (playerStateMachine != null && playerStateMachine.Input != null)
|
||||
{
|
||||
playerStateMachine.Input.OnPreviousInteractEvent += HandleDomainExpansion;
|
||||
Debug.Log("<color=green>[Sukuna] Sẵn sàng. slashRate: " + slashRate + "</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (playerStateMachine != null && playerStateMachine.Input != null)
|
||||
{
|
||||
playerStateMachine.Input.OnPreviousInteractEvent -= HandleDomainExpansion;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDomainExpansion()
|
||||
{
|
||||
if (isActive) return;
|
||||
Debug.Log("<color=red>[Sukuna] RYŌIKI TENKAI: FUKUMA MIZUZUSHI!</color>");
|
||||
StartCoroutine(DomainSequence());
|
||||
}
|
||||
|
||||
private IEnumerator DomainSequence()
|
||||
{
|
||||
isActive = true;
|
||||
|
||||
// Lưu vị trí ban đầu của Player (Vị trí thực tế trên mặt đất)
|
||||
Vector3 playerStartPos = playerStateMachine.transform.position;
|
||||
playerStateMachine.SetControl(false);
|
||||
|
||||
CameraController camController = playerStateMachine.Cam;
|
||||
bool originalCamEnabled = true;
|
||||
Transform mainCam = Camera.main.transform;
|
||||
|
||||
if (camController != null)
|
||||
{
|
||||
originalCamEnabled = camController.enabled;
|
||||
camController.enabled = false;
|
||||
}
|
||||
|
||||
// 1. Tạo Volume (Bóng bong lãnh địa)
|
||||
GameObject volumeObj = new GameObject("SukunaDomainVolume");
|
||||
volumeObj.transform.position = playerStartPos;
|
||||
localVolume = volumeObj.AddComponent<Volume>();
|
||||
localVolume.isGlobal = false;
|
||||
localVolume.priority = 100;
|
||||
localVolume.profile = domainVolumeProfile;
|
||||
SphereCollider volumeCollider = volumeObj.AddComponent<SphereCollider>();
|
||||
volumeCollider.isTrigger = true;
|
||||
volumeCollider.radius = 0.1f;
|
||||
|
||||
// 2. Mọc miếu và đẩy Player
|
||||
if (shrinePrefab != null)
|
||||
{
|
||||
// Spawn miếu ở vị trí rất sâu dưới chân Player
|
||||
Vector3 shrineSpawnPos = playerStartPos - Vector3.up * shrineRiseHeight;
|
||||
spawnedShrine = Instantiate(shrinePrefab, shrineSpawnPos, playerStateMachine.transform.rotation);
|
||||
|
||||
float riseDuration = 2.0f;
|
||||
float elapsed = 0;
|
||||
while (elapsed < riseDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.SmoothStep(0, 1, elapsed / riseDuration);
|
||||
|
||||
// Di chuyển miếu lên dần dần
|
||||
Vector3 currentShrinePos = Vector3.Lerp(shrineSpawnPos, playerStartPos, t);
|
||||
spawnedShrine.transform.position = currentShrinePos;
|
||||
|
||||
// Logic đẩy Player:
|
||||
// floorY là độ cao mặt sàn của miếu tại khung hình hiện tại
|
||||
float floorY = currentShrinePos.y + shrineFloorOffset;
|
||||
|
||||
// Nếu mặt sàn của miếu đã trồi lên cao hơn vị trí chân Player ban đầu
|
||||
if (floorY > playerStartPos.y)
|
||||
{
|
||||
// Player đi theo miếu
|
||||
playerStateMachine.transform.position = new Vector3(playerStartPos.x, floorY, playerStartPos.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Player đứng yên trên mặt đất ban đầu, chờ miếu trồi lên đỡ
|
||||
playerStateMachine.transform.position = playerStartPos;
|
||||
}
|
||||
|
||||
// Mở rộng bán kính Volume
|
||||
volumeCollider.radius = Mathf.Lerp(0.1f, domainRadius, t);
|
||||
|
||||
// Lia Camera mượt mà
|
||||
if (cinematicCameraPoint != null)
|
||||
{
|
||||
mainCam.position = Vector3.Lerp(mainCam.position, cinematicCameraPoint.position, Time.deltaTime * camMoveSpeed);
|
||||
mainCam.rotation = Quaternion.Slerp(mainCam.rotation, Quaternion.LookRotation((playerStateMachine.transform.position + Vector3.up * 2f) - mainCam.position), Time.deltaTime * camMoveSpeed);
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Thực thi chém liên tục dựa trên slashRate
|
||||
float timer = 0;
|
||||
float slashCooldown = 1f / slashRate;
|
||||
float lastSlashTime = 0;
|
||||
|
||||
while (timer < domainDuration)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
|
||||
if (timer - lastSlashTime >= slashCooldown)
|
||||
{
|
||||
SpawnRandomSlash(playerStateMachine.transform.position);
|
||||
lastSlashTime = timer;
|
||||
}
|
||||
|
||||
// Camera luôn theo dõi Player trên đỉnh miếu
|
||||
if (cinematicCameraPoint != null)
|
||||
{
|
||||
mainCam.position = Vector3.Lerp(mainCam.position, cinematicCameraPoint.position, Time.deltaTime * camMoveSpeed * 0.5f);
|
||||
mainCam.LookAt(playerStateMachine.transform.position + Vector3.up * 2f);
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// 4. Miếu sụp xuống (Player đứng lại vị trí Y ban đầu)
|
||||
if (spawnedShrine != null)
|
||||
{
|
||||
float sinkTime = 1f;
|
||||
float elapsed = 0;
|
||||
Vector3 currentShrinePos = spawnedShrine.transform.position;
|
||||
Vector3 targetSinkPos = currentShrinePos - Vector3.up * shrineRiseHeight;
|
||||
while (elapsed < sinkTime)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = elapsed / sinkTime;
|
||||
spawnedShrine.transform.position = Vector3.Lerp(currentShrinePos, targetSinkPos, t);
|
||||
|
||||
// Player từ từ hạ xuống sàn ban đầu
|
||||
float floorY = spawnedShrine.transform.position.y + shrineFloorOffset;
|
||||
if (floorY > playerStartPos.y)
|
||||
playerStateMachine.transform.position = new Vector3(playerStartPos.x, floorY, playerStartPos.z);
|
||||
else
|
||||
playerStateMachine.transform.position = playerStartPos;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
Destroy(spawnedShrine);
|
||||
}
|
||||
|
||||
// 5. Thu nhỏ Volume
|
||||
float shrinkDuration = 0.5f;
|
||||
float sElapsed = 0;
|
||||
while (sElapsed < shrinkDuration)
|
||||
{
|
||||
sElapsed += Time.deltaTime;
|
||||
volumeCollider.radius = Mathf.Lerp(domainRadius, 0.1f, sElapsed / shrinkDuration);
|
||||
yield return null;
|
||||
}
|
||||
Destroy(volumeObj);
|
||||
|
||||
// Dọn dẹp
|
||||
foreach (var s in activeSlashes) if (s != null) Destroy(s);
|
||||
activeSlashes.Clear();
|
||||
|
||||
if (camController != null) camController.enabled = originalCamEnabled;
|
||||
playerStateMachine.SetControl(true);
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
private void SpawnRandomSlash(Vector3 center)
|
||||
{
|
||||
Vector2 randCircle = Random.insideUnitCircle * domainRadius;
|
||||
Vector3 spawnPos = center + new Vector3(randCircle.x, Random.Range(1f, 6f), randCircle.y);
|
||||
|
||||
if (slashPrefab != null)
|
||||
{
|
||||
GameObject slash = Instantiate(slashPrefab, spawnPos, Random.rotation);
|
||||
slash.transform.localScale *= Random.Range(0.6f, 2.5f);
|
||||
activeSlashes.Add(slash);
|
||||
StartCoroutine(DestroySlashAfterTime(slash, 0.7f));
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator DestroySlashAfterTime(GameObject slash, float time)
|
||||
{
|
||||
yield return new WaitForSeconds(time);
|
||||
if (activeSlashes != null && activeSlashes.Contains(slash)) activeSlashes.Remove(slash);
|
||||
}
|
||||
}
|
||||
}
|
||||
// using UnityEngine;
|
||||
// using UnityEngine.Rendering;
|
||||
// using UnityEngine.Rendering.Universal;
|
||||
// using System.Collections;
|
||||
// using System.Collections.Generic;
|
||||
//
|
||||
// namespace OnlyScove.Scripts
|
||||
// {
|
||||
// public class SukunaDomainController : MonoBehaviour
|
||||
// {
|
||||
// [Header("References")]
|
||||
// // public PlayerStateMachine playerStateMachine;
|
||||
// public GameObject slashPrefab;
|
||||
// public GameObject shrinePrefab;
|
||||
// public VolumeProfile domainVolumeProfile;
|
||||
// public Transform cinematicCameraPoint;
|
||||
//
|
||||
// [Header("Domain Settings")]
|
||||
// public float domainRadius = 15f;
|
||||
// public float domainDuration = 10f;
|
||||
//
|
||||
// [Tooltip("Số lượng vệt chém tạo ra mỗi giây")]
|
||||
// public float slashRate = 15f;
|
||||
//
|
||||
// public float shrineRiseHeight = 7f; // Độ sâu bắt đầu của miếu
|
||||
//
|
||||
// [Tooltip("Khoảng cách từ Pivot của Miếu đến mặt sàn nơi Player đứng")]
|
||||
// public float shrineFloorOffset = 0.5f;
|
||||
//
|
||||
// public float camMoveSpeed = 4f;
|
||||
//
|
||||
// private bool isActive = false;
|
||||
// private List<GameObject> activeSlashes = new List<GameObject>();
|
||||
// private Volume localVolume;
|
||||
// private GameObject spawnedShrine;
|
||||
//
|
||||
// // private void Start()
|
||||
// // {
|
||||
// // if (playerStateMachine == null)
|
||||
// // playerStateMachine = GetComponent<PlayerStateMachine>();
|
||||
// //
|
||||
// // if (playerStateMachine != null && playerStateMachine.Input != null)
|
||||
// // {
|
||||
// // playerStateMachine.Input.OnPreviousInteractEvent += HandleDomainExpansion;
|
||||
// // Debug.Log("<color=green>[Sukuna] Sẵn sàng. slashRate: " + slashRate + "</color>");
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// // private void OnDestroy()
|
||||
// // {
|
||||
// // if (playerStateMachine != null && playerStateMachine.Input != null)
|
||||
// // {
|
||||
// // playerStateMachine.Input.OnPreviousInteractEvent -= HandleDomainExpansion;
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// private void HandleDomainExpansion()
|
||||
// {
|
||||
// if (isActive) return;
|
||||
// Debug.Log("<color=red>[Sukuna] RYŌIKI TENKAI: FUKUMA MIZUZUSHI!</color>");
|
||||
// StartCoroutine(DomainSequence());
|
||||
// }
|
||||
//
|
||||
// // private IEnumerator DomainSequence()
|
||||
// // {
|
||||
// // isActive = true;
|
||||
// //
|
||||
// // // Lưu vị trí ban đầu của Player (Vị trí thực tế trên mặt đất)
|
||||
// // Vector3 playerStartPos = playerStateMachine.transform.position;
|
||||
// // playerStateMachine.SetControl(false);
|
||||
// //
|
||||
// // CameraController camController = playerStateMachine.Cam;
|
||||
// // bool originalCamEnabled = true;
|
||||
// // Transform mainCam = Camera.main.transform;
|
||||
// //
|
||||
// // if (camController != null)
|
||||
// // {
|
||||
// // originalCamEnabled = camController.enabled;
|
||||
// // camController.enabled = false;
|
||||
// // }
|
||||
// //
|
||||
// // // 1. Tạo Volume (Bóng bong lãnh địa)
|
||||
// // GameObject volumeObj = new GameObject("SukunaDomainVolume");
|
||||
// // volumeObj.transform.position = playerStartPos;
|
||||
// // localVolume = volumeObj.AddComponent<Volume>();
|
||||
// // localVolume.isGlobal = false;
|
||||
// // localVolume.priority = 100;
|
||||
// // localVolume.profile = domainVolumeProfile;
|
||||
// // SphereCollider volumeCollider = volumeObj.AddComponent<SphereCollider>();
|
||||
// // volumeCollider.isTrigger = true;
|
||||
// // volumeCollider.radius = 0.1f;
|
||||
// //
|
||||
// // // 2. Mọc miếu và đẩy Player
|
||||
// // if (shrinePrefab != null)
|
||||
// // {
|
||||
// // // Spawn miếu ở vị trí rất sâu dưới chân Player
|
||||
// // Vector3 shrineSpawnPos = playerStartPos - Vector3.up * shrineRiseHeight;
|
||||
// // spawnedShrine = Instantiate(shrinePrefab, shrineSpawnPos, playerStateMachine.transform.rotation);
|
||||
// //
|
||||
// // float riseDuration = 2.0f;
|
||||
// // float elapsed = 0;
|
||||
// // while (elapsed < riseDuration)
|
||||
// // {
|
||||
// // elapsed += Time.deltaTime;
|
||||
// // float t = Mathf.SmoothStep(0, 1, elapsed / riseDuration);
|
||||
// //
|
||||
// // // Di chuyển miếu lên dần dần
|
||||
// // Vector3 currentShrinePos = Vector3.Lerp(shrineSpawnPos, playerStartPos, t);
|
||||
// // spawnedShrine.transform.position = currentShrinePos;
|
||||
// //
|
||||
// // // Logic đẩy Player:
|
||||
// // // floorY là độ cao mặt sàn của miếu tại khung hình hiện tại
|
||||
// // float floorY = currentShrinePos.y + shrineFloorOffset;
|
||||
// //
|
||||
// // // Nếu mặt sàn của miếu đã trồi lên cao hơn vị trí chân Player ban đầu
|
||||
// // if (floorY > playerStartPos.y)
|
||||
// // {
|
||||
// // // Player đi theo miếu
|
||||
// // playerStateMachine.transform.position = new Vector3(playerStartPos.x, floorY, playerStartPos.z);
|
||||
// // }
|
||||
// // else
|
||||
// // {
|
||||
// // // Player đứng yên trên mặt đất ban đầu, chờ miếu trồi lên đỡ
|
||||
// // playerStateMachine.transform.position = playerStartPos;
|
||||
// // }
|
||||
// //
|
||||
// // // Mở rộng bán kính Volume
|
||||
// // volumeCollider.radius = Mathf.Lerp(0.1f, domainRadius, t);
|
||||
// //
|
||||
// // // Lia Camera mượt mà
|
||||
// // if (cinematicCameraPoint != null)
|
||||
// // {
|
||||
// // mainCam.position = Vector3.Lerp(mainCam.position, cinematicCameraPoint.position, Time.deltaTime * camMoveSpeed);
|
||||
// // mainCam.rotation = Quaternion.Slerp(mainCam.rotation, Quaternion.LookRotation((playerStateMachine.transform.position + Vector3.up * 2f) - mainCam.position), Time.deltaTime * camMoveSpeed);
|
||||
// // }
|
||||
// // yield return null;
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// // 3. Thực thi chém liên tục dựa trên slashRate
|
||||
// float timer = 0;
|
||||
// float slashCooldown = 1f / slashRate;
|
||||
// float lastSlashTime = 0;
|
||||
//
|
||||
// while (timer < domainDuration)
|
||||
// {
|
||||
// timer += Time.deltaTime;
|
||||
//
|
||||
// if (timer - lastSlashTime >= slashCooldown)
|
||||
// {
|
||||
// SpawnRandomSlash(playerStateMachine.transform.position);
|
||||
// lastSlashTime = timer;
|
||||
// }
|
||||
//
|
||||
// // Camera luôn theo dõi Player trên đỉnh miếu
|
||||
// if (cinematicCameraPoint != null)
|
||||
// {
|
||||
// mainCam.position = Vector3.Lerp(mainCam.position, cinematicCameraPoint.position, Time.deltaTime * camMoveSpeed * 0.5f);
|
||||
// mainCam.LookAt(playerStateMachine.transform.position + Vector3.up * 2f);
|
||||
// }
|
||||
// yield return null;
|
||||
// }
|
||||
//
|
||||
// // 4. Miếu sụp xuống (Player đứng lại vị trí Y ban đầu)
|
||||
// if (spawnedShrine != null)
|
||||
// {
|
||||
// float sinkTime = 1f;
|
||||
// float elapsed = 0;
|
||||
// Vector3 currentShrinePos = spawnedShrine.transform.position;
|
||||
// Vector3 targetSinkPos = currentShrinePos - Vector3.up * shrineRiseHeight;
|
||||
// while (elapsed < sinkTime)
|
||||
// {
|
||||
// elapsed += Time.deltaTime;
|
||||
// float t = elapsed / sinkTime;
|
||||
// spawnedShrine.transform.position = Vector3.Lerp(currentShrinePos, targetSinkPos, t);
|
||||
//
|
||||
// // Player từ từ hạ xuống sàn ban đầu
|
||||
// float floorY = spawnedShrine.transform.position.y + shrineFloorOffset;
|
||||
// if (floorY > playerStartPos.y)
|
||||
// playerStateMachine.transform.position = new Vector3(playerStartPos.x, floorY, playerStartPos.z);
|
||||
// else
|
||||
// playerStateMachine.transform.position = playerStartPos;
|
||||
//
|
||||
// yield return null;
|
||||
// }
|
||||
// Destroy(spawnedShrine);
|
||||
// }
|
||||
//
|
||||
// // 5. Thu nhỏ Volume
|
||||
// float shrinkDuration = 0.5f;
|
||||
// float sElapsed = 0;
|
||||
// while (sElapsed < shrinkDuration)
|
||||
// {
|
||||
// sElapsed += Time.deltaTime;
|
||||
// volumeCollider.radius = Mathf.Lerp(domainRadius, 0.1f, sElapsed / shrinkDuration);
|
||||
// yield return null;
|
||||
// }
|
||||
// Destroy(volumeObj);
|
||||
//
|
||||
// // Dọn dẹp
|
||||
// foreach (var s in activeSlashes) if (s != null) Destroy(s);
|
||||
// activeSlashes.Clear();
|
||||
//
|
||||
// if (camController != null) camController.enabled = originalCamEnabled;
|
||||
// playerStateMachine.SetControl(true);
|
||||
// isActive = false;
|
||||
// }
|
||||
//
|
||||
// private void SpawnRandomSlash(Vector3 center)
|
||||
// {
|
||||
// Vector2 randCircle = Random.insideUnitCircle * domainRadius;
|
||||
// Vector3 spawnPos = center + new Vector3(randCircle.x, Random.Range(1f, 6f), randCircle.y);
|
||||
//
|
||||
// if (slashPrefab != null)
|
||||
// {
|
||||
// GameObject slash = Instantiate(slashPrefab, spawnPos, Random.rotation);
|
||||
// slash.transform.localScale *= Random.Range(0.6f, 2.5f);
|
||||
// activeSlashes.Add(slash);
|
||||
// StartCoroutine(DestroySlashAfterTime(slash, 0.7f));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private IEnumerator DestroySlashAfterTime(GameObject slash, float time)
|
||||
// {
|
||||
// yield return new WaitForSeconds(time);
|
||||
// if (activeSlashes != null && activeSlashes.Contains(slash)) activeSlashes.Remove(slash);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user