273 lines
7.7 KiB
C#
273 lines
7.7 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.Serialization;
|
||
|
||
public class LegionBattleMove : MonoBehaviour
|
||
{
|
||
public LegionLevelData.TrackState state = LegionLevelData.TrackState.Stop;
|
||
public List<LegionLevelData.BezierKnotData> Tracks = new List<LegionLevelData.BezierKnotData>();
|
||
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<LegionLevelData.BezierKnotData> 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<LegionLevelData.BezierKnotData> 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
|
||
);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算 Catmull-Rom 曲线的切线方向
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 估算曲线长度(提高匀速精度)
|
||
/// </summary>
|
||
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; // 如果不是循环,停止运动
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 计算贝塞尔曲线上的点(四控制点)
|
||
/// </summary>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算贝塞尔曲线的切线(方向)
|
||
/// </summary>
|
||
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
|
||
} |