162 lines
6.4 KiB
C#
162 lines
6.4 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|