using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.VisualScripting;
using UnityEngine;
namespace DS
{
///
/// 玩家角色移动/旋转控制器
/// 负责处理玩家的移动、旋转,并与输入系统、动画系统、物理系统进行交互
///
public class PlayerLocomotion : MonoBehaviour
{
// ============================================================
// 根运动混合参数
// ============================================================
///
/// 根运动(Root Motion)与程序化移动的混合比例。
/// 0 = 完全使用程序化移动(手动计算位移),
/// 1 = 完全使用动画中的根运动(由动画驱动位移)。
/// 建议在跑步、行走等需要动画精细控制位移时使用根运动;
/// 在需要响应式操控(如闪避、冲刺)时可降低此值。
///
[Range(0, 1)] public float rootMotionBlending = 1f;
///
/// 便捷属性:将 rootMotionBlending 映射为布尔值。
/// true = 使用根运动(blending > 0.5),
/// false = 使用程序化移动(blending <= 0.5)。
///
public bool usingRootMotion
{
get => rootMotionBlending > 0.5f;
set => rootMotionBlending = value ? 0 : 1;
}
// ============================================================
// 公开字段
// ============================================================
///
/// 当前帧从动画中提取的根运动位移向量(由 AnimatorHandler 提供)。
/// 单位:米/帧。
///
[HideInInspector] public Vector3 rootMotion;
///
/// 当前角色的 Transform 引用(缓存避免重复查找)。
///
[HideInInspector] public Transform myTransform;
///
/// 动画处理器引用(负责驱动 Animator 参数和根运动提取)。
///
[HideInInspector] public AnimatorHandler animatorHandler;
///
/// 角色的 Rigidbody 组件引用(用于物理驱动移动)。
/// 使用 new 关键字隐藏父类 Rigidbody 字段以避免混淆。
///
public new Rigidbody rigidbody;
///
/// 常规第三人称摄像机对象的引用(用于旋转跟随等逻辑)。
///
public GameObject normalCamera;
// ============================================================
// 私有字段
// ============================================================
private Transform _cameraObject; // 主摄像机的 Transform 缓存
private InputHandler _inputHandler; // 输入处理器引用
private Vector3 _moveDirection; // 计算出的移动方向向量
private Vector3 _normalVector; // 地面法线向量(用于投影移动方向到地面)
private Vector3 _targetPosition; // 目标位置(当前未使用,保留备用)
// ============================================================
// 参数配置
// ============================================================
[Header("Stats")]
[SerializeField] private float movementSpeed = 5; // 移动速度(单位:米/秒)
[SerializeField] private float sprintSpeed = 5; //冲刺速度
[SerializeField] private float rotationSpeed = 10; // 旋转速度(单位:度/秒,实际用于 Slerp 插值)
public bool isSprinting;
// ============================================================
// 初始化
// ============================================================
private void Start()
{
// 获取并缓存 Rigidbody 组件
rigidbody = GetComponent();
// 获取并缓存输入处理器
_inputHandler = GetComponent();
// 在子物体中查找 AnimatorHandler 组件并缓存
animatorHandler = GetComponentInChildren();
// 缓存主摄像机的 Transform
_cameraObject = Camera.main.transform;
// 缓存当前物体的 Transform
myTransform = transform;
// 初始化 AnimatorHandler(例如获取 Animator 组件引用等)
animatorHandler.Initialize();
}
// ============================================================
// 每帧更新(使用 Update 处理移动逻辑,因为需要与输入同步)
// ============================================================
// ============================================================
// 固定更新(当前未使用,但保留以备未来物理相关逻辑)
// ============================================================
private void FixedUpdate()
{
// 当前未在此处处理逻辑
// 若需要基于物理的移动,可将 rigidbody.velocity 赋值移到这里
}
// ============================================================
// 移动与旋转相关方法
// ============================================================
#region Movement
///
/// 处理角色旋转逻辑。
/// 根据输入方向和摄像机朝向,平滑旋转角色面向移动方向。
///
/// 帧时间(秒)
private void HandleRotation(float delta)
{
Vector3 targetDir = Vector3.zero; // 目标朝向向量
// 注意:当前未使用 moveOverride,保留备用
float moveOverride = _inputHandler.moveAmount;
// ----------------------------------------------------------
// 1. 根据输入计算目标朝向
// ----------------------------------------------------------
// 目标朝向 = 摄像机正方向 * 垂直输入 + 摄像机右方向 * 水平输入
targetDir = _cameraObject.forward * _inputHandler.vertical;
targetDir += _cameraObject.right * _inputHandler.horizontal;
// 归一化并置平 Y 轴
targetDir.Normalize();
targetDir.y = 0;
// 如果没有输入(目标方向为零向量),则保持当前面向不变
if (targetDir == Vector3.zero)
{
targetDir = myTransform.forward;
}
// ----------------------------------------------------------
// 2. 通过 Slerp 平滑旋转至目标方向
// ----------------------------------------------------------
float rs = rotationSpeed; // 旋转速度
Quaternion tr = Quaternion.LookRotation(targetDir); // 目标旋转四元数
// 使用球形插值(Slerp)实现平滑旋转
Quaternion targetRotation = Quaternion.Slerp(myTransform.rotation, tr, rs * delta);
// 应用旋转
myTransform.rotation = targetRotation;
}
// ============================================================
// 角色移动更新(核心逻辑)
// ============================================================
public void UpdateCharacterMovement(float delta)
{
if (_inputHandler.rollFlag)
return;
// ----------------------------------------------------------
// 1. 计算移动方向(基于摄像机朝向)
// ----------------------------------------------------------
// 前方向量 = 摄像机正方向 * 垂直输入(W/S)
_moveDirection = _cameraObject.forward * _inputHandler.vertical;
// 侧方向量 = 摄像机右方向 * 水平输入(A/D)
_moveDirection += _cameraObject.right * _inputHandler.horizontal;
// 归一化,确保对角线移动时速度不叠加(保持长度 1)
_moveDirection.Normalize();
// 将 y 分量置 0,确保移动只在水平面上
_moveDirection.y = 0;
// ----------------------------------------------------------
// 2. 应用移动速度
// ----------------------------------------------------------
float speed = movementSpeed;
if (_inputHandler.sprintFlag)
{
speed = sprintSpeed;
isSprinting = true;
_moveDirection *= speed;
}
else
{
_moveDirection *= speed; // 现在 _moveDirection 是速度向量(单位:米/秒)
}
// ----------------------------------------------------------
// 3. 混合根运动与程序化移动
// ----------------------------------------------------------
// 根据 rootMotionBlending 在根运动位移和程序化速度之间做线性插值
var baseVelocity = Vector3.Lerp(rootMotion, _moveDirection, rootMotionBlending);
// 将速度向量投影到地面法线平面上(防止角色飞起或陷入地面)
Vector3 projectedVelocity = Vector3.ProjectOnPlane(baseVelocity, _normalVector);
// 设置 Rigidbody 速度(物理驱动移动)
rigidbody.velocity = new Vector3(
projectedVelocity.x,
// 保留原始的 Velocity Y
rigidbody.velocity.y,
projectedVelocity.z);
// ----------------------------------------------------------
// 4. 更新动画参数
// ----------------------------------------------------------
// 将玩家的移动量(0~1)传递给 Animator,用于混合行走/奔跑动画
animatorHandler.UpdateAnimatorValues(_inputHandler.moveAmount, 0,isSprinting);
// ----------------------------------------------------------
// 5. 更新翻滚参数
// ----------------------------------------------------------
if (_inputHandler.rollTriggered)
{
animatorHandler.TriggerRoll();
_inputHandler.rollTriggered = false;
}
// ----------------------------------------------------------
// 5. 处理旋转(仅在允许旋转时执行)
// ----------------------------------------------------------
if (animatorHandler.canRotate)
{
HandleRotation(delta);
}
}
public void HandleRollingAndSpringting(float delta)
{
if (animatorHandler.animator.GetBool("isInteracting"))
{
return;
}
if (_inputHandler.rollFlag)
{
_moveDirection = _cameraObject.forward * _inputHandler.vertical;
_moveDirection += _cameraObject.right * _inputHandler.horizontal;
if (_inputHandler.moveAmount > 0)
{
animatorHandler.PlayerTargetAnimation("Roll",true);
_moveDirection.y = 0;
Quaternion rollRotation = Quaternion.LookRotation(_moveDirection);
myTransform.rotation = rollRotation;
}
else
{
animatorHandler.PlayerTargetAnimation("step_back",true);
}
}
}
#endregion
}
}