0

嗨,我尝试实现一个简单的球体碰撞检测算法,如下所述:http ://wp.freya.no/3d-math-and-physics/simple-sphere-sphere-collision-detection-and-collision-response/

所以我实现了检查是否发生碰撞的方法:

private boolean advancedSphereSphere(Sphere a, Sphere b) {

Vector2D s = a.getPos().sub(b.getPos());
Vector2D v = a.getVelocity().sub(b.getVelocity());

float r = a.getRadius() + b.getRadius();

double c1 = s.dot(s) - r*r;

if(c1 < 0.0) {
    timeToCollision = .0f;
    return true;
}

double a1 = v.dot(v);
if(a1 < 0.00001f) {
    return false;
}

double b1 = v.dot(s);
if(b1 >= 0.0) {
    return false;
}

double d1 = b1*b1 - a1*c1;
if(d1 < 0.0) {
    return false;
}

timeToCollision = (float) (-b1 - Math.sqrt(d1) / a1);

return true;
}

然后我有一个 onDraw 方法循环绘制所有必要的元素,像这样:

protected void onDraw(Canvas canvas) {
    flowPhysics(false);
    for(Sphere s : mSpheres) {
        s.draw(canvas);
    }

    invalidate();
}

问题出现在 flowPhysics(boolean) 方法中,在这一行:

for(int i=0; i < mSpheres.size(); ++i) {
            for(int j=i+1; j < mSpheres.size(); ++j) {

                Sphere a = mSpheres.get(i);
                Sphere b = mSpheres.get(j);

                if(advancedSphereSphere(a, b) || step) {
                    if(timeToCollision < dt && !step) {
                        flowPhysics(true);
                    }

                    if(step) {
                        sphereCollisionResponse(a, b);
                    }
                }

            }
        }

当调用 advancedSphereSphere() 时,第一个 Sphere 消失了,我检查并注意到问题出在第一行的那个方法中:

Vector2D s = a.getPos().sub(b.getPos());

如果我在这里放了其他东西并且不从向量中减去 b,它会绘制球(但不会发生碰撞)。上面的 Java 代码主要是从这里移植的代码:http ://wp.freya.no/websvn/filedetails.php?repname=Public&path=%2Fopengl%2Fcollisiondetect%2Fcollisiondetect.cpp

你能给我一个关于问题所在的想法吗?

谢谢

更新

private void sphereCollisionResponse(Sphere a, Sphere b)
    {
        double m1, m2, x1, x2;
        Vector2D v1, v2, v1x, v2x, v1y, v2y;
        Vector2D x = new Vector2D(a.getPos().sub(b.getPos()));

        x.normalize();
        v1 = new Vector2D(a.getVelocity());
        x1 = x.dot(v1);
        v1x = new Vector2D(x.multiply(x1));
        v1y = new Vector2D(v1.sub(v1x));
        m1 = a.getMass();

        x = new Vector2D(x.multiply(-1));
        v2 = new Vector2D(b.getVelocity());
        x2 = x.dot(v2);
        v2x = new Vector2D(x.multiply(x2));
        v2y = new Vector2D(v2.sub(v2x));
        m2 = b.getMass();

        Vector2D nn = new Vector2D(v1x.multiply(m1-m2));
        Vector2D mm = new Vector2D(nn.divide(m1+m2));
        Vector2D tt = new Vector2D(v2x.multiply(2*m2));
        Vector2D rr = new Vector2D(tt.divide(m1+m2));
        Vector2D gg = new Vector2D(mm.add(rr));
        Vector2D ss = new Vector2D(gg.add(v1y));

        Vector2D nva = ss; 
        a.setVelocity(nva);

        Vector2D nvb = new Vector2D(v1x.multiply(2*m1).divide(m1+m2).add(v2x.multiply(m2-m2).divide(m1+m2).add(v2y)));
        b.setVelocity(nvb);     
} 
4

1 回答 1

0

如果问题真的出在这一行

Vector2D s = a.getPos().sub(b.getPos());

那么我怀疑问题可能出在你的Vector2D课堂上。您应该检查该sub方法是否只返回一个新的Vector2D并且不修改原始向量的坐标a。尽可能保持你的对象不可变使它们使用起来更安全。

如果做不到这一点,答案是单元测试。从长远来看,学习使用JUnitTestNG会有所收获,但即使是一些临时打印语句现在也可能对您有所帮助。无论球体在屏幕上的显示方式如何,您都应该能够检查数学是否正确,并且始终如一地执行此操作可能会引导您进行更清晰的设计。

仅仅通过测试每个球体在碰撞后的位置没有变化,并且在一些简单的情况下速度符合预期,您很可能会发现代码中的错误。例如,如果两个速度事先都为零,那么它们之后应该为零。如果碰撞是弹性的,并且一个球体 (A) 与另一个静止球体 (B) 正面碰撞,则球体 A 之后应该是静止的,而球体 B 应该具有 A 开始时的速度。

我没有在http://pastie.org/2499691上检查你的代码中的数学,但是当我设置一些简单的类来填充依赖关系并给它一些合理的起始条件时,它不能保存动力,所以我可以相信对于某些值,球最终可能会以光速飞出屏幕。

一旦你掌握了物理知识,你就可以解决任何剩余的问题,同时确保球体的显示安全,因为知道问题确实存在。

于 2011-09-07T19:20:30.527 回答