using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace DS
{
///
/// 动画处理器 - 负责管理角色动画参数的更新、根运动(Root Motion)的提取,
/// 以及角色旋转状态的控制。
///
public class AnimatorHandler : MonoBehaviour
{
[Header("绑定")]
public Transform rootMotionRotation; // 将模型旋转绑定到 RootMotion
[Header("组件引用")]
public PlayerLocomotion playerLocomotion; // 玩家移动控制器引用,用于传递根运动数据
public Animator animator; // 动画器组件引用
public InputHandler inputHandler;
public PlayerManager playerManager;
[Header("动画参数哈希")]
private int _vertical; // "Vertical" 动画参数的哈希值,用于高效设置动画参数
private int _horizontal; // "Horizontal" 动画参数的哈希值
private int _roll; // "Roll" 动画参数的哈希值
[Header("旋转状态")]
public bool canRotate; // 是否允许角色旋转,在动画事件中控制
///
/// 初始化函数 - 获取 Animator 组件并计算动画参数的哈希值。
/// 哈希值用于替代字符串名称,提高性能。
///
public void Initialize()
{
// 获取当前 GameObject 上的 Animator 组件
animator = GetComponent();
inputHandler = GetComponentInParent();
playerLocomotion = GetComponentInParent();
// 使用 Animator.StringToHash 将字符串参数名转换为整数哈希值
// 这样在 SetFloat 时使用哈希值比使用字符串更高效
_vertical = Animator.StringToHash("Vertical");
_horizontal = Animator.StringToHash("Horizontal");
_roll = Animator.StringToHash("Roll");
}
///
/// 更新动画参数值 - 将输入的移动数值映射为动画混合树使用的离散值。
/// 输入值范围通常为 -1 到 1,将其量化为 -1, -0.5, 0, 0.5, 1 五个档位,
/// 以实现从慢走到快跑的平滑过渡。
///
/// 垂直方向的移动输入(前进/后退),范围 -1 ~ 1
/// 水平方向的移动输入(左移/右移),范围 -1 ~ 1
public void UpdateAnimatorValues(float verticalMovement, float horizontalMovement,bool isSprinting)
{
#region Vertical — 垂直方向(前进/后退)的动画参数处理
float v = 0; // 存储映射后的垂直动画参数值
// 判断垂直移动输入,将其量化为几档:
// 0~0.55 之间视为慢速行走(0.5),大于 0.55 视为奔跑(1)
// 负值同理,处理后退动作
if (verticalMovement > 0 && verticalMovement < 0.55f)
{
v = 0.5f; // 慢速前进
}
else if(verticalMovement > 0.55f)
{
v = 1; // 快速前进/奔跑
}
else if (verticalMovement < 0 && verticalMovement > -0.55f)
{
v = -0.5f; // 慢速后退
}
else if(verticalMovement < -0.55f)
{
v = -1; // 快速后退
}
else
{
v = 0; // 无输入或死区范围内,保持静止
}
#endregion
#region Horizontal — 水平方向(左/右)的动画参数处理
float h = 0; // 存储映射后的水平动画参数值
// 同样将水平移动输入量化为几档,用于控制侧移或转身动画
if (horizontalMovement > 0 && horizontalMovement < 0.55f)
{
h = 0.5f; // 慢速向右
}
else if(horizontalMovement > 0.55f)
{
h = 1; // 快速向右
}
else if (horizontalMovement < 0 && horizontalMovement > -0.55f)
{
h = -0.5f; // 慢速向左
}
else if(horizontalMovement < -0.55f)
{
h = -1; // 快速向左
}
else
{
h = 0; // 无输入或死区范围内,保持静止
}
#endregion
if (isSprinting)
{
v = 2;
h = horizontalMovement;
}
// 将计算后的参数值设置到 Animator 中
// 第三个参数 dampTime 为 0.1f,表示在 0.1 秒内平滑过渡到目标值
// 第四个参数 deltaTime 用于确保平滑速度与帧率无关
animator.SetFloat(_vertical, v, 0.1f, Time.deltaTime);
animator.SetFloat(_horizontal, h, 0.1f, Time.deltaTime);
}
public void PlayerTargetAnimation(string targetAnim, bool isInteractions)
{
Debug.Log($"尝试播放动画: {targetAnim}");
animator.applyRootMotion = isInteractions;
animator.SetBool("isInteracting",isInteractions);
animator.CrossFade(targetAnim,0.2f);
}
///
/// 触发动画的翻滚
///
public void TriggerRoll()
{
animator.SetTrigger(_roll);
}
///
/// 动画事件回调 - 允许角色在动画的特定帧开始旋转。
/// 通常在动画的某个时间点(如转身动画的起始帧)由动画事件调用。
///
public void CanRotate()
{
canRotate = true; // 启用旋转
}
///
/// 动画事件回调 - 停止角色的旋转。
/// 通常在动画的某个时间点(如转身动画的结束帧)由动画事件调用,
/// 防止角色在不需要旋转时继续旋转。
///
public void StopRotation()
{
canRotate = false; // 禁用旋转
}
///
/// MonoBehaviour 生命周期函数 - 在 Animator 更新动画时每帧调用。
/// 用于处理根运动(Root Motion)数据,将动画驱动的位移传递给 PlayerLocomotion。
/// 需要勾选 Animator 组件的"Apply Root Motion"选项才能触发此回调。
///
private void OnAnimatorMove()
{
// 获取当前帧的根运动位移向量
var rootMotion = GetRootMotion();
// 将根运动数据传递给 PlayerLocomotion 组件
// PlayerLocomotion 会将这些位移应用到角色控制器上
playerLocomotion.rootMotion = rootMotion;
if (playerManager.isInteracting == false)
{
return;
}
//更改刚体速度方案
// float delta = Time.deltaTime;
// playerLocomotion.rigidbody.drag = 0;
// Vector3 deltaPosition = animator.deltaPosition;
// deltaPosition.y = 0;
// Vector3 velocity = deltaPosition / delta;
// playerLocomotion.rigidbody.velocity = velocity;
}
///
/// 编辑器辅助函数 - 在 Scene 视图中绘制调试线条,
/// 可视化显示根运动的方向和大小(放大 2 倍以便观察)。
/// 仅在编辑器中有效,不会影响运行时性能。
///
private void OnDrawGizmos()
{
// 从角色当前位置绘制一条线到 当前位置 + 根运动方向 * 2
Gizmos.DrawLine(transform.position, transform.position + GetRootMotion() * 2f);
}
///
/// 提取根运动向量 - 从 Animator 的当前速度中提取水平方向上的位移,
/// 忽略垂直方向(Y 轴)的运动。
/// 这样可以防止动画中的微小垂直位移影响角色位置(如地面抖动)。
///
/// 返回一个 Vector3,表示投影到水平平面上的根运动位移
private Vector3 GetRootMotion()
{
// 假定地面法线始终朝上(即世界坐标的 Y 轴方向)
// 注意:如果角色在斜坡上运动,此处需要根据地面法线动态调整
var normalVector = Vector3.up;
// 将 animator.velocity(动画驱动的速度向量)投影到法线定义的平面上
// Vector3.ProjectOnPlane 会去除向量中沿法线方向的分量
// 这样只保留水平方向的位移,避免 Y 轴上的微小波动影响角色位置
// return Vector3.ProjectOnPlane(animator.velocity, normalVector);
// return Vector3.ProjectOnPlane(animator.velocity, normalVector);
return Vector3.ProjectOnPlane(animator.velocity, normalVector);
}
}
}