using System.Collections.Generic; using UnityEngine; using UnityEngine.Serialization; public class LegionBattleMove : MonoBehaviour { public LegionLevelData.TrackState state = LegionLevelData.TrackState.Stop; public List Tracks = new List(); public float speed = 3f; public bool loop = true; public int trackID = -1; public int startPos; public int endPos; private int currentIndex = 0; // 当前关键点索引 private float t = 0f; // 当前曲线段的插值进度(0 ~ 1) void Update() { if (Tracks == null || Tracks.Count < 2) return; if (state == LegionLevelData.TrackState.Stop) return; if (currentIndex >= Tracks.Count - 1) { if (loop) { currentIndex = 0; t = 0f; } else { return; // 到达最后一个点,停止移动 } } // 确保索引安全(Catmull-Rom 需要 4 个点) var p0Index = Mathf.Max(0, currentIndex - 1); var p1Index = currentIndex; var p2Index = Mathf.Min(Tracks.Count - 1, currentIndex + 1); var p3Index = Mathf.Min(Tracks.Count - 1, currentIndex + 2); var p0 = Tracks[p0Index]; var p1 = Tracks[p1Index]; var p2 = Tracks[p2Index]; var p3 = Tracks[p3Index]; // 计算 Catmull-Rom 曲线上的新位置 var newPosition = CatmullRom(p0.position, p1.position, p2.position, p3.position, t); transform.position = newPosition; // 计算方向并调整物体朝向 var tangent = CatmullRomTangent(p0.position, p1.position, p2.position, p3.position, t); transform.rotation = Quaternion.LookRotation(tangent); // 计算曲线段长度 var segmentLength = EstimateCatmullRomLength(p0.position, p1.position, p2.position, p3.position); // 匀速运动 t += (speed * Time.deltaTime) / segmentLength; // t 超过 1 时切换到下一个轨道段 while (t >= 1f && currentIndex < Tracks.Count - 1) { t -= 1f; // 保留超出的部分 currentIndex++; // 立刻更新点位 if (currentIndex >= Tracks.Count - 1) { if (loop) { currentIndex = 0; } else { return; } } } } public int GetTrackID() { return trackID; } public List GetTrackKnotData() { return Tracks; } public void SetState(LegionLevelData.TrackState newState) { state = newState; } public void SetBuildingInfo(int start, int end) { startPos = start; endPos = end; } public void SetTracks(int trackID, List newTracks, LegionLevelData.TrackState newState, int start, int end) { if (newTracks == null || newTracks.Count < 2 || trackID < 0) { Debug.LogError("无效的 BezierKnotData 轨道数据!"); return; } this.trackID = trackID; startPos = start; endPos = end; Tracks = newTracks; currentIndex = 0; t = 0f; // 立即更新物体位置和旋转 transform.position = Tracks[0].position; transform.rotation = Tracks[0].rotation; state = newState; } #region Auto模式下使用 Catmull-Rom 插值计算运动 private Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { float t2 = t * t; float t3 = t2 * t; return 0.5f * ( (2 * p1) + (-p0 + p2) * t + (2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 + (-p0 + 3 * p1 - 3 * p2 + p3) * t3 ); } /// /// 计算 Catmull-Rom 曲线的切线方向 /// private Vector3 CatmullRomTangent(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { float t2 = t * t; return 0.5f * ( (-p0 + p2) + (4 * p0 - 10 * p1 + 8 * p2 - 2 * p3) * t + (-3 * p0 + 9 * p1 - 9 * p2 + 3 * p3) * t2 ).normalized; } /// /// 估算曲线长度(提高匀速精度) /// private float EstimateCatmullRomLength(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) { float length = 0f; Vector3 prevPoint = p1; int numSamples = 10; // 采样点数 for (int i = 1; i <= numSamples; i++) { float t = i / (float)numSamples; Vector3 newPoint = CatmullRom(p0, p1, p2, p3, t); length += Vector3.Distance(prevPoint, newPoint); prevPoint = newPoint; } return length; } #endregion #region 贝塞尔曲线 private void Move() { // 获取当前段的起始点和终点 LegionLevelData.BezierKnotData p0 = Tracks[currentIndex]; LegionLevelData.BezierKnotData p1 = Tracks[currentIndex + 1]; // 计算贝塞尔曲线上的位置 Vector3 newPosition = CalculateBezierPoint(t, p0.position, p0.tangentOut, p1.tangentIn, p1.position); transform.position = newPosition; // 计算方向并调整物体朝向 Vector3 tangent = CalculateBezierTangent(t, p0.position, p0.tangentOut, p1.tangentIn, p1.position); transform.rotation = Quaternion.LookRotation(tangent); // 计算当前曲线段的近似长度 float segmentLength = EstimateBezierCurveLength(p0.position, p0.tangentOut, p1.tangentIn, p1.position); // 根据速度调整 t,确保匀速运动 t += (speed * Time.deltaTime) / segmentLength; // 确保 t 没有跳回 if (t >= 1f) { t = 0f; // 重置 t,用于下一个段 currentIndex++; // 切换到下一个轨道段 // 确保不会越界,循环轨道 if (currentIndex >= Tracks.Count - 1) { if (loop) { currentIndex = 0; // 如果是循环模式,重置 } else { return; // 如果不是循环,停止运动 } } } } /// /// 计算贝塞尔曲线上的点(四控制点) /// private Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) { float u = 1 - t; float tt = t * t; float uu = u * u; float uuu = uu * u; float ttt = tt * t; return (uuu * p0) + (3 * uu * t * p1) + (3 * u * tt * p2) + (ttt * p3); } /// /// 计算贝塞尔曲线的切线(方向) /// private Vector3 CalculateBezierTangent(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) { float u = 1 - t; float tt = t * t; float uu = u * u; Vector3 tangent = (-3 * uu * p0) + (3 * uu * t * p1 - 6 * u * t * p1) + (6 * u * t * p2 - 3 * tt * p2) + (3 * tt * p3); return tangent.normalized; } private float EstimateBezierCurveLength(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, int sampleCount = 10) { float length = 0f; Vector3 prevPoint = p0; for (int i = 1; i <= sampleCount; i++) { float t = i / (float)sampleCount; Vector3 point = CalculateBezierPoint(t, p0, p1, p2, p3); length += Vector3.Distance(prevPoint, point); prevPoint = point; } return length; } #endregion }