3

我要解决的问题是我似乎无法以恒定速度沿三次贝塞尔曲线移动 2D 点。

我按照本教程:http ://catlikecoding.com/unity/tutorials/curves-and-splines/最初实现了曲线,效果非常好。但是当试图以恒定的速度逼近该点时,它的方式,方式。

根据我目前所读到的内容,您应该遍历曲线,计算每一步的弧长和间隔距离。然后,将这些距离与目标距离(弧长 * 时间)进行比较,以找到最近的距离。有了足够高的分辨率,这应该几乎没有错误,并且对我的需要足够准确。

这是我到目前为止的代码:

积分计算:

public static Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) 
{
    t = Mathf.Clamp01(t);
    float oneMinusT = 1f - t;
    return
        oneMinusT * oneMinusT * oneMinusT * p0 +
            3f * oneMinusT * oneMinusT * t * p1 +
            3f * oneMinusT * t * t * p2 +
            t * t * t * p3;
}

在恒定时间计算点的可悲尝试:

private float GetApproximatedTime(float u)
{
    int resolution = 100;
    float ratio = 1.0f / resolution;
    float arcLength = 0.0f;
    Vector3 p0 = SelectedSpline.Evaluate(0.0f);
    List<MultiCurveUtility.ArcTimeLength> arcTimeLengthMap = new List<MultiCurveUtility.ArcTimeLength>();
    arcTimeLengthMap.Add(new MultiCurveUtility.ArcTimeLength(0.0f, 0.0f));

    for (int i = 1; i <= resolution; i++)
    {
        float t = ((float)i) * ratio;
        Vector3 p1 = SelectedSpline.Evaluate(t);
        arcLength += Vector3.Distance(p0, p1);
        arcTimeLengthMap.Add(new MultiCurveUtility.ArcTimeLength(t, arcLength));
        p0 = p1;
    }

    float target = u * arcLength;
    int low = 0;
    int high = 1;
    float min = 0.0f;
    float max = 0.0f;

    for (int i = 1; i < arcTimeLengthMap.Count; i++)
    {
        max = arcTimeLengthMap[i].ArcLength;
        if (target > min && target < max) 
        {
            high = i;
            low = i - 1;
            break; 
        }

        min = max;
    }

    float p = (target - min) / (max - min);
    float lowTime = arcTimeLengthMap[low].ArcTime;
    float highTime = arcTimeLengthMap[high].ArcTime;
    float lowHighDelta = highTime - lowTime;
    return arcTimeLengthMap[low].ArcTime + (lowHighDelta * p);
}

在上面的代码中,我传递了 0 和 1 之间的时间(u),以便返回一个时间,该时间可用于评估三次贝塞尔曲线上的一个点,该点表示 x 轴以恒定速率移动。

结果是这样的: 三次贝塞尔图像

红点表示仅使用贝塞尔公式评估原始时间返回的法线点。黄点代表经过近似时间后的“恒定”速度位置。在我开始将切线更改为相当夸张之前,它似乎非常准确。我也尝试过增加间隔,但这无济于事。

无论如何,任何帮助都会很棒。我还不是很擅长阅读公式(我确定问题出在哪里),所以请使用代码示例获得一些帮助会很棒。:>

谢谢!

4

2 回答 2

0

我没有看到该方法有任何明显的错误,因此将分辨率提高到 1000 或 10000 会有所帮助。

这不是您要求的,但是为了提高效率,以防这是某些高性能游戏的一部分,其中包含大量图形和严格的性能要求

a) 在一个步骤中将值存储在一个表中,然后在一个单独的步骤中访问它们,这样对于该曲线,您就不必每次重新计算 100(0(0)) 个点

b) 不要逐个遍历这些值,而是使用二分搜索或线性估计正确区间的下一个猜测

您还想用 C 而不是 Python 编写它,但显然这是关于 Python 的。

于 2015-11-15T03:27:46.030 回答
-4

好吧,我似乎自己找到了答案。

TLDR;对于二维曲线,不要使用弧长来计算目标距离。仅使用水平(x 轴)长度。

快速注意:如果您的曲线可以沿 x 轴向后移动,此解决方案可能对您不起作用。我的没有。

详细地说,目标距离(用于近似我应该在曲线上看的位置的值)是时间和弧长的乘积。弧长不准确,因为它考虑了 y 轴距离。我只关心水平移动,所以 y 距离是不必要的。

这是我更新的代码:

private float GetApproximatedTime(float u)
{
int resolution = 25 * SelectedSpline.CurveCount; // Factor in additional curves.
float ratio = 1.0f / resolution;
float arcLength = 0.0f;
Vector3 p0 = SelectedSpline.Evaluate(0.0f);
List<MultiCurveUtility.ArcTimeLength> arcTimeLengthMap = new List<MultiCurveUtility.ArcTimeLength>();
arcTimeLengthMap.Add(new MultiCurveUtility.ArcTimeLength(0.0f, 0.0f));

for (int i = 1; i <= resolution; i++)
{
    float t = ((float)i) * ratio;
    Vector3 p1 = SelectedSpline.Evaluate(t);
    arcLength += Mathf.Abs(p1.x - p0.x); // Only use the x-axis delta.
    arcTimeLengthMap.Add(new MultiCurveUtility.ArcTimeLength(t, arcLength));
    p0 = p1;
}

float target = u * arcLength;
int low = 0;
int high = 1;
float min = 0.0f;
float max = 0.0f;

for (int i = 1; i < arcTimeLengthMap.Count; i++)
{
    max = arcTimeLengthMap[i].ArcLength;
    if (target > min && target < max) 
    {
        high = i;
        low = i - 1;
        break; 
    }

    min = max;
}

float p = (target - min) / (max - min);
float lowTime = arcTimeLengthMap[low].ArcTime;
float highTime = arcTimeLengthMap[high].ArcTime;
float lowHighDelta = highTime - lowTime;
return arcTimeLengthMap[low].ArcTime + (lowHighDelta * p);
}

请注意,这里有两个重大变化:

arcLength += Mathf.Abs(p1.x - p0.x);

int resolution = 25 * SelectedSpline.CurveCount;

第二个更改是确保添加曲线时分辨率不会降低。否则,您可能会注意到返回时间的准确性有误。我发现每条曲线 25 的间隔非常准确且非常快。也就是说,在这段代码中有一些明确的优化,但是,如果你也想不通,它应该适合你。

这是结果的屏幕截图。黄点是我使用新时间评估的点。绿点代表我的高点和低点。

IMAGE - 结果图 - 三次贝塞尔曲线上的恒定时间

于 2015-11-15T18:52:29.617 回答