graphics - 球与球的碰撞 - 检测和处理

标签 graphics language-agnostic collision-detection physics

在 Stack Overflow 社区的帮助下,我编写了一个非常基本但有趣的物理模拟器。

alt text

您单击并拖动鼠标来发射球。它会弹来弹去,最终停在“地板”上。

我想添加的下一个重要功能是球与球的碰撞。球的运动被分解为 x 和 y 速度矢量。我有重力(每一步都会小幅减少 y 向量),我有摩擦力(每次与墙壁碰撞时两个向量都会小幅减少)。球以一种令人惊讶的真实方式诚实地移动。

我想我的问题有两个部分:

  1. 检测球与球碰撞的最佳方法是什么?
    我是否只有一个 O(n^2) 循环来迭代每个球并检查其他每个球以查看其半径是否重叠?
  2. 我使用什么方程来处理球与球的碰撞?物理101
    它如何影响两个球速度 x/y 向量?两个球的最终运动方向是什么?我如何将其应用到每个球?

alt text

处理“墙”的碰撞检测和由此产生的矢量变化很容易,但我发现球与球的碰撞更加复杂。对于墙壁,我只需取适当的 x 或 y 向量的负值,然后它就会朝正确的方向移动。对于球,我不认为是这样。

一些快速说明:为了简单起见,我现在可以接受完美的弹性碰撞,而且我所有的球现在都具有相同的质量,但我将来可能会改变这一点。

<小时/>

编辑:我发现有用的资源

带有向量的二维球物理:2-Dimensional Collisions Without Trigonometry.pdf
2d 球碰撞检测示例:Adding Collision Detection

<小时/>

成功!

我的球碰撞检测和响应工作得很好!

相关代码:

碰撞检测:

for (int i = 0; i < ballCount; i++)  
{  
    for (int j = i + 1; j < ballCount; j++)  
    {  
        if (balls[i].colliding(balls[j]))  
        {
            balls[i].resolveCollision(balls[j]);
        }
    }
}

这将检查每个球之间的碰撞,但跳过多余的检查(如果您必须检查球 1 是否与球 2 碰撞,那么您不需要检查球 2 是否与球 1 碰撞。此外,它还会跳过检查与自身碰撞)。

然后,在我的球类中,我有 colliding() 和resolveCollision() 方法:

public boolean colliding(Ball ball)
{
    float xd = position.getX() - ball.position.getX();
    float yd = position.getY() - ball.position.getY();

    float sumRadius = getRadius() + ball.getRadius();
    float sqrRadius = sumRadius * sumRadius;

    float distSqr = (xd * xd) + (yd * yd);

    if (distSqr <= sqrRadius)
    {
        return true;
    }

    return false;
}

public void resolveCollision(Ball ball)
{
    // get the mtd
    Vector2d delta = (position.subtract(ball.position));
    float d = delta.getLength();
    // minimum translation distance to push balls apart after intersecting
    Vector2d mtd = delta.multiply(((getRadius() + ball.getRadius())-d)/d); 


    // resolve intersection --
    // inverse mass quantities
    float im1 = 1 / getMass(); 
    float im2 = 1 / ball.getMass();

    // push-pull them apart based off their mass
    position = position.add(mtd.multiply(im1 / (im1 + im2)));
    ball.position = ball.position.subtract(mtd.multiply(im2 / (im1 + im2)));

    // impact speed
    Vector2d v = (this.velocity.subtract(ball.velocity));
    float vn = v.dot(mtd.normalize());

    // sphere intersecting but moving away from each other already
    if (vn > 0.0f) return;

    // collision impulse
    float i = (-(1.0f + Constants.restitution) * vn) / (im1 + im2);
    Vector2d impulse = mtd.normalize().multiply(i);

    // change in momentum
    this.velocity = this.velocity.add(impulse.multiply(im1));
    ball.velocity = ball.velocity.subtract(impulse.multiply(im2));

}

源代码:Complete source for ball to ball collider.

如果有人对如何改进这个基本物理模拟器有一些建议,请告诉我!我还没有添加的一件事是角动量,这样球会更真实地滚动。还有其他建议吗?发表评论!

最佳答案

要检测两个球是否发生碰撞,只需检查两个球中心之间的距离是否小于半径的两倍即可。要在球之间进行完美的弹性碰撞,您只需担心碰撞方向上的速度分量。两个球的另一个分量(与碰撞相切)将保持相同。您可以通过创建一个指向从一个球到另一个球的方向的单位向量,然后与球的速度向量进行点积来获得碰撞分量。然后,您可以将这些组件代入一维完美弹性碰撞方程。

维基百科有一个相当不错的summary of the whole process 。对于任何质量的球,可以使用以下方程计算新的速度(其中 v1 和 v2 是碰撞后的速度,u1、u2 是碰撞前的速度):

v_{1} = \frac{u_{1}(m_{1}-m_{2})+2m_{2}u_{2}}{m_{1}+m_{2}}

v_{2} = \frac{u_{2}(m_{2}-m_{1})+2m_{1}u_{1}}{m_{1}+m_{2}}

如果球具有相同的质量,则只需交换速度即可。这是我编写的一些代码,它们执行类似的操作:

void Simulation::collide(Storage::Iterator a, Storage::Iterator b)
{
    // Check whether there actually was a collision
    if (a == b)
        return;

    Vector collision = a.position() - b.position();
    double distance = collision.length();
    if (distance == 0.0) {              // hack to avoid div by zero
        collision = Vector(1.0, 0.0);
        distance = 1.0;
    }
    if (distance > 1.0)
        return;

    // Get the components of the velocity vectors which are parallel to the collision.
    // The perpendicular component remains the same for both fish
    collision = collision / distance;
    double aci = a.velocity().dot(collision);
    double bci = b.velocity().dot(collision);

    // Solve for the new velocities using the 1-dimensional elastic collision equations.
    // Turns out it's really simple when the masses are the same.
    double acf = bci;
    double bcf = aci;

    // Replace the collision velocity components with the new ones
    a.velocity() += (acf - aci) * collision;
    b.velocity() += (bcf - bci) * collision;
}

至于效率,Ryan Fox 是对的,您应该考虑将区域划​​分为多个部分,然后在每个部分内进行碰撞检测。请记住,球可能会与部分边界上的其他球发生碰撞,因此这可能会使您的代码变得更加复杂。不过,除非你有几百个球,否则效率可能并不重要。为了获得奖励积分,您可以在不同的核心上运行每个部分,或者拆分每个部分内的碰撞处理。

关于graphics - 球与球的碰撞 - 检测和处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/345838/

相关文章:

JavaScript:碰撞检测

jQuery 生成 div 和碰撞检测

svg - 如何从此 SVG 中删除背景

java - Swing 游戏应该只有绘图组件吗?

math - 裁剪两个 2D 三角形

language-agnostic - 建议处理基于图 block 的系统上的自由运动的逻辑

python - 如何使实体在pygame中因颜色碰撞而受到伤害?

c# - Bitmap.FromFile(path) 和 new Bitmap(path) 的区别

data-binding - UI 数据绑定(bind) : alternatives and future

algorithm - 对字符串进行就地排序以查明是否存在非唯一字符