Files
back_cantanBuilding/Assets/Scripts/UI/PartnerGather/Mining/EventPartnerMiningCharacterController.cs
2026-05-26 16:15:54 +08:00

497 lines
18 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Threading.Tasks;
using UnityEngine;
using asap.core;
namespace UI.PartnerGather.Mining
{
/// <summary>
/// 事件合作伙伴挖矿角色控制器
/// 控制角色的移动、动画播放、挖矿行为等
///
/// 建议挂载在:角色模型的根对象上
/// 需要组件CharacterController必须
///
/// 场景结构建议:
/// Character (挂载此脚本 + CharacterController)
/// ├── Model (角色模型)
/// │ └── Animator (动画控制器)
/// └── Body (可选的身体Transform用于旋转控制)
/// </summary>
[RequireComponent(typeof(CharacterController))]
public class EventPartnerMiningCharacterController : MonoBehaviour
{
private static readonly int BasicMove = Animator.StringToHash("_basicMove");
private static readonly int BackToIdle = Animator.StringToHash("_backToIdle");
private static readonly int ToBasic = Animator.StringToHash("_toBasic");
private static readonly int BasicGathering = Animator.StringToHash("_basicGathering");
[Header("组件引用")] public Animator animator;
public Transform body;
public CharacterController characterController;
[Header("移动配置")] public float moveSpeed = 2f;
public float idleThreshold = 0.05f;
public float rotationSpeed = 5f;
[Header("模型配置")] [Tooltip("是否平滑旋转")]
public bool smoothRotation = true;
[Tooltip("旋转动画持续时间")] [Range(0.1f, 1f)]
public float rotationDuration = 0.2f;
[Header("调试配置")] public bool showDebugInfo = true;
public bool showMovementPath = true;
public Color targetPointColor = Color.red;
public Color pathColor = Color.yellow;
public Color directionColor = Color.green;
[Header("动画参数")] public string runAnimParam = "run";
public string mineAnimParam = "mine";
public string idleAnimParam = "idle";
// 调试信息
private Vector3 _debugTargetPosition;
private Vector3 _debugStartPosition;
private bool _isMoving = false;
private Vector3 _debugMoveDirection;
private void Awake()
{
if (characterController == null)
characterController = GetComponent<CharacterController>();
if (body == null) body = transform;
}
void OnDrawGizmos()
{
if (body != null && showDebugInfo)
{
// 绘制角色位置和朝向
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(body.position, 0.5f);
// 绘制前方向量(蓝色)
Gizmos.color = Color.blue;
Gizmos.DrawRay(body.position, body.forward * 2f);
// 绘制右方向量(红色)
Gizmos.color = Color.red;
Gizmos.DrawRay(body.position, body.right * 1.5f);
// 绘制上方向量(黄色,较短)
Gizmos.color = Color.yellow;
Gizmos.DrawRay(body.position, body.up * 1f);
// === 新增:移动调试可视化 ===
if (_isMoving)
{
// 绘制目标点
Gizmos.color = targetPointColor;
Gizmos.DrawWireSphere(_debugTargetPosition, 0.8f);
Gizmos.DrawCube(_debugTargetPosition + Vector3.up * 0.5f, Vector3.one * 0.3f);
// 绘制起始点
Gizmos.color = Color.cyan;
Gizmos.DrawWireCube(_debugStartPosition, Vector3.one * 0.4f);
if (showMovementPath)
{
// 绘制移动路径线
Gizmos.color = pathColor;
Gizmos.DrawLine(_debugStartPosition, _debugTargetPosition);
// 绘制当前位置到目标的连线
Gizmos.color = Color.white;
Gizmos.DrawLine(body.position, _debugTargetPosition);
// 绘制移动方向箭头
Vector3 directionArrow = _debugMoveDirection * 3f;
Gizmos.color = directionColor;
Gizmos.DrawRay(body.position + Vector3.up * 0.2f, directionArrow);
// 绘制箭头头部
Vector3 arrowEnd = body.position + Vector3.up * 0.2f + directionArrow;
Vector3 arrowSide1 = Quaternion.Euler(0, 30, 0) * (-directionArrow.normalized * 0.5f);
Vector3 arrowSide2 = Quaternion.Euler(0, -30, 0) * (-directionArrow.normalized * 0.5f);
Gizmos.DrawLine(arrowEnd, arrowEnd + arrowSide1);
Gizmos.DrawLine(arrowEnd, arrowEnd + arrowSide2);
}
}
// 显示四个主要方向(运行时)
if (Application.isPlaying)
{
Gizmos.color = Color.cyan;
// 显示四个主要方向
Gizmos.DrawRay(body.position + Vector3.up * 0.1f, Vector3.forward * 0.8f); // 前
Gizmos.DrawRay(body.position + Vector3.up * 0.1f, Vector3.back * 0.8f); // 后
Gizmos.DrawRay(body.position + Vector3.up * 0.1f, Vector3.left * 0.8f); // 左
Gizmos.DrawRay(body.position + Vector3.up * 0.1f, Vector3.right * 0.8f); // 右
}
// 显示默认朝向(编辑器中)
if (!Application.isPlaying)
{
Gizmos.color = Color.magenta;
// Unity默认前方向量
Gizmos.DrawRay(body.position, Vector3.forward * 1.5f);
}
}
#if UNITY_EDITOR
// 在编辑器中显示文字信息
if (body != null && showDebugInfo)
{
UnityEditor.Handles.color = Color.white;
string info = $"Current Rotation: {body.rotation.eulerAngles}\n";
info += $"Forward: {body.forward.ToString("F2")}\n";
info += $"Right: {body.right.ToString("F2")}";
if (_isMoving)
{
info += $"\n--- 移动调试 ---";
info += $"\n起点: {_debugStartPosition.ToString("F1")}";
info += $"\n目标: {_debugTargetPosition.ToString("F1")}";
info += $"\n当前: {body.position.ToString("F1")}";
info += $"\n方向: {_debugMoveDirection.ToString("F2")}";
float distance = Vector3.Distance(body.position, _debugTargetPosition);
info += $"\n剩余距离: {distance:F2}";
}
UnityEditor.Handles.Label(body.position + Vector3.up * 2f, info);
}
#endif
}
/// <summary>
/// 移动到指定的3D位置
/// </summary>
/// <param name="targetPosition">目标位置</param>
/// <param name="speed">移动速度0表示使用默认速度</param>
public async Task MoveToPosition(Vector3 targetPosition, float speed = 0f)
{
if (speed <= 0f) speed = moveSpeed;
// 设置调试信息
_debugStartPosition = body.position;
_debugTargetPosition = targetPosition;
_isMoving = true;
Vector3 startPos = body.position;
Vector3 direction = (targetPosition - startPos);
// 忽略Y轴方向只考虑水平面移动
direction.y = 0f;
_debugMoveDirection = direction.normalized;
// float bodyForward = Mathf.Atan2(body.forward.x, body.forward.z) * Mathf.Rad2Deg;
// 检查水平移动距离是否足够大
if (direction.magnitude > 0.01f)
{
Log($"开始移动: 从 {startPos} 到 {targetPosition}, 方向: {_debugMoveDirection}");
// 设置角色朝向目标方向
await SetCharacterDirectionVector(direction);
}
else
{
Log($"目标位置太近,跳过移动: 距离 {direction.magnitude:F3}");
_isMoving = false;
return;
}
// return;
// 开始移动动画
SetAnimationState("moving");
//TODO:LF 开发暂时停止优先制作六边形场景目前人物移动的配置还有问题3D 模型上面的UI的结构要调整
// 移动到目标位置
var bodyStandPosition = body.position;
bodyStandPosition.y = 0;
while (Vector3.Distance(bodyStandPosition, targetPosition) > idleThreshold)
{
var moveDirection = (targetPosition - bodyStandPosition).normalized;
moveDirection.y = 0f;
_debugMoveDirection = moveDirection; // 更新当前移动方向
Vector3 moveVector = moveDirection * speed * Time.deltaTime;
if (characterController && characterController.enabled)
{
// moveVector.y = -9.81f * Time.deltaTime;
characterController.Move(moveVector);
}
else
{
Vector3 newPos = body.position + moveVector;
newPos.y = body.position.y;
body.position = newPos;
}
bodyStandPosition = body.position;
bodyStandPosition.y = 0;
await Awaiters.NextFrame;
}
// SetAnimationState();
// 精确定位
// animator.SetTrigger(BackToIdle);
// animator.SetTrigger(gam);
// SetAnimationState("gathering");
Vector3 finalPos = targetPosition;
finalPos.y = body.position.y;
body.position = finalPos;
_ = SetCharacterDirectionVector(Vector3.right);
Log($"移动完成: 到达 {body.position}");
// 停止移动动画
// SetAnimationState("gathering");
// 清除调试信息
_isMoving = false;
}
/// <summary>
/// 设置角色朝向方向(基于方向向量)
/// </summary>
/// <param name="direction">目标方向向量</param>
private async Task SetCharacterDirectionVector(Vector3 direction)
{
direction.y = 0f;
// 检查方向向量是否有效(在标准化之前)
if (direction.magnitude < 0.01f) return;
direction.Normalize();
// 标准Unity旋转计算
float targetYRotation = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
Quaternion targetRotation = Quaternion.Euler(0f, targetYRotation - 90, 0f);
if (smoothRotation)
{
Quaternion startRotation = body.rotation;
float rotationTime = 0f;
while (rotationTime < rotationDuration)
{
rotationTime += Time.deltaTime;
float t = rotationTime / rotationDuration;
body.rotation = Quaternion.Slerp(startRotation, targetRotation, t);
await Awaiters.NextFrame;
}
}
body.rotation = targetRotation;
Log($"朝向设置: 目标方向{direction} -> Y旋转{targetYRotation:F1}° -> 最终旋转{targetRotation.eulerAngles}");
}
public async Task PlayMining( float duration)
{
SetAnimationState("gathering");
await Awaiters.Seconds(duration);
animator.SetTrigger(BackToIdle);
}
public void SetIdle()
{
SetAnimationState("idle");
}
private void SetAnimationState(string state)
{
if (animator == null) return;
if (state == "moving")
{
animator.SetTrigger(ToBasic);
}
if (state == "gathering")
{
animator.SetTrigger(BasicGathering);
}
if (state == "idle")
{
animator.SetTrigger(BackToIdle);
}
// 重置所有动画状态
// animator.SetBool(runAnimParam, false);
// animator.SetBool(idleAnimParam, false);
//
// switch (state.ToLower())
// {
// case "moving":
// animator.SetBool(runAnimParam, true);
// break;
//
// case "mining":
// animator.SetTrigger(mineAnimParam);
// break;
//
// case "idle":
// default:
// animator.SetBool(idleAnimParam, true);
// break;
// }
}
// 获取当前是否在移动
public bool IsMoving()
{
return animator != null && animator.GetBool(runAnimParam);
}
// 强制停止所有动作
public void StopAllActions()
{
SetAnimationState("idle");
}
/// <summary>
/// 立即设置角色朝向(基于方向向量,不带动画)
/// </summary>
/// <param name="direction">目标方向向量</param>
public void SetDirectionVectorImmediate(Vector3 direction)
{
direction.y = 0f;
// 检查方向向量是否有效(在标准化之前)
if (direction.magnitude < 0.01f) return;
direction.Normalize();
// 标准Unity旋转计算
float targetYRotation = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
body.rotation = Quaternion.Euler(0f, targetYRotation, 0f);
Log($"立即朝向: 目标方向{direction} -> Y旋转{targetYRotation:F1}°");
}
/// <summary>
/// 立即朝向目标位置(不带动画)
/// </summary>
/// <param name="targetPosition">目标位置</param>
public void LookAtPositionImmediate(Vector3 targetPosition)
{
Vector3 direction = (targetPosition - body.position).normalized;
SetDirectionVectorImmediate(direction);
}
/// <summary>
/// 重置到默认朝向
/// </summary>
public void ResetToDefaultRotation()
{
body.rotation = Quaternion.identity;
}
/// <summary>
/// 获取当前面向的方向
/// </summary>
/// <returns>1为右-1为左0为其他方向</returns>
public float GetCurrentDirection()
{
Vector3 forward = body.forward;
float dot = Vector3.Dot(forward, Vector3.right);
if (dot > 0.5f) return 1f;
if (dot < -0.5f) return -1f;
return 0f;
}
#if UNITY_EDITOR
/// <summary>
/// 编辑器辅助:重置到默认朝向
/// </summary>
[UnityEngine.ContextMenu("重置到默认朝向")]
private void ResetToDefault()
{
ResetToDefaultRotation();
}
/// <summary>
/// 编辑器辅助:测试朝向前
/// </summary>
[UnityEngine.ContextMenu("测试朝向前")]
private void TestFaceForward()
{
SetDirectionVectorImmediate(Vector3.forward);
}
/// <summary>
/// 编辑器辅助:测试移动到指定位置
/// </summary>
[UnityEngine.ContextMenu("测试移动(右前方)")]
private async void TestMoveToPosition()
{
Vector3 testTarget = body.position + new Vector3(5f, 0f, 5f);
Debug.Log($"测试移动到位置: {testTarget}");
await MoveToPosition(testTarget, 3f);
}
/// <summary>
/// 编辑器辅助:测试移动(向右)
/// </summary>
[UnityEngine.ContextMenu("测试移动(向右)")]
private async void TestMoveRight()
{
Vector3 testTarget = body.position + new Vector3(3f, 0f, 0f);
Debug.Log($"测试向右移动到: {testTarget}");
await MoveToPosition(testTarget, 2f);
}
/// <summary>
/// 编辑器辅助:测试移动(向前)
/// </summary>
[UnityEngine.ContextMenu("测试移动(向前)")]
private async void TestMoveForward()
{
Vector3 testTarget = body.position + new Vector3(0f, 0f, 3f);
Debug.Log($"测试向前移动到: {testTarget}");
await MoveToPosition(testTarget, 2f);
}
/// <summary>
/// 编辑器辅助:显示当前朝向信息
/// </summary>
[UnityEngine.ContextMenu("显示当前朝向")]
private void ShowCurrentDirection()
{
Debug.Log($"=== 角色朝向信息 ===");
Debug.Log($"当前旋转: {body.rotation.eulerAngles}");
Debug.Log($"Forward: {body.forward.ToString("F3")}");
Debug.Log($"Right: {body.right.ToString("F3")}");
Debug.Log($"Up: {body.up.ToString("F3")}");
}
/// <summary>
/// 编辑器辅助:验证旋转逻辑
/// </summary>
[UnityEngine.ContextMenu("验证旋转逻辑")]
private void ValidateRotationLogic()
{
Debug.Log($"=== 标准Unity旋转逻辑验证 ===");
// 测试四个主要方向
Vector3[] testDirections =
{
Vector3.right, // 向右 (1, 0, 0)
Vector3.left, // 向左 (-1, 0, 0)
Vector3.forward, // 向前 (0, 0, 1)
Vector3.back // 向后 (0, 0, -1)
};
string[] directionNames = { "右", "左", "前", "后" };
for (int i = 0; i < testDirections.Length; i++)
{
Vector3 direction = testDirections[i];
float expectedYRotation = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
Debug.Log($"方向{directionNames[i]}: " +
$"输入向量{direction} -> " +
$"Y旋转{expectedYRotation:F1}°");
}
}
#endif
private static void Log(object t)
{
Debug.Log($"<color=cyan>EventPartnerMiningCharacterController-> {t} </color>");
}
}
}