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 { /// /// 事件合作伙伴挖矿角色控制器 /// 控制角色的移动、动画播放、挖矿行为等 /// /// 建议挂载在:角色模型的根对象上 /// 需要组件:CharacterController(必须) /// /// 场景结构建议: /// Character (挂载此脚本 + CharacterController) /// ├── Model (角色模型) /// │ └── Animator (动画控制器) /// └── Body (可选的身体Transform,用于旋转控制) /// [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 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 _playMiningTcs; private TaskCompletionSource _changeShowTypeTask; private TaskCompletionSource _idleSpecialTask; private TaskCompletionSource _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(); if (!body) body = transform.Find("body"); _textNum = transform.Find("ui/myself/myself/head/text_num").GetComponent(); // _textName = transform.Find("ui/myself/myself/head/btn_head/text_name").GetComponent(); 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("audio_ui_partnermining_npc_basicrun").Task; audioAdvanced = await Addressables.LoadAssetAsync("audio_ui_partnermining_npc_advancedrun").Task; audioMaster = await Addressables.LoadAssetAsync("audio_ui_partnermining_npc_masterrun").Task; } void AudioRelease() { Addressables.Release(audioBasic); Addressables.Release(audioAdvanced); Addressables.Release(audioMaster); } private void Start() { if (animator) { animator.GetComponent()?.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(); GContext.container.Resolve().SetHeadImage(image, headImageUrl); } // 看逻辑没有清理,直接设置为0 public void SetSpinPointText(int point) { _textNum.text = point.ToString(); } /// /// 移动到指定的3D位置 /// /// 目标位置 /// 移动速度,0表示使用默认速度 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(); } /// /// 设置角色朝向方向(基于方向向量) /// /// 目标方向向量 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(); 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(); } // 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(); 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(); await _playCelebrateTcs.Task; } private TaskCompletionSource _idleTask; public async Task WaitForIdleFinished() { _idleTask = new TaskCompletionSource(); 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().GetNewUISound(audioBasic); break; case 1: soundMove = GContext.container.Resolve().GetNewUISound(audioAdvanced); break; case 2: soundMove = GContext.container.Resolve().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(); 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"); } } /// /// 立即设置角色朝向(基于方向向量,不带动画) /// /// 目标方向向量 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}"); } /// /// 立即朝向目标位置(不带动画) /// /// 目标位置 public void LookAtPositionImmediate(Vector3 targetPosition) { Vector3 direction = (targetPosition - body.position).normalized; SetDirectionVectorImmediate(direction); } /// /// 重置到默认朝向 /// public void ResetToDefaultRotation() { body.rotation = Quaternion.identity; } /// /// 获取当前面向的方向 /// /// 1为右,-1为左,0为其他方向 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 // /// // /// 编辑器辅助:重置到默认朝向 // /// // [UnityEngine.ContextMenu("重置到默认朝向")] // private void ResetToDefault() // { // ResetToDefaultRotation(); // } // // /// // /// 编辑器辅助:测试朝向前 // /// // [UnityEngine.ContextMenu("测试朝向前")] // private void TestFaceForward() // { // SetDirectionVectorImmediate(Vector3.forward); // } // // /// // /// 编辑器辅助:测试移动到指定位置 // /// // [UnityEngine.ContextMenu("测试移动(右前方)")] // private async void TestMoveToPosition() // { // Vector3 testTarget = body.position + new Vector3(5f, 0f, 5f); // Debug.Log($"测试移动到位置: {testTarget}"); // await MoveToPosition(testTarget, 3f); // } // // /// // /// 编辑器辅助:测试移动(向右) // /// // [UnityEngine.ContextMenu("测试移动(向右)")] // private async void TestMoveRight() // { // Vector3 testTarget = body.position + new Vector3(3f, 0f, 0f); // Debug.Log($"测试向右移动到: {testTarget}"); // await MoveToPosition(testTarget, 2f); // } // // /// // /// 编辑器辅助:测试移动(向前) // /// // [UnityEngine.ContextMenu("测试移动(向前)")] // private async void TestMoveForward() // { // Vector3 testTarget = body.position + new Vector3(0f, 0f, 3f); // Debug.Log($"测试向前移动到: {testTarget}"); // await MoveToPosition(testTarget, 2f); // } // // /// // /// 编辑器辅助:显示当前朝向信息 // /// // [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")}"); // } // // /// // /// 编辑器辅助:验证旋转逻辑 // /// // [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($"EventPartnerMiningCharacterController-> {t} "); #endif } private static void LogWarning(object t) { Debug.LogWarning($"EventPartnerMiningCharacterController-> {t} "); } private static void LogError(object t) { Debug.LogError($"EventPartnerMiningCharacterController-> {t} "); } [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); } } }