497 lines
18 KiB
C#
497 lines
18 KiB
C#
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>");
|
||
}
|
||
}
|
||
} |