球碰撞
在Stack Overflow社区的帮助下,我编写了一个非常基本但很有趣的物理模拟器。
你点击并拖动鼠标来启动一个球。 它会反弹并最终停止在“地板”上。
我想添加的下一个重要特征是球碰撞。 球的运动被分解为斧和速度矢量。 我有重力(每个步骤y矢量的小幅减少),我有摩擦(每次与墙壁碰撞时矢量都减小了)。 这些球以一种令人惊讶的现实方式诚实地移动。
我想我的问题有两个部分:
我是否有一个O(n ^ 2)循环遍历每个球并检查其他所有球以查看它是否与半径重叠?
它如何影响两个球速度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