Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 三次Bezier曲线上的恒速_C#_Unity3d_Bezier_Curves_Cubic - Fatal编程技术网

C# 三次Bezier曲线上的恒速

C# 三次Bezier曲线上的恒速,c#,unity3d,bezier,curves,cubic,C#,Unity3d,Bezier,Curves,Cubic,我试图解决的问题是,我似乎不能以恒定的速度沿三次贝塞尔曲线移动2D点 我遵循本教程:最初实现曲线,效果非常好。但是当试图以恒定的速度来近似这个点时,它就偏离了 根据我到目前为止读到的内容,你应该在曲线上迭代,计算每一步的弧长和间隔距离。然后,将这些距离与目标距离(弧长*时间)进行比较,以找到最近的距离。有了足够高的分辨率,这应该有小的误差,并足够准确,以满足我的需要 以下是我目前掌握的代码: 点数计算: public static Vector3 GetPoint (Vector3 p0, Ve

我试图解决的问题是,我似乎不能以恒定的速度沿三次贝塞尔曲线移动2D点

我遵循本教程:最初实现曲线,效果非常好。但是当试图以恒定的速度来近似这个点时,它就偏离了

根据我到目前为止读到的内容,你应该在曲线上迭代,计算每一步的弧长和间隔距离。然后,将这些距离与目标距离(弧长*时间)进行比较,以找到最近的距离。有了足够高的分辨率,这应该有小的误差,并足够准确,以满足我的需要

以下是我目前掌握的代码:

点数计算:

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);
}
private float GetApproximatedTime(浮点u)
{
int分辨率=100;
浮动比率=1.0f/分辨率;
浮弧长度=0.0f;
向量3 p0=所选样条曲线。评估(0.0f);
List arcTimeLengthMap=新列表();
arcTimeLengthMap.Add(新的MultiCurveUtility.ArcTimeLength(0.0f,0.0f));
对于(int i=1;i最小值&目标值<最大值)
{
高=i;
低=i-1;
打破
}
最小值=最大值;
}
浮动p=(目标-最小值)/(最大-最小值);
浮点低时间=arcTimeLengthMap[低]。ArcTime;
浮点高时间=arcTimeLengthMap[高]。ArcTime;
浮点lowHighDelta=高时间-低时间;
返回arcTimeLengthMap[low].ArcTime+(lowHighDelta*p);
}
在上面的代码中,我输入了0到1之间的时间(u),以便返回一个时间,该时间可用于计算立方贝塞尔曲线上的一个点,该曲线表示x轴以恒定速率移动

结果是:

红点表示通过使用贝塞尔公式计算原始时间返回的法线点。黄点表示经过近似时间后的“恒定”速度位置。它看起来相当准确,直到我开始改变切线,使之相当夸张。我也尝试过增加间隔时间,但那没有任何帮助

不管怎样,任何帮助都会很好。我还不太擅长阅读公式(我确定问题来自何方),所以如果能使用代码示例获得一些帮助,那就太好了。:>


谢谢

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

这不是你所要求的,但是为了使它更高效,以防这是一些高性能游戏的一部分,其中有大量的图形和苛刻的性能要求

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

b) 使用二进制搜索或线性估计下一个正确区间的猜测,而不是单步遍历值


另外,您可能想用C语言而不是Python来编写它,但显然这是关于Python的。

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

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);
}

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

下面是结果的屏幕截图。黄点是我使用新时间计算的点。绿色的圆点代表我的高点和低点


谢谢。我将在优化时考虑这些问题,我将研究二进制搜索和线性估计。另外,我不是用Python写的,而是用c语言写的。。。没有意义?如果你有一个四分之一圆的曲线,具有严格的正x-行程,那么使用“水平长度”和其他与弧长无关的东西一样无用。你将在曲线的不同部分以完全不同的速度行驶。为了“看起来足够正确”,只需将曲线展平,在每个顶点上绑定
t
(基本上,构建一个LUT),然后对下一个移动点进行二进制搜索,完成。。。事实上我就是这么做的。LUT由水平长度和t组成。但是,是的,我的局部变量被命名为弧长这一事实确实令人困惑。它不再是弧长,因为它只包含x轴的长度。谢谢你的反馈!:)不,这也是事实,你声称水平长度是好的,这绝对不是真的。一条简单的单调曲线应该已经告诉你了:沿着曲线越往右走,相同的t区间值给出的x区间越来越小,而相同的x区间值给出的t区间越来越大。你声称单独使用水平长度就足够了,这显然是错误的。嗯,因为这对我来说是有效的,我只能假设你不明白我在说什么。当然,这是我的错。以后,我会尽量在解释上更清楚一些。如果你真的有兴趣指出我到底做错了什么,请随意使用我提供的代码来查看它的实际情况,然后,也许,根据所述代码向我提供详细的解释。如果没有,谢谢你抽出时间来
arcLength += Mathf.Abs(p1.x - p0.x);
int resolution = 25 * SelectedSpline.CurveCount;