libgdx: speed loss on object collision

时间:2018-03-25 18:46:26

标签: java libgdx box2d game-physics

I'm trying to build a sample that consists in some circles moving and colliding among themselves. As all the circles have the same attributes and restitution = 1 and friction = 0, I expect that they should bounce eternally always at the same speed. But, instead, some objects slow down:

enter image description here

World creation:

World.setVelocityThreshold(0);
world = new World(new Vector2(0, 0), true);

Boundaries:

EdgeShape edge = new EdgeShape();
FixtureDef wallFixtureDef = new FixtureDef();
wallFixtureDef.shape = edge;
wallFixtureDef.density = 0;
wallFixtureDef.friction = 0;
wallFixtureDef.restitution = 1;

BodyDef wallBodyDef = new BodyDef();
wallBodyDef.type = BodyDef.BodyType.StaticBody;

Body wallsBody = world.createBody(wallBodyDef);

// Bottom
edge.set(1, 1, WIDTH - 1, 1);
wallsBody.createFixture(wallFixtureDef);

// Top
edge.set(1, HEIGTH - 1, WIDTH - 1, HEIGTH - 1);
wallsBody.createFixture(wallFixtureDef);

// Left
edge.set(1, 1, 1, HEIGTH - 1);
wallsBody.createFixture(wallFixtureDef);

// Right
edge.set(WIDTH - 1, 1, WIDTH - 1, HEIGTH - 1);
wallsBody.createFixture(wallFixtureDef);

Circles:

CircleShape circleShape = new CircleShape();
circleShape.setRadius(24);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circleShape;
fixtureDef.density = 1;
fixtureDef.friction = 0;
fixtureDef.restitution = 1;

for (int i = 1; i <= 10; i++) {
    BodyDef bodyDef = new BodyDef();
    bodyDef.type = BodyDef.BodyType.DynamicBody;
    bodyDef.position.set(i * 70, HEIGTH / 2);

    Body ballBody = world.createBody(bodyDef);
    ballBody.createFixture(fixtureDef);
    ballBody.setBullet(true);
    ballBody.setLinearVelocity(300, 120);
}

I've also tried without: World.setVelocityThreshold(0); and without ballBody.setBullet(true); but it does the same.

So, how could I achieve it? If restitution = 1 and friction = 0, what's causing this behavior?

UPDATE:

Finally it works as expected. There are two main problems: float precision and too large initial velocity (box2d has a maximum speed limit).

I have reproduced the example but with much smaller units:

radius = 10 
density = 0.1f
velocity = (10, 10)

This does not prevent the precision error in the positions of the circles, which will end up not being perfectly aligned. But it prevents the system from losing speed.

With these values, once the circles are not aligned, depending on the angle at which they collide, if one slows down the other one accelerates. That is, the momentum of the system is maintained.

In the original example, the problem is that I use large units. Circles' initial velocity is limited to the maximum that box2d allows. In this situation, when the circles collide without being aligned, one may slow down but the other one can not accelerate (because it's already at max speed) and, therefore, the system loses speed. The momentum of the system is not maintained.

The first correction is to use smaller units, so that the precision error is lower and values does not exceed the limits of box2d.

enter image description here

The second, only valid for this concrete example, would be to correct the position of the circles once the simulation of physics has been made.

world.step(STEP_TIME, VELOCITY_ITERATIONS, POSITION_ITERATIONS);

float y = bodies[0].getPosition().y;
for (Body body : bodies) {
    body.setTransform(body.getPosition().x, y, body.getAngle());
}

enter image description here

1 个答案:

答案 0 :(得分:2)

身体上大于零的线性阻尼设置意味着减慢身体的速度。

鉴于您显示的来源和box2d sources show that by default the linear damping is 0,线性阻尼设置似乎不是罪魁祸首。我将重新检查应用程序源和libgdx的构建源,以确认实际上没有任何东西将动态物体上的线性阻尼设置为非零值。

另一方面,如果在某个地方默认将线性阻尼设置为非零,我希望所有圆圈都减慢(同样,至少在理论上)并且您的图片看起来只显示一个圆圈显着的速度变化:速度损失。

所以我相信一些不那么有意(而且更深奥)的东西在起作用。

至少你看到的一些内容将来自于使用floating point arithmetic。浮点运算的不准确性因圆圈对墙壁和彼此的反弹而变得更加复杂。在这种情况下,圈子 - 位于不同的xy位置 - 有时会产生略微不同的碰撞响应错误,从而破坏了他们开始的有序设置。

有关使用浮点运算可能出现的问题的详细说明,请参阅:What Every Computer Scientist Should Know About Floating-Point Arithmetic

至于你可以做些什么来防止这种情况,我没有任何简单的解决方案。缓解问题会更容易。一些可能的缓解措施可能是:

  • 使用不同的尺寸,距离,密度和速度重新运行模拟,以查看哪种组合可以最佳地优化模拟。如果速度损失比模拟所涉及的一般混乱更令人担忧,那么增加质量/密度将是我尝试这些的第一件事。
  • 减少步骤增量时间并更频繁地调用步骤。
  • 减少模拟中的圆圈数。
  • 在所有Box2D源代码中替换floatdouble的使用,并在修改后的Box2D上重建Java实现。

使用经过验证的固定点整数实现替换浮点数的使用应该消除浮点不准确性,但代价是引入不同类型的不准确性并且比用双倍替换浮点数替换使用更多的工作。您可以尝试解决此问题的另一个方法是覆盖联系人侦听器回调中的冲突响应,以便您考虑不准确性并将其反馈到计算中。如果没有您想要模拟的其他交互,我认为在您的联系人侦听器代码中强制使用已知路径而不是尝试纠正错误会更容易。

如果你期望100%牛顿的摇篮般的结果,那么我可能更难以实现浮点运算的原因。请注意,类似Box2D的物理模拟通常不能提供类似牛顿的摇篮般的结果(例如,请参阅YouTube上的this视频)。我在网上发现了一些关于此问题的讨论,例如Bullet物理论坛上的Newton's cradle,可能会提供更多见解或想法。顺便说一下,我有a Newton's cradle demo in the test bed of the physics engine I've been working on提供了很多运行时调整能力,并且还演示了非牛顿的摇篮般的结果。

对不起,如果这看起来像坏消息,但我希望无论如何都可以提供帮助。