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