在Stack Overflow社区的帮助下,我编写了一个非常基本但有趣的物理模拟器。
你点击并拖动鼠标来发射一个球。它会弹来弹去,最终停在“地板”上。
我想添加的下一个重要功能是球与球的碰撞。球的运动被分解成x和y速度向量。有重力(每一步y向量都有小幅度的减小),有摩擦力(每一次与墙碰撞两个向量都有小幅度的减小)。这些球以一种令人惊讶的真实方式移动。
我想我的问题有两部分:
检测球与球碰撞的最佳方法是什么?
我只是有一个O(n²)循环,遍历每个球,并检查每个球的半径是否重叠?
我用什么方程来处理球与球的碰撞?101年物理
它如何影响两个球的速度x/y向量?这两个球最终的方向是什么?我怎么把它应用到每个球上呢?
处理“墙壁”的碰撞检测和由此产生的矢量变化很容易,但我发现球-球碰撞更复杂。对于墙,我只需要取适当的x或y向量的负数,它就会朝着正确的方向移动。我可不这么认为。
一些快速的澄清:为了简单起见,我现在对完美弹性碰撞没有问题,而且我所有的球现在都有相同的质量,但未来我可能会改变这一点。
编辑:我发现有用的资源
带矢量的二维球物理:不含三角函数的二维碰撞。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碰撞。此外,它跳过检查与自身的碰撞)。
然后,在我的球类中,我有我的collision()和resolveccollision()方法:
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));
}
源代码:完整的源球到球对撞机。
如果有人对如何改进这个基本物理模拟器有一些建议,请告诉我!我还需要补充的一件事是角动量,这样球就会滚动得更真实。还有其他建议吗?请留下评论!
经过一些试验和错误,我使用了本文的2D碰撞方法:https://www.vobarian.com/collisions/2dcollisions2.pdf
(那个OP链接到)
我在一个使用p5js的JavaScript程序中应用了这一点,它工作得很完美。我之前曾尝试使用三角方程,虽然它们确实适用于特定的碰撞,但我无法找到一个适用于每一次碰撞的方程,无论它发生的角度是多少。
本文档中解释的方法没有使用任何三角函数,它只是简单的向量操作,我将此推荐给任何试图实现球与球碰撞的人,以我的经验,三角函数很难推广。我请我大学里的一位物理学家教我怎么做,他告诉我不要用三角函数来麻烦,并向我展示了一个类似于文档中链接的方法。
注:我的质量都是相等的,但这可以推广到不同的质量使用在文件中提出的方程。
下面是我计算碰撞后速度矢量的代码:
//you just need a ball object with a speed and position vector.
class TBall {
constructor(x, y, vx, vy) {
this.r = [x, y];
this.v = [0, 0];
}
}
//throw two balls into this function and it'll update their speed vectors
//if they collide, you need to call this in your main loop for every pair of
//balls.
function collision(ball1, ball2) {
n = [ (ball1.r)[0] - (ball2.r)[0], (ball1.r)[1] - (ball2.r)[1] ];
un = [n[0] / vecNorm(n), n[1] / vecNorm(n) ] ;
ut = [ -un[1], un[0] ];
v1n = dotProd(un, (ball1.v));
v1t = dotProd(ut, (ball1.v) );
v2n = dotProd(un, (ball2.v) );
v2t = dotProd(ut, (ball2.v) );
v1t_p = v1t; v2t_p = v2t;
v1n_p = v2n; v2n_p = v1n;
v1n_pvec = [v1n_p * un[0], v1n_p * un[1] ];
v1t_pvec = [v1t_p * ut[0], v1t_p * ut[1] ];
v2n_pvec = [v2n_p * un[0], v2n_p * un[1] ];
v2t_pvec = [v2t_p * ut[0], v2t_p * ut[1] ];
ball1.v = vecSum(v1n_pvec, v1t_pvec); ball2.v = vecSum(v2n_pvec, v2t_pvec);
}
经过一些试验和错误,我使用了本文的2D碰撞方法:https://www.vobarian.com/collisions/2dcollisions2.pdf
(那个OP链接到)
我在一个使用p5js的JavaScript程序中应用了这一点,它工作得很完美。我之前曾尝试使用三角方程,虽然它们确实适用于特定的碰撞,但我无法找到一个适用于每一次碰撞的方程,无论它发生的角度是多少。
本文档中解释的方法没有使用任何三角函数,它只是简单的向量操作,我将此推荐给任何试图实现球与球碰撞的人,以我的经验,三角函数很难推广。我请我大学里的一位物理学家教我怎么做,他告诉我不要用三角函数来麻烦,并向我展示了一个类似于文档中链接的方法。
注:我的质量都是相等的,但这可以推广到不同的质量使用在文件中提出的方程。
下面是我计算碰撞后速度矢量的代码:
//you just need a ball object with a speed and position vector.
class TBall {
constructor(x, y, vx, vy) {
this.r = [x, y];
this.v = [0, 0];
}
}
//throw two balls into this function and it'll update their speed vectors
//if they collide, you need to call this in your main loop for every pair of
//balls.
function collision(ball1, ball2) {
n = [ (ball1.r)[0] - (ball2.r)[0], (ball1.r)[1] - (ball2.r)[1] ];
un = [n[0] / vecNorm(n), n[1] / vecNorm(n) ] ;
ut = [ -un[1], un[0] ];
v1n = dotProd(un, (ball1.v));
v1t = dotProd(ut, (ball1.v) );
v2n = dotProd(un, (ball2.v) );
v2t = dotProd(ut, (ball2.v) );
v1t_p = v1t; v2t_p = v2t;
v1n_p = v2n; v2n_p = v1n;
v1n_pvec = [v1n_p * un[0], v1n_p * un[1] ];
v1t_pvec = [v1t_p * ut[0], v1t_p * ut[1] ];
v2n_pvec = [v2n_p * un[0], v2n_p * un[1] ];
v2t_pvec = [v2t_p * ut[0], v2t_p * ut[1] ];
ball1.v = vecSum(v1n_pvec, v1t_pvec); ball2.v = vecSum(v2n_pvec, v2t_pvec);
}
I implemented this code in JavaScript using the HTML Canvas element, and it produced wonderful simulations at 60 frames per second. I started the simulation off with a collection of a dozen balls at random positions and velocities. I found that at higher velocities, a glancing collision between a small ball and a much larger one caused the small ball to appear to STICK to the edge of the larger ball, and moved up to around 90 degrees around the larger ball before separating. (I wonder if anyone else observed this behavior.)
一些计算记录显示,在这些情况下,最小平移距离不足以防止相同的球在下一个时间步中发生碰撞。我做了一些实验,发现我可以通过根据相对速度放大MTD来解决这个问题:
dot_velocity = ball_1.velocity.dot(ball_2.velocity);
mtd_factor = 1. + 0.5 * Math.abs(dot_velocity * Math.sin(collision_angle));
mtd.multplyScalar(mtd_factor);
我验证了在修正之前和之后,每次碰撞的总动能都是守恒的。mtd_factor中的0.5值大约是在碰撞后总是导致球分离的最小值。
虽然这个修复在系统的精确物理中引入了少量的错误,但代价是现在可以在浏览器中模拟非常快的球,而不减少时间步长。