【问题标题】:libgdx: speed loss on object collisionlibgdx:物体碰撞时的速度损失
【发布时间】:2018-09-03 21:31:26
【问题描述】:

我正在尝试构建一个样本,该样本包含一些相互移动和碰撞的圆圈。由于所有圆圈都具有相同的属性以及restitution = 1friction = 0,我希望它们应该永远以相同的速度弹跳。但是,相反,一些物体会减慢:

世界创造:

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

边界:

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

圈子:

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

我也尝试过不使用:World.setVelocityThreshold(0); 和不使用 ballBody.setBullet(true);,但效果相同。

那么,我该如何实现呢?如果restitution = 1friction = 0,是什么导致了这种行为?

更新:

终于按预期工作了。主要有两个问题:float精度和初速度太大(box2d有最大速度限制)。

我已经复制了这个例子,但单位要小得多:

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

这并不能防止圆圈位置的精度误差,最终无法完美对齐。但它可以防止系统失去速度。

使用这些值,一旦圆没有对齐,取决于它们碰撞的角度,如果一个减速另一个加速。也就是说,系统的动量保持不变。

在原始示例中,问题是我使用了大单位。 Circles 的初始速度被限制为 box2d 允许的最大值。在这种情况下,当圆圈在没有对齐的情况下发生碰撞时,一个可能会减速,但另一个不能加速(因为它已经处于最大速度),因此系统会失去速度。系统的动量没有保持。

第一个修正是使用更小的单位,这样精度误差会更低,数值不会超过box2d的限制。

第二个,只对这个具体的例子有效,是在物理模拟完成后纠正圆圈的位置。

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

【问题讨论】:

    标签: java libgdx box2d game-physics


    【解决方案1】:

    物体上大于零的线性阻尼设置是为了减慢该物体的速度。

    鉴于您显示的来源和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

    至于您可以采取哪些措施来防止这种情况发生,据我所知,没有任何简单的解决方案。缓解问题会更容易。一些可能的缓解措施可能是:

    • 使用不同的尺寸、距离、密度和速度重新运行模拟,看看哪种组合最适合您优化模拟。如果速度损失比模拟转移到的一般混乱更令人担忧,那么我首先会尝试增加质量/密度。
    • 减少 step delta 时间并更频繁地调用 step。
    • 减少模拟中的圈数。
    • 在所有 Box2D 源代码中将 float 的使用替换为 double,并在修改后的 Box2D 之上重新构建 Java 实现。

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

    如果您期望 100% 像牛顿的摇篮一样的结果,那么由于我提到的浮点算术原因,这可能更难实现。请注意,类似于 Box2D 的物理模拟通常不能提供完全类似于牛顿摇篮的结果(例如,请参阅 YouTube 上的 this 视频)。我在网上找到了一些关于此的讨论,例如 Bullet Physics 论坛上的Newton's cradle,这可能会提供更多见解或想法。顺便说一句,我有a Newton's cradle demo in the test bed of the physics engine I've been working on,它提供了很多运行时调整能力,也展示了非牛顿摇篮式的结果。

    很抱歉,这似乎是个坏消息,但我确实希望它对您有所帮助。

    【讨论】:

    • 我知道浮动算术会导致一些不准确,但我很惊讶它如此臭名昭著。使用其他参数运行模拟可能会有所帮助,但问题仍然存在。我将尝试纠正在侦听器中发生碰撞后的速度损失。
    • 感谢您的评论。它激发了我更多地思考这个问题,我更新了我的答案,希望增加它的有用性。特别是,听起来 sim 的混乱对您来说并不像比其他人慢的圈子那样令人担忧。如果奇怪球圈的速度是我的主要关注点,那么增加所有圆圈的密度/质量似乎是我尝试的第一个缓解措施。我很想听听您看到的结果。
    • 另一个想法...如果即使在圆圈的 AABB 不再重叠之后接触仍然存在,那可能有助于保存系统的能量,因为它们在存在时部分地这样做了。扩大 Box2D 设置文件中的 b2_aabbExtension 并使用更大的扩展名重建可能会提供这一点。嗯……
    • 感谢您如此详尽的回复!到目前为止,我已经尝试了不同的模拟参数,例如:步进时间、尺寸、初始速度、密度……对于其中的一些参数,模拟显然可以持续几分钟。我不期望完美的模拟,但至少圆圈永远不会停止或失去速度。我会对“显然正确”的模拟感到满意。
    • 每当我有空闲的时候,我都会尝试使用监听器在“beginContact”上捕捉对象的速度并在“endContact”上恢复它。根据您的解释,我认为这是拥有稳定和无限模拟的唯一解决方案。
    猜你喜欢
    • 1970-01-01
    • 2013-05-19
    • 2017-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多