二次曲线的速度

时间:2013-02-09 19:49:57

标签: c++ bezier

我正在写一个2D游戏,我在相机空间里有鸟。我想让它们飞起来。所以,我生成3~随机点。第一个是左上方,第二个是中下方,第三个是右上方。

结果我有180度旋转三角形。

为了让鸟儿通过曲线的路径,我有一个t参数,每个帧(渲染循环)增加一些增量。

问题在于,在不同的曲线中,鸟类的速度不同。如果三角形是“宽”( 1 ),它们会更慢,如果它被Y坐标( 2 )拉伸,速度会非常快。

enter image description here

但我想在不同的曲线上使速度相等。从逻辑上讲,我必须更改delta,每条曲线附加每条曲线。


我试图像这样解决它:

通过将2个向量的长度相加来找出曲线的长度:P1P2P2P3

比我定义了每帧1个虚拟仪表的速度。一个小伪代码:

float pixelsInMeter = 92.f; // One virtual meter equals to this number of pixels
float length = len(P1P2) + len(P2P3)
float speed  = 0.0003f; // m/frame

// (length * speed) / etalon_length
float speedForTheCurve = toPixels( (toMeters(length) * speed) / 1.f);

// ...
// Each frame code:
t += speedForTheCurve;
Vector2 newPos = BezierQuadratic(t, P1, P2, P3);

但是无论如何鸟类的速度都不同。怎么了?或者也许有更好的方法。

1 个答案:

答案 0 :(得分:2)

您正在使用的Bezier函数是一个边界为[0 ... 1]的参数化函数。你正在踩着步长,这就是你疯狂速度的原因。一般来说,距离 d 是等式中的因变量,它告诉我它们的速度将根据曲线的长度而不同。

由于速度是你的因变量,我们将通过计算步长来矢量化你的函数。

查看这个伪代码:

P1 = (x1, y1)

P2 = (x2, y2)

P3 = (x3, y3)

int vec[100][2]

int getPoint(int p1, int p2, float stepSize) {
   return p1 + (p2 - p1)*stepSize;
}

for (float i = 0.0; i < 1.0; i += 0.01 ) {
   int newX = getPoint(getPoint(x1, x2, i), getPoint(x2, x3, i), i);
   int newY = getPoint(getPoint(y1, y2, i), getPoint(y2, y3, i), i);
   vec[iter++][0] = newX;
   vec[iter][1] = newY;
}

您可以通过执行第一个差异来获取增量值,但我认为这不是必要的。只要您根据步骤迭代移动所有鸟类适当的距离,它们将移动不同的距离,但它们将以相同的方式开始和结束它们的轨迹。

从等式中,我们可以计算像素增量步长:

int pixelsToMove = toMeter(sqrt((x2 - x1)^2 + (y2 - y1)^2))/pixelsInMeter;

这将为您提供适当数量的像素来移动鸟。这样他们都会移动不同的步长,但他们的速度会有所不同。这有意义吗?


或者,尝试这样的事情(更难):

  1. 获取您选择的三个点的实际二次函数。
  2. 在两个xy直角坐标
  3. 之间积分二次方程式
  4. 将计算出的长度转换为像素或您正在使用的任何内容
  5. 获得因变速,以便所有曲线同时完成。
  6. 让我们从二次方开始:

    y = Ax^2 + Bx + C其中A != 0,因为你有三个点,你需要三个方程式。使用代数,你可以解决因素:

    A = (y3 - y2)/((x3 - x2)(x3 - x1)) - (y1 - y2)/((x1 - x2)(x3 - x1))

    B = (y1 - y2 + A(x2^2 - x1^2))/(x1 - x2)

    C = y1 - Ax1^2 - Bx1

    然后,您可以使用上面的公式来获得闭合形式的弧长。检查这个网站,wolfram会为你整合它,你只需要输入它:

    Closed form solution for quadradic integration

    现在您已经计算了弧长,将actualArcLength转换为您正在使用的速度或任何单位:

    float speedForTheCurve = toPixels( (toMeters(actualArcLength) * speed) / 1.f);