球对球碰撞 - 检测和处理 [英] Ball to Ball Collision - Detection and Handling

查看:174
本文介绍了球对球碰撞 - 检测和处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


$ b



单击并拖动鼠标启动球。它会弹跳并最终停在地板上。

我想增加的下一个重要特征是球碰撞。球的运动被分解为x和y速度矢量。我有重力(每个步骤y矢量的小幅减少),我有摩擦(每次与墙壁碰撞时矢量都减小了)。



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


  1. 检测球碰撞的最佳方法是什么?

    我是否有一个O(n ^ 2)循环遍历每个球并且检查其他球是否与半径重叠?

  2. 我用什么方程式来处理球碰撞?物理学101

    它如何影响两个球速度x / y向量?这两个球头的方向是什么?我应该如何将它应用于每个球?



处理墙壁的碰撞检测和导致的矢量变化很容易,但我发现球球碰撞会造成更多的并发症。对于墙壁,我只需将相应的x或y矢量的负数取下,然后按照正确的方向走。有球我不认为是这样。



一些快速澄清:为简单起见,我现在可以完美弹性的碰撞,所有我的球也有






编辑:我发现有用的资源

p>

2d与矢量的球物理学:二维碰撞Without Trigonometry.pdf

2d球碰撞检测示例:添加碰撞检测






成功!



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

相关代码:

<碰撞检测:

$ p $ for(int i = 0; i {
表示(int j = i + 1; j {
if(balls [i] .colliding(balls [j]))
{
balls [i] .resolveCollision(balls [j]);





$ b $ p $这将检查每跳一球但跳过多余的检查(如果你必须检查球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)
{
//获取mtd
Vector2d delta =(position.subtract(ball.position) );
float d = delta.getLength();
//在相交
之后将球推开的最小平移距离Vector2d mtd = delta.multiply(((getRadius()+ ball.getRadius()) - d)/ d);


//解析交点 -
//反质量数
float im1 = 1 / getMass();
float im2 = 1 / ball.getMass();

//根据它们的质量将它们推开 -
position = position.add(mtd.multiply(im1 /(im1 + im2)));
ball.position = ball.position.subtract(mtd.multiply(im2 /(im1 + im2)));

//撞击速度
Vector2d v =(this.velocity.subtract(ball.velocity));
float vn = v.dot(mtd.normalize());

//球体已经相交但彼此离开已经
if(vn> 0.0f)return;

//碰撞冲动
float i =( - (1.0f + Constants.restitution)* vn)/(im1 + im2);
Vector2d impulse = mtd.multiply(i);

//动量变化
this.velocity = this.velocity.add(impulse.multiply(im1));
ball.velocity = ball.velocity.subtract(impulse.multiply(im2));


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



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

解决方案

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

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


//upload.wikimedia.org/math/e/0/f/e0fa187fc3065bad45710620be5f7687.pngalt =v_ {2} = \frac {u_ {2}(m_ {2} -m_ {1})+ 2m_ {如果球具有相同的质量,那么只需切换速度即可。这里有一些我写的代码可以做类似的事情:

  void Simulation :: collide(Storage :: Iterator a,Storage :: Iterator b)
{
//如果(a == b)
return,检查是否存在碰撞
;

向量碰撞= a.position() - b.position();
double distance = collision.length();
if(distance == 0.0){// hack避免div被零
collision = Vector(1.0,0.0);
距离= 1.0;
}
if(distance> 1.0)
return;

//获取与碰撞平行的速度矢量的分量。
//鱼的垂直分量保持不变
collision = collision / distance;
double aci = a.velocity()。dot(collision);
double bci = b.velocity()。dot(collision);

//使用一维弹性碰撞方程来求解新的速度。
//当群众相同时,结果很简单。
double acf = bci;
double bcf = aci;

//将碰撞速度分量替换为新的碰撞速度分量
a.velocity()+ =(acf - aci)* collision;
b.velocity()+ =(bcf - bci)* collision;

$ / code>

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


With the help of the Stack Overflow community I've written a pretty basic-but fun physics simulator.

You click and drag the mouse to launch a ball. It will bounce around and eventually stop on the "floor".

My next big feature I want to add in is ball to ball collision. The ball's movement is broken up into a x and y speed vector. I have gravity (small reduction of the y vector each step), I have friction (small reduction of both vectors each collision with a wall). The balls honestly move around in a surprisingly realistic way.

I guess my question has two parts:

  1. What is the best method to detect ball to ball collision?
    Do I just have an O(n^2) loop that iterates over each ball and checks every other ball to see if it's radius overlaps?
  2. What equations do I use to handle the ball to ball collisions? Physics 101
    How does it effect the two balls speed x/y vectors? What is the resulting direction the two balls head off in? How do I apply this to each ball?

Handling the collision detection of the "walls" and the resulting vector changes were easy but I see more complications with ball-ball collisions. With walls I simply had to take the negative of the appropriate x or y vector and off it would go in the correct direction. With balls I don't think it is that way.

Some quick clarifications: for simplicity I'm ok with a perfectly elastic collision for now, also all my balls have the same mass right now, but I might change that in the future.


Edit: Resources I have found useful

2d Ball physics with vectors: 2-Dimensional Collisions Without Trigonometry.pdf
2d Ball collision detection example: Adding Collision Detection


Success!

I have the ball collision detection and response working great!

Relevant code:

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

This will check for collisions between every ball but skip redundant checks (if you have to check if ball 1 collides with ball 2 then you don't need to check if ball 2 collides with ball 1. Also, it skips checking for collisions with itself).

Then, in my ball class I have my colliding() and resolveCollision() methods:

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

}

Source Code: Complete source for ball to ball collider.

If anyone has some suggestions for how to improve this basic physics simulator let me know! One thing I have yet to add is angular momentum so the balls will roll more realistically. Any other suggestions? Leave a comment!

解决方案

To detect whether two balls collide, just check whether the distance between their centers is less than two times the radius. To do a perfectly elastic collision between the balls, you only need to worry about the component of the velocity that is in the direction of the collision. The other component (tangent to the collision) will stay the same for both balls. You can get the collision components by creating a unit vector pointing in the direction from one ball to the other, then taking the dot product with the velocity vectors of the balls. You can then plug these components into a 1D perfectly elastic collision equation.

Wikipedia has a pretty good summary of the whole process. For balls of any mass, the new velocities can be calculated using the equations (where v1 and v2 are the velocities after the collision, and u1, u2 are from before):

If the balls have the same mass then the velocities are simply switched. Here's some code I wrote which does something similar:

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

As for efficiency, Ryan Fox is right, you should consider dividing up the region into sections, then doing collision detection within each section. Keep in mind that balls can collide with other balls on the boundaries of a section, so this may make your code much more complicated. Efficiency probably won't matter until you have several hundred balls though. For bonus points, you can run each section on a different core, or split up the processing of collisions within each section.

这篇关于球对球碰撞 - 检测和处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆