1436 lines
53 KiB
C#
1436 lines
53 KiB
C#
using System;
|
||
using System.Threading.Tasks;
|
||
using asap.core;
|
||
using Game;
|
||
using TMPro;
|
||
using UI.PartnerGather.Mining;
|
||
using UnityEngine;
|
||
using UnityEngine.AddressableAssets;
|
||
using UnityEngine.UI;
|
||
using Quaternion = UnityEngine.Quaternion;
|
||
using Vector3 = UnityEngine.Vector3;
|
||
|
||
|
||
namespace EventPartnerGather.Mining
|
||
{
|
||
/// <summary>
|
||
/// 事件合作伙伴挖矿角色控制器
|
||
/// 控制角色的移动、动画播放、挖矿行为等
|
||
///
|
||
/// 建议挂载在:角色模型的根对象上
|
||
/// 需要组件:CharacterController(必须)
|
||
///
|
||
/// 场景结构建议:
|
||
/// Character (挂载此脚本 + CharacterController)
|
||
/// ├── Model (角色模型)
|
||
/// │ └── Animator (动画控制器)
|
||
/// └── Body (可选的身体Transform,用于旋转控制)
|
||
/// </summary>
|
||
[RequireComponent(typeof(CharacterController))]
|
||
public class EventPartnerMiningCharacterController : MonoBehaviour
|
||
{
|
||
private const bool EnableTraceLogs = false;
|
||
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");
|
||
private static readonly int ToAdvance = Animator.StringToHash("_toAdvance");
|
||
private static readonly int ToMaster = Animator.StringToHash("_toMaster");
|
||
private static readonly int ShowType = Animator.StringToHash("_showType");
|
||
private static readonly int Celebrate = Animator.StringToHash("_celebrate");
|
||
// private static readonly int Advancedmove = Animator.StringToHash("_advancedmove");
|
||
private static readonly int MasterMove = Animator.StringToHash("_masterMove");
|
||
private static readonly int AdvancedGathering = Animator.StringToHash("_advancedGathering");
|
||
private static readonly int MasterGathering = Animator.StringToHash("_masterGathering");
|
||
private static readonly int AdvancedMove = Animator.StringToHash("_advancedMove");
|
||
private static readonly int EndIdle = Animator.StringToHash("_endIdle");
|
||
private static readonly int IsGathering = Animator.StringToHash("_isGathering");
|
||
// 0 Idle 1 Move 2 Gathering 3 Celebrate
|
||
|
||
private static readonly int CurrentStatus = Animator.StringToHash("_currentStatus");
|
||
public Vector3 LastTargetPosition { get;set ;}
|
||
|
||
|
||
[Header("组件引用")] public Animator animator;
|
||
public Transform body;
|
||
public CharacterController characterController;
|
||
|
||
// [Tooltip("用户落点偏移")]
|
||
// [Range(-10f, 10f)] public List<float> userPointDelta = new();
|
||
|
||
// // 设置一下,以后修改
|
||
// [SerializeField]
|
||
// private float columnDistance = 1.0f;
|
||
|
||
[Header("移动配置")]
|
||
public float moveSpeed = 2f;
|
||
public float idleThreshold = 0.05f;
|
||
public float rotationSpeed = 5f;
|
||
|
||
[Tooltip("跟随速度")]
|
||
public float followSpeed = 3f;
|
||
|
||
[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";
|
||
|
||
|
||
[Header("头像")]
|
||
public Transform headUI;
|
||
|
||
// 头像跟随
|
||
[Header("Bone")]
|
||
public Transform targetBone;
|
||
public bool useSmoothing = true;
|
||
public float smoothingSpeed = 10f;
|
||
|
||
[Header("呼吸效果")]
|
||
public bool enableBreathing = true;
|
||
public float breathingAmplitude = 0.02f;
|
||
public float breathingFrequency = 1.2f;
|
||
public AnimationCurve breathingCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
||
|
||
[Header("变身特效")]
|
||
|
||
public Transform _fx_advancedToBasic;
|
||
public Transform _fx_basicToAdvanced;
|
||
public Transform _fx_advancedGather;
|
||
public Transform _fx_basicGather;
|
||
public Transform _fx_masterGather;
|
||
public Transform _fx_basicToMaster;
|
||
public Transform _fx_masterToBasic;
|
||
|
||
|
||
|
||
// 计算标准的变量
|
||
// 标准位置
|
||
[HideInInspector]
|
||
public int step = -1;
|
||
//
|
||
[HideInInspector]
|
||
public int col = -1 ;
|
||
|
||
|
||
//
|
||
// public Vector3 localOffset = new Vector3(0, 2f, 0);
|
||
|
||
private Vector3 localOffset;
|
||
|
||
private float localOffsetY;
|
||
|
||
private Vector3 velocity;
|
||
private Vector3 previousBonePosition;
|
||
private Vector3 currentOffset;
|
||
private float breathingTime;
|
||
|
||
|
||
|
||
private string basicAniParam = "basic";
|
||
private string advanceAniAnim = "advance";
|
||
private string masterAniAnim = "master";
|
||
|
||
// 用户积分
|
||
private TMP_Text _textNum;
|
||
private TMP_Text _textName;
|
||
|
||
|
||
// 调试信息
|
||
private Vector3 _debugTargetPosition;
|
||
private Vector3 _debugStartPosition;
|
||
private bool _isMoving = false;
|
||
private Vector3 _debugMoveDirection;
|
||
|
||
// 采集次数
|
||
private int _gatherNum;
|
||
public int GatherNumPerDuration { get; set; } = 3;
|
||
|
||
|
||
public bool IsMainCharacter { get; set; } = false;
|
||
//Task
|
||
private TaskCompletionSource<bool> _playMiningTcs;
|
||
private TaskCompletionSource<bool> _changeShowTypeTask;
|
||
private TaskCompletionSource<bool> _idleSpecialTask;
|
||
private TaskCompletionSource<bool> _playCelebrateTcs;
|
||
|
||
|
||
private int idleStateHash;
|
||
private int basicMoveHash;
|
||
// private int advancedMoveHash;
|
||
private int MasterMoveHash;
|
||
private int BasicGatheringHash;
|
||
private int BasicRunningHash;
|
||
private int BasicToAdvancedHash;
|
||
private int AdvancedRunningHash;
|
||
private int AdvancedToBasicHash;
|
||
private int AdvancedGatheringHash;
|
||
private int AdvancedIdleHash;
|
||
private int BasicToMasterHash;
|
||
private int MasterRunningHash;
|
||
private int MasterToBasicHash;
|
||
private int MasterGatheringHash;
|
||
private int MasterIdleHash;
|
||
|
||
|
||
private AudioClip audioBasic;
|
||
private AudioClip audioAdvanced;
|
||
private AudioClip audioMaster;
|
||
private Sound soundMove;
|
||
|
||
|
||
private void Awake()
|
||
{
|
||
if (!characterController)
|
||
characterController = GetComponent<CharacterController>();
|
||
if (!body)
|
||
body = transform.Find("body");
|
||
_textNum = transform.Find("ui/myself/myself/head/text_num").GetComponent<TMP_Text>();
|
||
//
|
||
_textName = transform.Find("ui/myself/myself/head/btn_head/text_name").GetComponent<TMP_Text>();
|
||
|
||
idleStateHash = Animator.StringToHash("Base Layer.Idle");
|
||
basicMoveHash = Animator.StringToHash("Base Layer.BasicMove");
|
||
MasterMoveHash = Animator.StringToHash("Base Layer.MasterMove");
|
||
BasicGatheringHash = Animator.StringToHash("Base Layer.BasicMove.BasicGathering");
|
||
BasicRunningHash = Animator.StringToHash("Base Layer.BasicMove.BasicRunning");
|
||
BasicToAdvancedHash = Animator.StringToHash("Base Layer.AdvancedMove.BasicToAdvanced");
|
||
AdvancedRunningHash = Animator.StringToHash("Base Layer.AdvancedMove.AdvancedRunning");
|
||
AdvancedToBasicHash = Animator.StringToHash("Base Layer.AdvancedMove.AdvancedToBasic");
|
||
AdvancedGatheringHash = Animator.StringToHash("Base Layer.AdvancedMove.AdvancedGathering");
|
||
AdvancedIdleHash = Animator.StringToHash("Base Layer.AdvancedMove.AdvancedIdle");
|
||
BasicToMasterHash = Animator.StringToHash("Base Layer.MasterMove.BasicToMaster");
|
||
MasterRunningHash = Animator.StringToHash("Base Layer.MasterMove.MasterRunning");
|
||
MasterToBasicHash = Animator.StringToHash("Base Layer.MasterMove.MasterToBasic");
|
||
MasterGatheringHash = Animator.StringToHash("Base Layer.MasterMove.MasterGathering");
|
||
MasterIdleHash = Animator.StringToHash("Base Layer.MasterMove.MasterIdle");
|
||
|
||
LoadAudio();
|
||
|
||
}
|
||
|
||
private async void LoadAudio()
|
||
{
|
||
audioBasic = await Addressables.LoadAssetAsync<AudioClip>("audio_ui_partnermining_npc_basicrun").Task;
|
||
audioAdvanced = await Addressables.LoadAssetAsync<AudioClip>("audio_ui_partnermining_npc_advancedrun").Task;
|
||
audioMaster = await Addressables.LoadAssetAsync<AudioClip>("audio_ui_partnermining_npc_masterrun").Task;
|
||
}
|
||
void AudioRelease()
|
||
{
|
||
Addressables.Release(audioBasic);
|
||
Addressables.Release(audioAdvanced);
|
||
Addressables.Release(audioMaster);
|
||
}
|
||
|
||
|
||
private void Start()
|
||
{
|
||
if (animator)
|
||
{
|
||
animator.GetComponent<CharacterAnimationReceiver>()?.SetCharacterController(this);
|
||
}
|
||
SetSpinPointText(0);
|
||
|
||
localOffset = new Vector3(headUI.position.x - targetBone.position.x , headUI.position.y - targetBone.position.y, 0);
|
||
|
||
localOffsetY = headUI.position.y - targetBone.position.y;
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
StopMoveAudio();
|
||
AudioRelease();
|
||
}
|
||
|
||
void StopMoveAudio()
|
||
{
|
||
if (soundMove)
|
||
{
|
||
soundMove.audioSource.Stop();
|
||
soundMove.ReturnPool();
|
||
soundMove = null;
|
||
}
|
||
}
|
||
|
||
private void LateUpdate()
|
||
{
|
||
// throw new NotImplementedException();
|
||
if (!targetBone) return;
|
||
UpdateBreathingEffect();
|
||
UpdateFollowPosition();
|
||
}
|
||
|
||
private void UpdateBreathingEffect()
|
||
{
|
||
if (!enableBreathing) return;
|
||
|
||
breathingTime += Time.deltaTime * breathingFrequency;
|
||
|
||
// 使用动画曲线获得更自然的呼吸
|
||
var breathingValue = breathingCurve.Evaluate((Mathf.Sin(breathingTime) + 1) * 0.5f);
|
||
|
||
// 应用呼吸偏移
|
||
var breathingOffset = Vector3.up * (breathingValue * breathingAmplitude);
|
||
currentOffset = localOffset + breathingOffset;
|
||
|
||
// localOffsetY
|
||
}
|
||
|
||
private void UpdateFollowPosition()
|
||
{
|
||
|
||
// var targetPosition = targetBone.position + currentOffset;// targetBone.TransformDirection(currentOffset);
|
||
|
||
var newPosition = new Vector3(headUI.position.x, targetBone.position.y + localOffsetY, headUI.position.z);
|
||
|
||
// // 预测性跟随
|
||
// if (predictiveFollowing)
|
||
// {
|
||
// var boneVelocity = (targetBone.position - previousBonePosition) / Time.deltaTime;
|
||
// targetPosition += boneVelocity * predictionStrength;
|
||
// }
|
||
|
||
// 平滑跟随
|
||
if (useSmoothing)
|
||
{
|
||
headUI.position = Vector3.SmoothDamp( headUI.position, newPosition, ref velocity, 1f / smoothingSpeed );
|
||
}
|
||
else
|
||
{
|
||
headUI.position = newPosition;
|
||
}
|
||
|
||
previousBonePosition = targetBone.position;
|
||
|
||
}
|
||
|
||
private string GetStateName()
|
||
{
|
||
var stateInfo = animator.GetCurrentAnimatorStateInfo(0);
|
||
var stateName = "NULL";
|
||
// 检查当前状态
|
||
if (stateInfo.fullPathHash == idleStateHash)
|
||
{
|
||
// Log("当前状态: Idle");
|
||
stateName = "Idle";
|
||
}
|
||
else if (stateInfo.fullPathHash == BasicRunningHash)
|
||
{
|
||
// Log("当前状态: BasicRunning");
|
||
stateName = "BasicRunning";
|
||
}
|
||
else if (stateInfo.fullPathHash == BasicGatheringHash)
|
||
{
|
||
// Log("当前状态: BasicGathering");
|
||
stateName = "BasicGathering";
|
||
}
|
||
else if (stateInfo.fullPathHash == BasicToAdvancedHash)
|
||
{
|
||
// Log("当前状态: BasicToAdvanced");
|
||
stateName = "BasicToAdvanced";
|
||
}
|
||
else if (stateInfo.fullPathHash == AdvancedRunningHash)
|
||
{
|
||
// Log("当前状态: AdvancedRunning");
|
||
stateName = "AdvancedRunning";
|
||
}
|
||
else if (stateInfo.fullPathHash == AdvancedToBasicHash)
|
||
{
|
||
// Log("当前状态: AdvancedToBasicHash");
|
||
stateName = "AdvancedToBasic";
|
||
}
|
||
else if (stateInfo.fullPathHash == AdvancedGatheringHash)
|
||
{
|
||
// Log("当前状态: AdvancedGathering");
|
||
stateName = "AdvancedGathering";
|
||
}
|
||
else if (stateInfo.fullPathHash == AdvancedIdleHash)
|
||
{
|
||
// Log("当前状态: AdvancedToBasicHash");
|
||
stateName = "AdvancedIdleHash";
|
||
}
|
||
else if (stateInfo.fullPathHash == BasicToMasterHash)
|
||
{
|
||
// Log("当前状态: BasicToMaster");
|
||
stateName = "BasicToMaster";
|
||
}
|
||
else if (stateInfo.fullPathHash == MasterRunningHash)
|
||
{
|
||
// Log("当前状态: MasterRunning");
|
||
stateName = "MasterRunning";
|
||
}
|
||
else if (stateInfo.fullPathHash == MasterGatheringHash)
|
||
{
|
||
// Log("当前状态: MasterGathering");
|
||
stateName = "MasterGathering";
|
||
}
|
||
else if (stateInfo.fullPathHash == MasterIdleHash)
|
||
{
|
||
stateName = "MasterIdleHash";
|
||
}
|
||
return stateName;
|
||
}
|
||
private 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
|
||
}
|
||
|
||
public void SetPlayerName(string buildPartnerDisplayName)
|
||
{
|
||
_textName.text = buildPartnerDisplayName;
|
||
}
|
||
|
||
public void SetHeadImage(string headImageUrl)
|
||
{
|
||
var iconHead = headUI?.Find("myself/myself/head/btn_head/mask/icon_head");
|
||
var image = iconHead?.GetComponent<Image>();
|
||
GContext.container.Resolve<IUIService>().SetHeadImage(image, headImageUrl);
|
||
}
|
||
|
||
// 看逻辑没有清理,直接设置为0
|
||
public void SetSpinPointText(int point)
|
||
{
|
||
_textNum.text = point.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移动到指定的3D位置
|
||
/// </summary>
|
||
/// <param name="targetPosition">目标位置</param>
|
||
/// <param name="speed">移动速度,0表示使用默认速度</param>
|
||
public async Task MoveToPosition_Old(Vector3 targetPosition, float speed = 0f)
|
||
{
|
||
if (speed <= 0f)
|
||
speed = moveSpeed;
|
||
var st = animator.GetInteger(ShowType);
|
||
Trace($"MoveToPositionStart target={targetPosition} showType={st}");
|
||
if (false)
|
||
{
|
||
LastTargetPosition = targetPosition;
|
||
// 设置调试信息
|
||
_debugStartPosition = body.position;
|
||
_debugTargetPosition = targetPosition;
|
||
}
|
||
|
||
_isMoving = true;
|
||
|
||
var startPos = body.position;
|
||
var 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
|
||
{
|
||
Trace($"MoveSkippedTooClose distance={direction.magnitude:F3}");
|
||
_isMoving = false;
|
||
return;
|
||
}
|
||
// return;
|
||
// 开始移动动画
|
||
SetAnimationState("moving");
|
||
// 移动到目标位置
|
||
var bodyStandPosition = body.position;
|
||
bodyStandPosition.y = targetPosition.y;
|
||
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;
|
||
try
|
||
{
|
||
characterController.Move(moveVector);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
LogError($"CharacterMoveFailed error={e.Message}");
|
||
break;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
Vector3 newPos = transform.position + moveVector;
|
||
newPos.y = transform.position.y;
|
||
transform.position = newPos;
|
||
}
|
||
bodyStandPosition = body.position;
|
||
bodyStandPosition.y = targetPosition.y;
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
Vector3 finalPos = targetPosition;
|
||
finalPos.y = transform.position.y;
|
||
transform.position = finalPos;
|
||
|
||
_ = SetCharacterDirectionVector(Vector3.right);
|
||
Trace($"MoveToPositionComplete bodyPos={body.position} transformPos={transform.position}");
|
||
_isMoving = false;
|
||
}
|
||
// 前进几列
|
||
public async Task MoveCol(int column,Vector3 theUserPoint,float columnDistance, float speed = 0f)
|
||
{
|
||
Trace($"MoveColStart column={column} userPoint={theUserPoint} lastTarget={LastTargetPosition}");
|
||
if (speed <= 0f)
|
||
speed = moveSpeed;
|
||
if (column <= 0) return;
|
||
var distance = theUserPoint.x - LastTargetPosition.x ;
|
||
if (distance < 0)
|
||
{
|
||
distance = 0;
|
||
}
|
||
// 这个是什么玩意儿。。
|
||
LastTargetPosition = theUserPoint;
|
||
// distance = 0;
|
||
|
||
Trace($"MoveColResolved column={column} userPoint={theUserPoint} distance={distance} finalDistance={column * columnDistance + distance}");
|
||
var targetPosition = new Vector3(body.position.x + column * columnDistance + distance, body.position.y, body.position.z);
|
||
SetAnimationState("moving");
|
||
var bodyStandPosition = body.position;
|
||
while (Vector3.Distance(bodyStandPosition, targetPosition) > idleThreshold)
|
||
{
|
||
var moveDirection = (targetPosition - bodyStandPosition).normalized;
|
||
moveDirection.y = 0f;
|
||
// _debugMoveDirection = moveDirection; // 更新当前移动方向
|
||
var moveVector = moveDirection * speed * Time.deltaTime;
|
||
if (characterController && characterController.enabled)
|
||
{
|
||
moveVector.y = -9.81f * Time.deltaTime;
|
||
characterController.Move(moveVector);
|
||
}
|
||
else
|
||
{
|
||
var newPos = body.position + moveVector;
|
||
newPos.y = body.position.y;
|
||
body.position = newPos;
|
||
}
|
||
bodyStandPosition = body.position;
|
||
bodyStandPosition.y = targetPosition.y;
|
||
await Awaiters.NextFrame;
|
||
}
|
||
}
|
||
|
||
public async Task MoveToPosition(Vector3 targetPosition, float speed = 0f, float maxDuration = 10f)
|
||
{
|
||
speed = speed > 0f ? speed : moveSpeed;
|
||
|
||
var st = animator.GetInteger(ShowType);
|
||
Trace($"MoveToPositionStart target={targetPosition} showType={st}");
|
||
|
||
_isMoving = true;
|
||
|
||
var startPos = body.position;
|
||
|
||
|
||
var initialDir = Horizontal(targetPosition - startPos);
|
||
if (initialDir.sqrMagnitude <= 0.0001f)
|
||
{
|
||
Trace($"MoveSkippedTooClose distance={initialDir.sqrMagnitude:F3}");
|
||
_isMoving = false;
|
||
return;
|
||
}
|
||
|
||
await SetCharacterDirectionVector(initialDir);
|
||
SetAnimationState("moving");
|
||
|
||
var startPosition = body.position;
|
||
var bodyPos = body.position;
|
||
float elapsed = 0f;
|
||
|
||
while (!IsCloseHorizontally(bodyPos, targetPosition, idleThreshold))
|
||
{
|
||
elapsed += Time.deltaTime;
|
||
if (elapsed > maxDuration)
|
||
{
|
||
LogWarning($"MoveTimeout maxDurationSec={maxDuration}");
|
||
break;
|
||
}
|
||
|
||
// 計算當前水平移動方向
|
||
var moveDir = Horizontal(targetPosition - bodyPos).normalized;
|
||
_debugMoveDirection = moveDir;
|
||
|
||
// 執行一步移動,並獲得實際水平位移量(用於檢測卡住/進度)
|
||
var actualMove = DoMoveStep(moveDir, speed);
|
||
bodyPos = body.position;
|
||
|
||
if (IsStuck(actualMove, bodyPos, targetPosition, idleThreshold))
|
||
{
|
||
Trace($"MovementBlocked distance={Horizontal(targetPosition - bodyPos).magnitude}");
|
||
if (!TryUnstuck())
|
||
break;
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
if (IsMinimalProgress(elapsed, maxDuration, startPosition, bodyPos, targetPosition))
|
||
{
|
||
LogWarning($"MoveMinimalProgress elapsedSec={elapsed:F2} maxDurationSec={maxDuration}");
|
||
break;
|
||
}
|
||
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
// 最終貼位:只調整XZ,保持當前Y
|
||
SnapToTargetXZKeepY(targetPosition);
|
||
|
||
// 維持和原行為一致:最後朝向設為 Vector3.right
|
||
_ = SetCharacterDirectionVector(Vector3.right);
|
||
|
||
Trace($"MoveToPositionComplete bodyPos={body.position} transformPos={transform.position}");
|
||
_isMoving = false;
|
||
}
|
||
|
||
/* ====================== Helpers ====================== */
|
||
// 只保留水平方向
|
||
private static Vector3 Horizontal(in Vector3 v) => new Vector3(v.x, 0f, v.z);
|
||
// 水平方向是否在閾值內
|
||
private static bool IsCloseHorizontally(in Vector3 a, in Vector3 b, float threshold)
|
||
{
|
||
var diff = Horizontal(b - a);
|
||
return diff.sqrMagnitude <= threshold * threshold;
|
||
}
|
||
|
||
// 執行一步位移,返回實際的水平位移(用於卡住判斷)
|
||
private Vector3 DoMoveStep(in Vector3 moveDir, float speed)
|
||
{
|
||
var before = body.position;
|
||
|
||
Vector3 moveVector = moveDir * speed * Time.deltaTime;
|
||
|
||
if (characterController && characterController.enabled)
|
||
{
|
||
// 添加重力,與原代碼一致
|
||
moveVector.y = -9.81f * Time.deltaTime;
|
||
try
|
||
{
|
||
characterController.Move(moveVector);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
LogError($"CharacterMoveFailed error={e.Message}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 非CharacterController:直接平移,保持Y不變
|
||
var newPos = transform.position + moveVector;
|
||
newPos.y = transform.position.y;
|
||
transform.position = newPos;
|
||
}
|
||
|
||
var after = body.position;
|
||
var actual = Horizontal(after - before);
|
||
return actual;
|
||
}
|
||
// 是否卡住
|
||
private static bool IsStuck(in Vector3 actualMove, in Vector3 bodyPos, in Vector3 target, float idleThreshold)
|
||
{
|
||
return actualMove.magnitude < 0.001f && Horizontal(target - bodyPos).magnitude > idleThreshold * 2f;
|
||
}
|
||
|
||
// 嘗試解卡:CharacterController 上抬一點;否則無法處理
|
||
private bool TryUnstuck()
|
||
{
|
||
if (characterController && characterController.enabled)
|
||
{
|
||
try
|
||
{
|
||
characterController.Move(Vector3.up * 0.1f);
|
||
return true;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
LogError($"UnstuckMoveFailed error={e.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 進度過少:時間過半但前進距離不到總距離的10%
|
||
private static bool IsMinimalProgress(float elapsed, float maxDuration, in Vector3 start, in Vector3 current, in Vector3 target)
|
||
{
|
||
if (elapsed <= maxDuration * 0.5f) return false;
|
||
|
||
float total = Horizontal(target - start).magnitude;
|
||
float progress = Horizontal(current - start).magnitude;
|
||
|
||
// 避免除0
|
||
if (total <= 0.0001f) return false;
|
||
|
||
return progress < total * 0.1f;
|
||
}
|
||
|
||
// 最終貼位:對齊XZ,保持當前Y
|
||
private void SnapToTargetXZKeepY(in Vector3 target)
|
||
{
|
||
var finalPos = new Vector3(target.x, transform.position.y, target.z);
|
||
transform.position = finalPos;
|
||
}
|
||
|
||
// FollowMOve
|
||
public async Task FollowToPosition(Vector3 targetPosition,float speed = 0f)
|
||
{
|
||
Trace($"FollowToPosition target={targetPosition}");
|
||
if (speed <= 0f)
|
||
{
|
||
speed = followSpeed;
|
||
}
|
||
await MoveToDestination(targetPosition, speed);
|
||
}
|
||
|
||
private async Task MoveToDestination(Vector3 targetPosition, float speed)
|
||
{
|
||
await MoveToPosition(targetPosition,speed);
|
||
FinishMove();
|
||
}
|
||
|
||
|
||
/// <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;
|
||
Trace($"SetDirection target={direction} targetY={targetYRotation:F1} finalRotation={targetRotation.eulerAngles}");
|
||
}
|
||
|
||
// private IEnumerator TriggerNextFrame(string state)
|
||
// {
|
||
// // animator.SetTrigger(BackToIdle);
|
||
// yield return null; // 等待一帧,让Animator处理BackToIdle的触发
|
||
// // animator.SetTrigger(BasicGathering);
|
||
// SetAnimationState(state);
|
||
// }
|
||
|
||
|
||
|
||
public async Task PlayMining( /*float duration */ )
|
||
{
|
||
Trace("PlayMining");
|
||
SetAnimationState("gathering");
|
||
// await Awaiters.Seconds(duration);
|
||
// SetAnimationState("idle");
|
||
_gatherNum = 0;
|
||
_playMiningTcs = new TaskCompletionSource<bool>();
|
||
await _playMiningTcs.Task;
|
||
}
|
||
|
||
public int GetAnimatorShowType()
|
||
{
|
||
return animator?.GetInteger(ShowType) ?? 0;
|
||
}
|
||
public void SetIdle()
|
||
{
|
||
// animator.SetTrigger(BackToIdle);
|
||
// SetAnimationState("idle");
|
||
// _ = UpdateShowType(0);
|
||
var showType = animator.GetInteger(ShowType);
|
||
Trace($"SetIdle showType={showType}");
|
||
{
|
||
animator.SetInteger(CurrentStatus,0);
|
||
animator.SetTrigger(BackToIdle);
|
||
}
|
||
}
|
||
public void SetAnimationState(string state)
|
||
{
|
||
var stateName = GetStateName();
|
||
if (!animator) return;
|
||
if (!state.Equals("idle"))
|
||
{
|
||
animator.ResetTrigger(BackToIdle);
|
||
}
|
||
var showType = animator.GetInteger(ShowType);
|
||
Trace($"SetAnimationState state={state} currentState={stateName} showType={showType}");
|
||
|
||
switch (state)
|
||
{
|
||
case "moving":
|
||
animator.SetInteger(CurrentStatus,1);
|
||
if (showType == 0)
|
||
{
|
||
animator.SetTrigger(ToBasic);
|
||
// GContext.Publish(new EventUISound("audio_ui_partnermining_npc_basicrun"));
|
||
|
||
}else if (showType == 1)
|
||
{
|
||
animator.SetTrigger(AdvancedMove);
|
||
// GContext.Publish(new EventUISound("audio_ui_partnermining_npc_advancedrun"));
|
||
}else if (showType == 2)
|
||
{
|
||
animator.SetTrigger(MasterMove);
|
||
// GContext.Publish(new EventUISound("audio_ui_partnermining_npc_masterrun"));
|
||
}
|
||
PlayMoveSoundEffect();
|
||
break;
|
||
case "gathering":
|
||
animator.SetInteger(CurrentStatus,2);
|
||
StopMoveAudio();
|
||
if (showType == 0)
|
||
{
|
||
PlayBasicGathering();
|
||
}else if (showType == 1)
|
||
{
|
||
PlayAdvancedGathering();
|
||
}else if (showType == 2)
|
||
{
|
||
var status = animator.GetInteger(CurrentStatus);
|
||
Trace($"AnimatorStatusUpdate status={status}");
|
||
PlayMasterGathering();
|
||
}
|
||
break;
|
||
case "celebrate":
|
||
// StartCelebrate();
|
||
StopMoveAudio();
|
||
animator.SetInteger(CurrentStatus,3);
|
||
animator.SetTrigger(Celebrate);
|
||
break;
|
||
default:
|
||
throw new NotSupportedException();
|
||
}
|
||
}
|
||
|
||
private void PlayMasterGathering()
|
||
{
|
||
animator.SetTrigger(MasterGathering);
|
||
// GContext.Publish(new EventUISound("audio_ui_partnermining_target_mastergather"));
|
||
}
|
||
|
||
private void PlayAdvancedGathering()
|
||
{
|
||
animator.SetTrigger(AdvancedGathering);
|
||
// GContext.Publish(new EventUISound("audio_ui_partnermining_target_advancedgather"));
|
||
}
|
||
|
||
private void PlayBasicGathering()
|
||
{
|
||
animator.SetTrigger(BasicGathering);
|
||
// GContext.Publish(new EventUISound("audio_ui_partnermining_target_basicgather"));
|
||
}
|
||
|
||
private void StartCelebrate()
|
||
{
|
||
_playCelebrateTcs = new TaskCompletionSource<bool>();
|
||
}
|
||
|
||
// private float moveTypeduration = 0.25f;
|
||
|
||
public async Task UpdateShowType(int showType)
|
||
{
|
||
var oldShowType = animator.GetInteger(ShowType);
|
||
Trace($"UpdateShowType old={oldShowType} new={showType}");
|
||
if (oldShowType == showType)
|
||
{
|
||
return;
|
||
}
|
||
_changeShowTypeTask = new TaskCompletionSource<bool>();
|
||
animator.SetInteger(ShowType,showType);
|
||
switch (showType)
|
||
{
|
||
case 0:
|
||
animator.SetTrigger( BackToIdle);
|
||
if (oldShowType == 1)
|
||
{
|
||
_fx_advancedToBasic.gameObject.SetActive(true);
|
||
}
|
||
else
|
||
{
|
||
_fx_masterToBasic.gameObject.SetActive(true);
|
||
}
|
||
|
||
break;
|
||
case 1:
|
||
animator.SetTrigger(ToAdvance);
|
||
// await Awaiters.Seconds(0.2f);
|
||
_fx_basicToAdvanced.gameObject.SetActive(true);
|
||
GContext.Publish(new EventUISound("audio_ui_partnermining_npc_mastershow"));
|
||
break;
|
||
case 2:
|
||
animator.SetTrigger(ToMaster);
|
||
_fx_basicToMaster.gameObject.SetActive(true);
|
||
GContext.Publish(new EventUISound("audio_ui_partnermining_npc_mastershow"));
|
||
break;
|
||
}
|
||
// _ = UpdateShowTypePosition(oldShowType, showType,basePos);
|
||
await _changeShowTypeTask.Task;
|
||
}
|
||
|
||
public async Task WaitForCelebrateFinished()
|
||
{
|
||
_playCelebrateTcs = new TaskCompletionSource<bool>();
|
||
await _playCelebrateTcs.Task;
|
||
}
|
||
|
||
private TaskCompletionSource<bool> _idleTask;
|
||
public async Task WaitForIdleFinished()
|
||
{
|
||
_idleTask = new TaskCompletionSource<bool>();
|
||
await _idleTask.Task;
|
||
}
|
||
|
||
// 获取当前是否在移动
|
||
public bool IsMoving()
|
||
{
|
||
return animator && animator.GetBool(runAnimParam);
|
||
}
|
||
|
||
public void OnAnimEnter(string args)
|
||
{
|
||
if (args.Equals("Gather"))
|
||
{
|
||
_gatherNum += 1;
|
||
Trace($"AnimEnter args={args} gatherNum={_gatherNum} gatherPerDuration={GatherNumPerDuration}");
|
||
if (_gatherNum > GatherNumPerDuration)
|
||
{
|
||
return;
|
||
}
|
||
var showType = animator.GetInteger(ShowType);
|
||
switch (showType)
|
||
{
|
||
case 0:
|
||
_fx_basicGather.gameObject.SetActive(true);
|
||
GContext.Publish(new EventUISound("audio_ui_partnermining_target_basicgather"));
|
||
break;
|
||
case 1:
|
||
GContext.Publish(new EventUISound("audio_ui_partnermining_target_advancedgather"));
|
||
if (_gatherNum == 1)
|
||
{
|
||
_fx_advancedGather.gameObject.SetActive(true);
|
||
}
|
||
break;
|
||
case 2:
|
||
GContext.Publish(new EventUISound("audio_ui_partnermining_target_mastergather"));
|
||
if (_gatherNum == 1)
|
||
{
|
||
_fx_masterGather.gameObject.SetActive(true);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (args.Equals("Move"))
|
||
{
|
||
// PlayMoveSoundEffect();
|
||
}
|
||
|
||
if (args.Equals("Idle"))
|
||
{
|
||
FinishIdle();
|
||
}
|
||
}
|
||
|
||
private void PlayMoveSoundEffect()
|
||
{
|
||
StopMoveAudio();
|
||
|
||
if (!IsMainCharacter) return;
|
||
|
||
var showType = animator.GetInteger(ShowType);
|
||
switch (showType)
|
||
{
|
||
case 0:
|
||
soundMove = GContext.container.Resolve<ISoundService>().GetNewUISound(audioBasic);
|
||
break;
|
||
case 1:
|
||
soundMove = GContext.container.Resolve<ISoundService>().GetNewUISound(audioAdvanced);
|
||
break;
|
||
case 2:
|
||
soundMove = GContext.container.Resolve<ISoundService>().GetNewUISound(audioMaster);
|
||
break;
|
||
}
|
||
soundMove.audioSource.Play();
|
||
soundMove.audioSource.loop = true;
|
||
}
|
||
|
||
public void OnAnimEnd(string args)
|
||
{
|
||
var stateName = GetStateName();
|
||
// Log($"OnAnimEnd -> {args} {stateName}");
|
||
if (args.Equals("Gather"))
|
||
{
|
||
_fx_basicGather.gameObject.SetActive(false);
|
||
// StopGatheringEffect();
|
||
Trace($"AnimEnd args={args} gatherNum={_gatherNum}");
|
||
// GContext.Publish(new EventMiningOre{ Num = _gatherNum});
|
||
if (_gatherNum >= GatherNumPerDuration)
|
||
{
|
||
FinishGather();
|
||
}
|
||
}
|
||
|
||
else if (args.Equals("ShowType"))
|
||
{
|
||
Trace($"AnimEnd args={args}");
|
||
FinishUpdateShowType();
|
||
}
|
||
else if (args.Equals("Idle"))
|
||
{
|
||
// Log($"-----OnAnimEnd -> {args} ");
|
||
FinishIdle();
|
||
}
|
||
else if (args.Equals("IdleSpecial"))
|
||
{
|
||
Trace($"AnimEndToIdle args={args}");
|
||
FinishIdleSpecial();
|
||
}
|
||
else if (args.Equals("Celebrate"))
|
||
{
|
||
FinishCelebrate();
|
||
}
|
||
}
|
||
|
||
public void OnAnimEvent(string args)
|
||
{
|
||
if (args.Equals("Mining"))
|
||
{
|
||
GContext.Publish(new EventMiningOre{ Num = _gatherNum});
|
||
}
|
||
|
||
if (_gatherNum == 1)
|
||
{
|
||
GContext.Publish(new EventUISound("audio_ui_partnermining_resource_break01"));
|
||
}
|
||
else if (_gatherNum == 2)
|
||
{
|
||
GContext.Publish(new EventUISound("audio_ui_partnermining_resource_break02"));
|
||
}
|
||
else if (_gatherNum == 3)
|
||
{
|
||
GContext.Publish(new EventUISound("audio_ui_partnermining_resource_break03"));
|
||
}
|
||
}
|
||
private void StopGatheringEffect()
|
||
{
|
||
// 挖矿特效
|
||
_fx_basicGather.gameObject.SetActive(false);
|
||
_fx_advancedGather.gameObject.SetActive(false) ;
|
||
_fx_masterGather.gameObject.SetActive(false);
|
||
}
|
||
|
||
public async Task WaitIdleSpecialFinish()
|
||
{
|
||
Trace("WaitIdleSpecialFinish");
|
||
animator.SetInteger(CurrentStatus,0);
|
||
var showType = animator.GetInteger(ShowType);
|
||
if (showType != 0)
|
||
{
|
||
_idleSpecialTask = new TaskCompletionSource<bool>();
|
||
await _idleSpecialTask.Task;
|
||
Trace($"WaitIdleSpecialFinishState showType={showType} endIdle={EndIdle}");
|
||
animator.SetTrigger(EndIdle);
|
||
await UpdateShowType(0);
|
||
}
|
||
}
|
||
|
||
private void FinishIdle()
|
||
{
|
||
// Log("FinishIdle->");
|
||
var success = _idleTask?.TrySetResult(true) ?? false;
|
||
if (success)
|
||
{
|
||
Trace("FinishIdle result=success");
|
||
}
|
||
}
|
||
private void FinishIdleSpecial()
|
||
{
|
||
Trace("FinishedIdleSpecial");
|
||
var success = _idleSpecialTask?.TrySetResult(true) ?? false;
|
||
if (success)
|
||
{
|
||
Trace("FinishedIdleSpecial result=success");
|
||
}
|
||
}
|
||
|
||
private void FinishUpdateShowType()
|
||
{
|
||
Trace("FinishUpdateShowType");
|
||
var success = _changeShowTypeTask.TrySetResult(true);
|
||
if (!success)
|
||
{
|
||
Trace("SetTaskResultSkipped reason=AlreadyCompleted");
|
||
}
|
||
_fx_basicToAdvanced.gameObject.SetActive(false);
|
||
_fx_advancedToBasic.gameObject.SetActive(false);
|
||
_fx_basicToMaster.gameObject.SetActive(false);
|
||
_fx_masterToBasic.gameObject.SetActive(false);
|
||
}
|
||
|
||
private void FinishGather()
|
||
{
|
||
Trace("FinishGather");
|
||
_gatherNum = 0;
|
||
GatherNumPerDuration = -1;
|
||
var ret = _playMiningTcs?.TrySetResult(true) ?? false;
|
||
if (ret)
|
||
{
|
||
Trace("FinishGather result=true");
|
||
}
|
||
|
||
StopGatheringEffect();
|
||
SetIdle();
|
||
}
|
||
|
||
public void FinishMove()
|
||
{
|
||
var showType = animator.GetInteger(ShowType);
|
||
if (showType == 0)
|
||
{
|
||
animator.ResetTrigger(ToBasic);
|
||
}else if (showType == 1)
|
||
{
|
||
animator.ResetTrigger(AdvancedMove);
|
||
}else if (showType == 2)
|
||
{
|
||
animator.ResetTrigger(MasterMove);
|
||
}
|
||
|
||
StopMoveAudio();
|
||
SetIdle();
|
||
}
|
||
private void FinishCelebrate()
|
||
{
|
||
Trace("FinishCelebrate");
|
||
var ret = _playCelebrateTcs?.TrySetResult(true) ?? false;
|
||
if (ret)
|
||
{
|
||
Trace("FinishCelebrate result=true");
|
||
}
|
||
}
|
||
|
||
/// <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);
|
||
Trace($"SetDirectionImmediate target={direction} targetY={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 ELog(object t)
|
||
{
|
||
#if UNITY_EDITOR
|
||
Debug.Log($"<color=cyan>EventPartnerMiningCharacterController-> {t} </color>");
|
||
#endif
|
||
}
|
||
private static void LogWarning(object t)
|
||
{
|
||
Debug.LogWarning($"<color=yellow>EventPartnerMiningCharacterController-> {t} </color>");
|
||
}
|
||
private static void LogError(object t)
|
||
{
|
||
Debug.LogError($"<color=red>EventPartnerMiningCharacterController-> {t} </color>");
|
||
}
|
||
|
||
[System.Diagnostics.Conditional("UNITY_EDITOR")]
|
||
private static void Trace(object t)
|
||
{
|
||
if (!EnableTraceLogs) return;
|
||
ELog(t);
|
||
}
|
||
public async void Test()
|
||
{
|
||
ELog("Test");
|
||
|
||
// this.UpdateShowType(0);
|
||
|
||
FinishMove();
|
||
animator.SetTrigger(Celebrate);
|
||
// throw new NotImplementedException();
|
||
// await UpdateShowType(2);
|
||
// animator.SetTrigger("_toAdvance");
|
||
// animator.SetTrigger(MasterGathering);
|
||
// animator.SetTrigger(MasterMove);
|
||
// await Awaiters.Seconds(0.8f);
|
||
// animator.ResetTrigger(MasterMove);
|
||
// animator.SetTrigger(MasterGathering);
|
||
}
|
||
|
||
|
||
|
||
}
|
||
} |