NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/GameWrapper/Level/LegionBattleMove.cs

273 lines
7.7 KiB
C#
Raw Normal View History

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
2025-02-13 19:12:22 +08:00
}