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);
}
}
}