如何更新Unity游戏对象以沿着样条曲线移动?

4
下午好,
我正在尝试在Unity中实现一个GameObject沿着Cubic CatMull-Rom样条线移动,给定8个受限随机值。我已经实现了一个函数ComputePointOnCatmullRomCurve,它返回Cubic Catmull-Rom曲线上的一个点(给定从0到1的标量'u'和指示用于插值的哪4个点的segment_number)。
我遇到了麻烦,实现更新功能以允许GameObject平稳移动。我目前每次调用更新时都会调用ComputePointOnCatmullRomCurve并逐步增加segment_number。然后,GameObject的位置被设置为该函数的结果。
然而,这导致GameObject移动得非常快。我认为我的Update函数是不正确的,但我不确定如何相对于插值函数输出的点移动GameObject。
如果有人能够向我解释我做错了什么,或者提供一个示例或链接到示例,那将非常有帮助! 计算曲线上一点的函数:
Vector3 ComputePointOnCatmullRomCurve(float u, int segmentNumber)
{
    // TODO - compute and return a point as a Vector3       
    // Points on segment number 0 start at controlPoints[0] and end at controlPoints[1]
    // Points on segment number 1 start at controlPoints[1] and end at controlPoints[2]
    //       etc...

    Vector3 point = new Vector3();

    float c0 = ((-u + 2f) * u - 1f) * u * 0.5f;
    float c1 = (((3f * u - 5f) * u) * u + 2f) * 0.5f;
    float c2 = ((-3f * u + 4f) * u + 1f) * u * 0.5f;
    float c3 = ((u - 1f) * u * u) * 0.5f;

    Vector3 p0 = controlPoints[(segmentNumber - 1) % NumberOfPoints];
    Vector3 p1 = controlPoints[segmentNumber % NumberOfPoints];
    Vector3 p2 = controlPoints[(segmentNumber + 1) % NumberOfPoints];
    Vector3 p3 = controlPoints[(segmentNumber + 2) % NumberOfPoints];

    point.x = (p0.x * c0) + (p1.x * c1) + (p2.x * c2) + (p3.x * c3);
    point.y = (p0.y * c0) + (p1.y * c1) + (p2.y * c2) + (p3.y * c3);
    point.x = (p0.z * c0) + (p1.z * c1) + (p2.z * c2) + (p3.z * c3);

    return point;
}

更新功能:

void Update () {
    // TODO - use time to determine values for u and segment_number in this function call
    // 0.5 Can be used as u
    time += DT;

    segCounter++;

    Vector3 temp = ComputePointOnCatmullRomCurve(time, segCounter);
    transform.position = temp;
}

变量:

const int NumberOfPoints = 8;
Vector3[] controlPoints;

const int MinX = -5;
const int MinY = -5;
const int MinZ = 0;

const int MaxX = 5;
const int MaxY = 5;
const int MaxZ = 5;

float time = 0;
const float DT = 0.01f;
public static int segCounter = 0;

Thanks!

Matt


1
既然你说它运动得太快了,那么将 time += DT; 更改为 time += DT * Time.deltaTime; 是否可以解决你的时间问题?请参阅 http://docs.unity3d.com/ScriptReference/Time-deltaTime.html。 - Chris McFarland
你是否在每个Update调用中都增加实体所在的段? - Matt Coubrough
@Chris 添加 Time.deltaTime 不能解决问题。时间变量可以设为常数,比如0.5,但快速运动仍会发生。 - Marcus Koz
@MattCoubrough 是的,线段的参考点在每次更新时都会增加,用于基于该初始点插值的其他三个点在ComputePointOnCatmullRomCurve中进行初始化。 - Marcus Koz
1个回答

4

您在更新函数中犯了两个错误。

第一个错误:

您在每帧都增加当前段的索引(segmentNumber)。我猜这应该只在对象沿着前一个样条段行进完成时才做。

提示:

对于由多个补丁定义的样条曲线,我通常将时间(u)表达为范围 [0,n],其中 n 是定义曲线的段数。这样,您可以通过从参数中提取整数部分来检索当前补丁的索引(segmentNumber)。类似于:

int segmentNumber =  Mathf.FloorToInt(u);
float segmentU = u - segmentNumber;

第二个错误

我不知道你的DT变量是什么,但除非你在其他地方按帧增加它,否则你必须这样做。基本上,你可以通过以下方式增加时间:

time += Time.deltaTime * speedAlongCurve;

我希望这能有所帮助。


好的,时间的变化将允许GameObject沿着曲线移动。每次更新更改GameObject的索引会强制它从一个位置跳到另一个位置。所以问题是在适当的时候增加segmentNumber,对吗?很抱歉,我很难理解您在第一个代码片段中尝试实现什么。 - Marcus Koz
如上所述,我很难理解您包含的代码片段的功能。我能够通过仅在(时间==1)时递增段号来获得功能实现。我还在那一点上将时间重置为0。但是,我有一种感觉,这只是巧合而已,而且有一种更合适的方法来循环遍历段。 - Marcus Koz
1
@Matt Koczwara:没事了,第一个片段只是一个提示,应该在时间等于1时与增加段索引的功能相当。所以如果你这样做并且它有效,那就没问题了。第一个片段只是做同样的事情:例如,假设你在时间0.5处的第二个seg中,使用你的方法-> t=0.5 seg=1,使用片段tTot = 1.5 seg = floortoint(1.5) = 1,t = 1.5 - 1 = 0.5。这是一样的。 - Heisenbug

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接