using System; using Cielonos.MainGame.Characters; using Sirenix.OdinInspector; using SLSUtilities.FunctionalAnimation; using UnityEngine; namespace Cielonos.MainGame.FunctionalAnimation { [Serializable] [EventColor(0.2f, 0.7f, 1.0f)] // 蓝色的时间轴事件 public class ManualMove : FuncAnimPayloadBase { public override bool IsCombatOnly => true; [Header("Target Settings")] [Tooltip("目标获取方式:从行为树的变量中获取 Vector3 坐标,或者从行为树的变量中获取 GameObject 进行追踪。")] public TargetSourceType targetSource = TargetSourceType.Vector3Variable; [ShowIf("targetSource", TargetSourceType.Vector3Variable)] [Tooltip("行为树中存储目标 Vector3 坐标的变量名称。")] public string targetPositionVariableName = "TargetPosition"; [ShowIf("targetSource", TargetSourceType.GameObjectVariable)] [Tooltip("行为树中存储目标 GameObject 的变量名称。")] public string targetGameObjectVariableName = "Target"; [ShowIf("targetSource", TargetSourceType.GameObjectVariable)] [Tooltip("是否启用前置位置预测(Lead Prediction)?仅当目标是玩家时生效。")] public bool usePlayerPrediction = false; [Header("Movement Frame Config")] [Tooltip("移动持续的动画帧数。")] public int moveFrameCount = 30; [Tooltip("动画自身的帧率(默认30帧/秒)。用于换算持续秒数。")] public float animFrameRate = 30f; [Header("Curve & Height")] [Tooltip("水平移动插值的曲线(X轴是0-1进度,Y轴是插值权重0-1)。")] public AnimationCurve horizontalCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); [Tooltip("是否启用垂直高度弧线(手动跳跃抛物线)?若为 false,将保留动画自身的 Y 轴根运动与重力。")] public bool enableVerticalArc = false; [ShowIf("enableVerticalArc")] [Tooltip("跳跃的最大拱起高度(米)。")] public float peakHeight = 3.0f; [Header("Collision & Physics")] [Tooltip("是否在移动期间临时关闭碰撞体?(建议设为 false,以使 Boss 能够正常与墙体碰撞,防止穿墙异常)")] public bool disableCollisionDuringMove = false; // 运行时状态标志 [HideInInspector] [NonSerialized] private bool isUpdateInstance = false; [HideInInspector] [NonSerialized] private Vector3 startPosition; [HideInInspector] [NonSerialized] private Vector3 targetPosition; [HideInInspector] [NonSerialized] private float duration; [HideInInspector] [NonSerialized] private float elapsedTime; [HideInInspector] [NonSerialized] private bool isFinished = false; [HideInInspector] [NonSerialized] private bool hasCleanedUp = false; [HideInInspector] [NonSerialized] private AutomataLandMovementSubcontroller movementController; [HideInInspector] [NonSerialized] private bool originalGravityState = true; public override void Invoke() { if (isUpdateInstance) { RunUpdate(); } else { StartMovement(); } } private void StartMovement() { if (Character == null) return; // 1. 获取目标点 Vector3 finalTargetPos = Character.transform.position; // 缺省为原位 BehaviorSubcontroller behaviorSc = (Character as Automata)!.behaviorSc; if (behaviorSc != null && behaviorSc.mainBehaviorTree != null) { if (targetSource == TargetSourceType.Vector3Variable) { var varObj = behaviorSc.mainBehaviorTree.GetVariable(targetPositionVariableName); if (varObj != null) { finalTargetPos = (Vector3)varObj.GetValue(); } } else if (targetSource == TargetSourceType.GameObjectVariable) { var varObj = behaviorSc.mainBehaviorTree.GetVariable(targetGameObjectVariableName); if (varObj != null && varObj.GetValue() is GameObject targetGo) { finalTargetPos = targetGo.transform.position; // 玩家位置预测 if (usePlayerPrediction && Character is Automata automata && targetGo.GetComponent() != null) { float calcDuration = moveFrameRateDuration(); finalTargetPos = automata.PredictPlayerPosition(calcDuration); } } } } // 2. 准备克隆作为每帧更新实例 ManualMove updateClone = (ManualMove)this.DeepClone(); updateClone.isUpdateInstance = true; updateClone.startPosition = Character.transform.position; updateClone.targetPosition = finalTargetPos; updateClone.duration = moveFrameRateDuration(); updateClone.elapsedTime = 0f; updateClone.isFinished = false; updateClone.hasCleanedUp = false; updateClone.movementController = Character.movementSc as AutomataLandMovementSubcontroller; if (updateClone.movementController != null) { updateClone.originalGravityState = updateClone.movementController.isApplyingGravity; // 3. 挂起导航 if (updateClone.movementController.navMeshAgent.enabled) { updateClone.movementController.navMeshAgent.enabled = false; } // 4. 若手动接管 Y 轴抛物线,则临时挂起重力计算 if (enableVerticalArc) { updateClone.movementController.isApplyingGravity = false; } } // 5. 临时关闭碰撞体 (如果勾选) if (disableCollisionDuringMove && Character.collisionSc != null) { Character.collisionSc.DisableAllColliders(); } // 6. 注册到 FuncAnim 的运行时 Update 队列中,以驱动每帧更新 runtimeFuncAnim.AddUpdateEvent(updateClone); } private void RunUpdate() { if (isFinished || Character == null) return; // 自我保护与打断清理:若动作已被打断、或当前正在播放的动作已发生更换,立刻中断 if (runtimeFuncAnim.isDisrupted || Character.animationSc.fullBodyFuncAnimSm.currentRuntimeFuncAnim != runtimeFuncAnim) { isFinished = true; EndMovement(); return; } float dt = Character.selfTimeSm.DeltaTime; elapsedTime += dt; float progress = duration > 0f ? Mathf.Clamp01(elapsedTime / duration) : 1f; // 1. 计算当前的水平坐标 XZ float curveT = horizontalCurve != null ? horizontalCurve.Evaluate(progress) : progress; Vector3 currentHorizontalPos = Vector3.Lerp(startPosition, targetPosition, curveT); // 2. 计算上一步的水平坐标 XZ float prevProgress = duration > 0f ? Mathf.Clamp01((elapsedTime - dt) / duration) : 0f; float prevCurveT = horizontalCurve != null ? horizontalCurve.Evaluate(prevProgress) : prevProgress; Vector3 prevHorizontalPos = Vector3.Lerp(startPosition, targetPosition, prevCurveT); // XZ 位移差 Vector3 deltaPosition = currentHorizontalPos - prevHorizontalPos; deltaPosition.y = 0f; // 3. 如果启用垂直高度弧线 (重写 Y 轴) if (enableVerticalArc && peakHeight > 0f) { float arcY = 4f * peakHeight * progress * (1f - progress); float prevArcY = 4f * peakHeight * prevProgress * (1f - prevProgress); float currentY = Mathf.Lerp(startPosition.y, targetPosition.y, progress) + arcY; float prevY = Mathf.Lerp(startPosition.y, targetPosition.y, prevProgress) + prevArcY; deltaPosition.y = currentY - prevY; } // 4. 将位移量传递给 MovementController 进行物理应用,而非直接赋值 transform.position if (movementController != null) { movementController.customHorizontalMovementOverride += deltaPosition; } // 5. 检查移动结束 if (progress >= 1f) { isFinished = true; EndMovement(); } } private void EndMovement() { if (hasCleanedUp) return; hasCleanedUp = true; if (Character == null) return; // 1. 恢复碰撞体 if (disableCollisionDuringMove && Character.collisionSc != null) { Character.collisionSc.EnableAllColliders(); } // 2. 重新激活并对齐导航系统,恢复重力 if (movementController != null) { movementController.isApplyingGravity = originalGravityState; if (!movementController.navMeshAgent.enabled) { movementController.navMeshAgent.enabled = true; movementController.navMeshAgent.baseOffset = 0f; // 落地瞬间重置导航高度偏移,消除浮空 movementController.navMeshAgent.Warp(Character.transform.position); } } } private float moveFrameRateDuration() { float rate = animFrameRate > 0f ? animFrameRate : 30f; return (float)moveFrameCount / rate; } public enum TargetSourceType { Vector3Variable, GameObjectVariable } } }