如何更新 Unity GameObject 以沿样条曲线移动?

Posted

技术标签:

【中文标题】如何更新 Unity GameObject 以沿样条曲线移动?【英文标题】:How to update Unity GameObject to move along Spline Curve? 【发布时间】:2014-12-30 16:49:30 【问题描述】:

下午好,

我正在尝试在 Unity 中实现一个 GameObject,它在给定 8 个受约束的随机值的情况下沿着 Cubic CatMull-Rom 样条线移动。我已经实现了一个函数,ComputePointOnCatmullRomCurve,它返回三次 Catmull-Rom 曲线上的一个点(给定一个从 0 到 1 的标量 'u' 和表示用于插值的 4 个点的段数)。

我在实现更新功能以使游戏对象顺利移动时遇到问题。我目前每次更新都调用 ComputePointOnCatmullRomCurve 并且每次都在增加 segment_number。然后将 GameObjects 位置设置为函数的结果。

但是,这会导致游戏对象移动得非常快。我相信我的更新函数不正确,但我不确定如何相对于插值函数输出的点移动游戏对象。

如果有人能够向我解释我做错了什么,或者提供示例或示例链接,那将非常有帮助!

计算曲线上的点的函数:

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;

谢谢!

马特

【问题讨论】:

既然您说它移动得太快,那么将time += DT; 更改为time += DT * Time.deltaTime; 是否可以解决您的时间问题?见docs.unity3d.com/ScriptReference/Time-deltaTime.html 您是否在每次更新调用中增加实体所在的段? @Chris 添加 Time.delatTime 不能解决问题。时间变量可以设置为常数,比如 0.5 并且仍然会发生快速运动。 @MattCoubrough 是的,段的参考点每次更新都会增加,用于基于该初始点进行插值的其他三个点在 ComputePointOnCatmullRomCurve 内初始化 【参考方案1】:

您在更新功能中出现 2 个错误。

第一个错误:

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

提示:

对于由多个面片定义的样条曲线,我通常在[0,n] 范围内表示时间(u),其中n 是定义曲线的段数。这样您就可以检索当前补丁的索引 (segmentNumber),只需从参数中提取整数部分。比如:

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

第二个错误

我不知道您的 DT 变量是什么,但除非您在其他地方按帧增量时间对其进行缩放,否则您必须这样做。基本上你可以这样增加时间:

time += Time.deltaTime * speedAlongCurve;

希望对你有帮助。

【讨论】:

好的,时间的变化将允许游戏对象沿着曲线移动。每次更新更改 GameObject 的索引将迫使它从一个位置跳到另一个位置。所以问题是在适当的时候增加segmentNumber,对吗?很抱歉,但我很难理解您在包含的第一个 sn-p 代码中要实现的目标。 如上述评论中所述,我很难理解您包含的代码的 sn-p 功能。只有当(时间== 1)时,我才能通过增加段号来获得功能实现。那时我还将时间重置为0。但是,我有一种感觉,这只是巧合,并且有一种更合适的方式来循环浏览片段。 @Matt Koczwara:没关系,第一个 sn-p 只是一个提示,当时间 == 1 时,它在功能上应该等同于增加段索引。所以,如果你这样做并且工作得很好。第一个 sn-p 只是做同样的事情:例如,假设您在时间 0.5 处于第二个 seg 中,您的方法 -> t=0.5 seg=1 ,sn-p tTot = 1.5 seg =floortoint(1.5 ) = 1,t = 1.5 - 1 = 0.5。都是一样的。

以上是关于如何更新 Unity GameObject 以沿样条曲线移动?的主要内容,如果未能解决你的问题,请参考以下文章

unity3d 如何把gameobject上的脚本删除

Unity如何生成具有旋转速度的GameObject

用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 在脚本中使用MonoBehaviour

unity3d 如何把gameobject上的脚本删除

unity3d 中如何去除掉一个GameObject的MeshRenderer

Unity 如何使 GameObject Speed 获得速度