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

273 lines
7.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}