球碰撞

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

替代文字

你点击并拖动鼠标来启动一个球。 它会反弹并最终停止在“地板”上。

我想添加的下一个重要特征是球碰撞。 球的运动被分解为斧和速度矢量。 我有重力(每个步骤y矢量的小幅减少),我有摩擦(每次与墙壁碰撞时矢量都减小了)。 这些球以一种令人惊讶的现实方式诚实地移动。

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

  • 检测球对球碰撞的最佳方法是什么?
    我是否有一个O(n ^ 2)循环遍历每个球并检查其他所有球以查看它是否与半径重叠?
  • 我用什么方程式处理球碰撞? 物理101
    它如何影响两个球速度x / y向量? 这两个球头的方向是什么? 我如何将这个应用于每个球?
  • 替代文字

    处理“墙壁”的碰撞检测和由此产生的矢量变化很容易,但是我发现球球碰撞会造成更多的复杂性。 对于墙壁,我只需将相应的x或y矢量的负数取下,然后按照正确的方向走。 用球我不认为这是这样。

    一些快速说明:为了简单起见,我现在可以完美弹性碰撞,现在我所有的球也有相同的质量,但我可能会在未来改变这种情况。


    编辑:我发现有用的资源

    2d球物理学与向量:二维碰撞没有Trigonometry.pdf
    2d球碰撞检测示例:添加碰撞检测


    成功!

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

    相关代码:

    碰撞检测:

    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.multiply(i);
    
        // change in momentum
        this.velocity = this.velocity.add(impulse.multiply(im1));
        ball.velocity = ball.velocity.subtract(impulse.multiply(im2));
    
    }
    

    源代码:球对撞机的完整源代码。

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


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

    维基百科对整个过程进行了很好的总结。 对于任何质量的球,新的速度可以用公式计算(其中v1和v2是碰撞后的速度,而u1,u2来自之前):

    如果球具有相同的质量,那么只需切换速度。 以下是我写的一些代码,它可以做类似的事情:

    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是对的,你应该考虑将区域划​​分成不同的部分,然后在每个部分进行碰撞检测。 请记住,球可以与一个部分的边界上的其他球碰撞,所以这可能会让你的代码更加复杂。 效率可能不会影响,直到你有几百个球。 对于奖励积分,您可以在不同核心上运行每个部分,或者分割每个部分内的冲突处理。


    好吧,几年前我就像你在这里介绍的那样制作了这个程序。
    有一个隐藏的问题(或很多,取决于观点):

  • 如果球的速度太高,您可能会错过碰撞。
  • 而且,几乎在100%的情况下,你的新速度将是错误的。 好吧,不是速度,而是位置。 您必须在正确的位置准确计算新的速度。 否则,你只需要在一些小的“错误”量上移动球,这可以从前面的离散步骤中获得。

    解决方案是显而易见的:你必须拆分时间步骤,首先你转移到正确的位置,然后碰撞,然后转移到剩下的时间。


    你应该使用空间分区来解决这个问题。

    阅读二进制空间分区和四叉树

    链接地址: http://www.djcxy.com/p/65395.html

    上一篇: Ball to Ball Collision

    下一篇: ormlite store unknown foreign object in DatabaseField