如何在libgdx + box2d中检查物体是否几乎停止运动

5

所以,我有一个玩家的身体+装置等,它本质上是一个在周围弹跳的球。

我想要检测它何时“基本上”完成移动。

目前我是这样做的:

public Boolean isStopped() {
    return body.getLinearVelocity().x <= 0.3f && body.getLinearVelocity().y <= 0.3f;
}

这样基本可以实现,问题在于当玩家撞到东西时,速度瞬间变为0,所以这个条件返回true。我真正想要的是在它基本完成时返回true。最好是在一个范围内,我可以根据自己调整游戏世界的物理特性来设置。我不能通过检查它是否睡眠来进行检查,因为那太晚了,它只有在停止受力作用后才会进入睡眠状态,我需要在此之前确定。我可以仅存储停止时间/停止步骤的计数,但我希望有一个我可能错过的很好的预先存在的方法。有什么想法吗?
3个回答

4

您可以通过混入一些当前速度来跟踪最近的运动并更新它,每个时间步骤都进行一次:

float speedNow = body.getLinearVelocity().len();
recentSpeed = 0.1 * speedNow + 0.9 * recentSpeed;
if ( recentSpeed < someThreshold )
    ... do something ...

您需要首先将recentSpeed设置为适当的高值,否则它可能在第一个时间步骤就低于阈值。


是的,我之前就想到了类似的解决方案。但我真正寻找的是已经内置在body类中的东西,可能是我在文档中错过或误解了。如果我要走这种路线,我会(并且目前已经)将时间差作为参数添加到检测停止多长时间的函数中。虽然这样可以工作,但感觉应该有更好的方法。 - Tom Manterfield
嗨,虽然最终我使用了delta方法,但至少你的答案向我展示了另一种选择,所以我接受了它。从你的代码中看,如果我将我的isMoving检查更改为_Math.abs(body.getLinearVelocity().len())> = 0.25f_,那么我是正确的吗?在.len()中是否需要Math.abs?顺便说一下,非常感谢你的答案! - Tom Manterfield
哦,我突然意识到我从哪里认识iforce2d!我使用您的RUBE编辑器进行我的关卡创作。加上rubeloader使我的生活变得更加轻松。物超所值。 - Tom Manterfield
谢谢Tom!len()函数只是做sqrt(xx+yy),所以它总是正数。 - iforce2d

1

既然您已经确定您的误报是由身体与其他物体接触引起的,为什么不在您的ContactListener的beginContact方法中添加几行代码,将身体的当前速度存储在其用户数据中呢?然后您可以在isStopped方法中检查该速度。如果有存储的速度并且当前速度不大于它,这意味着身体正在弹开任何被撞到的东西:忽略。如果有存储的速度并且当前速度更高,则球已经反弹并朝某个新方向前进:清除存储的速度。如果没有存储的速度并且当前速度低于您的阈值,则检测到了所寻找的情况。

在您的ContactListener中:

public void beginContact(Contact contact) {
    Body a = contact.getFixtureA().getBody();
    Body b = contact.getFixtureB().getBody();

    if (a == mBall) {
        a.setUserData(a.getLinearVelocity().len());
    } else if (b == mBall) {
        b.setUserData(b.getLinearVelocity().len());
    }
}

在你的isStopped检查中:
public Boolean isStopped() {
    float storedSpd = (Float) body.getUserData();
    float currentSpd = body.getLinearVelocity().len();

    if ((storedSpd > Float.MIN_VALUE) && (currentSpd > storedSpd)) {
        body.setUserData(Float.MIN_VALUE);
        return false;
    } else {
        return (currentSpd < THRESHOLD);
    }
}

这段代码没有经过测试,但你可以理解它的意思。同时,记得最初将用户数据设置为Float.MIN_VALUE。

0
最后,我只是将每个渲染调用的增量传递给isStopped()方法。
public Boolean isStopped(float delta) {
    boolean isMoving = (
            Math.abs(body.getLinearVelocity().x) >= 0.25f || Math.abs(body.getLinearVelocity().y) >= 0.25f);
    if(isMoving) {
        timeStopped = 0f;
        return false;
    } else {
        timeStopped += delta;
        return timeStopped >= 0.3f;
    }
}

timeStopped只是一个以零开始的类属性。虽然在游戏开始时(用户尚未移动)此值返回true,但在我的应用程序中,这绝对没问题。而且,可以说在这种情况下它已经停止了。

我仍然希望能够找到一种不需要存储额外信息的方法,因为我猜想box2d必须在某处保存了这些信息,以便确定速度为零的物体是否有力作用或者仅仅是在撞击后改变方向。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接