【问题标题】:Calculating Collision Times Between Two Circles - Physics计算两个圆之间的碰撞时间 - 物理学
【发布时间】:2017-09-20 11:57:08
【问题描述】:

我一直在寻找游戏/模拟解决方案以在时间流逝时寻找距离,但这不是我想要的。

我正在寻找一个 O(1) 公式来计算(0 或 1 或 2)时钟时间,其中两个圆之间的距离正好为 r1+r2。负时间是可能的。有可能两个圆圈没有碰撞,并且它们可能没有交叉路口(如两辆车在相反方向行驶太靠近路中间时相互“夹”),这弄乱了我所有的 mx+b解决方案。

从技术上讲,单点碰撞应该是可能的。

我大约有 100 行代码,我确信肯定有更好的方法,我什至不确定我的测试用例是否正确。我的初始设置是:

dist( x1+dx1*t, y1+dy1*t, x2+dx2*t, y2+dy2*t ) == r1+r2

假设任何时间 t 的距离都可以用毕达哥拉斯计算,我想知道到中心的距离恰好是半径之和的两个时间点。我求解了 a、b 和 c 并应用了二次公式,我相信如果我假设它们是幻像物体,这会给我碰撞的第一个时刻和最后一个碰撞时刻,我可以假设每时每刻,它们都在重叠。

我的工作前提是 2 个对象不可能在 t0 重叠,这意味着“相互卡住”的无限碰撞是不可能的。当斜率为 0 或无穷大时,我也会过滤掉并使用特殊处理,这是有效的。

我尝试计算距离,此时对象 1 位于交点,它与对象 2 的距离,同样当 o2 位于交点时,但这不起作用,因为当它们可能发生碰撞时不在他们的路口。

当斜率相等但幅度不同时,我遇到了问题。

已经有一个简单的物理/数学公式了吗?

编程语言无关紧要,伪代码会很棒,或者任何没有复杂符号的数学公式(我不是数学/物理学家)......但没有更高阶的(我假设 python 可能有已经有一个碰撞(p1,p2)方法)

【问题讨论】:

  • 物体是否像您的方程式中那样具有恒定的速度?如果是这样,您应该能够简单地求解 t。什么不工作?

标签: physics collision


【解决方案1】:

有一个简单的(-ish)解决方案。您已经提到使用二次公式,这是一个好的开始。

首先定义二次公式可能有用的问题,在这种情况下,随着时间的推移,到中心之间的距离。

  • 让我们将时间定义为t
  • 因为我们使用的是两个维度,我们可以将我们的维度称为x & y
  • 首先让我们定义两个圆心t = 0为a & b
  • 让我们将t = 0 和a & b 的速度分别定义为u & v。
  • 最后,假设a & b 的恒定加速度分别为o & p。
  • 任何一维位置(我们称之为i)相对于时间t 的方程如下:i(t) = 1 / 2 * a * t^2 + v * t + i0; 其中a 是恒定加速度,v 是初始速度,而i0 是沿维度i 的初始位置。
  • 我们知道任何时候两个二维点之间的距离t是((a.x(t) - b.x(t))^2 + (a.y(t) - b.y(t))^2)的平方根
  • 使用沿维度的位置公式,我们可以仅用 t 和我们之前定义的常数来替换距离方程中的所有内容。简而言之,我们将调用函数d(t);
  • 最后使用该等式,我们将知道t 的值,其中d(t) = a.radius + b.radius 是碰撞开始或结束的位置。
  • 用二次公式表示,我们将半径向左移动,得到d(t) - (a.radius + b.radius) = 0
  • 然后我们可以扩展和简化得到的等式,所以一切都以t 和给定的常数值表示。使用二次公式求解正值和负值。
  • 这也将处理错误,因为如果你得到两个永远不会碰撞的对象,你将得到一个未定义的或虚数。

您应该能够相当轻松地将其余部分转换为代码。我的时间不多了,我会尽可能写一个简单的解决方案。

【讨论】:

  • 很棒的解释!我发现将加速度从方程中分离出来并在碰撞检测后添加它很有用。根据我目前的需要,它已经足够接近了。因此,我需要为 t 求解 2 次多项式,而不是 4 次。并感谢您指出有办法解决它!
  • 这里是完整实现的链接:github.com/viljami/nemo-ccd
  • 还有一件事。我应该先完成单元测试。我认为公式中存在错误。当我将半径之和提高到二次方时,它就解决了。 d(t) = (r1 + r2)^2 那么结果是正确的。当我以solving t like this结束时。
【解决方案2】:

跟进@TinFoilPancakes 的回答并大量使用 WolframAlpha 来简化公式,我想出了以下伪代码,实际上是我评论过的 C# 代码:

Ball 类具有以下属性:

public double X;
public double Y;
public double Xvel;
public double Yvel;
public double Radius;

算法:

public double TimeToCollision(Ball other)
{
    double distance = (Radius + other.Radius) * (Radius + other.Radius);
    double a = (Xvel - other.Xvel) * (Xvel - other.Xvel) + (Yvel - other.Yvel) * (Yvel - other.Yvel);
    double b = 2 * ((X - other.X) * (Xvel - other.Xvel) + (Y - other.Y) * (Yvel - other.Yvel));
    double c = (X - other.X) * (X - other.X) + (Y - other.Y) * (Y - other.Y) - distance;
    double d = b * b - 4 * a * c;
    // Ignore glancing collisions that may not cause a response due to limited precision and lead to an infinite loop
    if (b > -1e-6 || d <= 0)
        return double.NaN;
    double e = Math.Sqrt(d);
    double t1 = (-b - e) / (2 * a);    // Collison time, +ve or -ve
    double t2 = (-b + e) / (2 * a);    // Exit time, +ve or -ve
    // b < 0 => Getting closer
    // If we are overlapping and moving closer, collide now
    if (t1 < 0 && t2 > 0 && b <= -1e-6)
        return 0;
    return t1;
}

该方法将返回 Balls 碰撞的时间,可以是 +ve、-ve 或 NaN,NaN 表示它们不会或没有碰撞。

进一步需要注意的是,我们可以检查判别式与

'b' 变量可用于此检查,因为幸运的是它类似于点积。如果 b >-1e-6 即。他们没有足够快地靠近我们返回NaN,即。他们不会碰撞。您可以调整此值以避免冻结,较小的值将允许更近距离的扫视碰撞,但当它们发生时会增加冻结的机会,例如当一堆圆圈紧紧地挤在一起时。同样,为了避免球相互穿过,如果它们已经重叠并靠近,我们会立即发出碰撞信号。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多