【问题标题】:How to check if a body has almost stopped moving in libgdx + box2d如何在 libgdx + box2d 中检查身体是否几乎停止移动
【发布时间】:2014-02-24 21:59:56
【问题描述】:

所以,我有一个球员身体+固定装置等,它本质上是一个反弹的球。

我想检测它何时“几乎”完成移动。

目前我这样做:

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

这主要是可行的,问题是当玩家击中某物时,它的速度会在一瞬间为 0,因此返回 true。我真正想要的是在它基本完成时返回 true。最好在我调整游戏世界的物理特性时可以设置为任何我喜欢的范围内。

我不能检查它是否在睡觉,因为来得太晚了,直到它停止对它的作用力后它才会睡觉,我需要在它之前。

我可以只存储它已停止的时间/已停止的步骤数,但我希望有一个很好的预先存在的方法,我错过了。

有什么想法吗?

【问题讨论】:

    标签: java android libgdx box2d jbox2d


    【解决方案1】:

    您可以跟踪最近的运动并通过在每个时间步混合一点当前速度来更新它:

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

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

    【讨论】:

    • 是的,我之前就按照这些思路想到了一些东西。不过,我真的在寻找一些融入到身体课程中的东西,我在文档中错过或误解了这些东西。如果我要走这种路线,我会(并且目前)只是添加时间增量作为参数来检测它已经停止了多长时间。这行得通,但感觉应该有更好的东西。
    • 嘿,虽然我最后使用了 delta 方法,但我接受了你的回答,因为它至少向我展示了一个不同的选择。通过查看您的代码,我是否认为我可以将 isMoving 检查更改为 Math.abs(body.getLinearVelocity().len()) >= 0.25f ? .len() 甚至需要 Math.abs 吗?非常感谢您的回答!
    • 哦,我才意识到我从哪里知道 iforce2d!我使用您的 RUBE 编辑器来创建关卡。加上 rubeloader 让我的生活变得轻松多了。物有所值。
    • 谢谢汤姆! len() 函数只是做 sqrt(xx+yy) 所以它总是积极的。
    【解决方案2】:

    看到您如何确定您的误报是由身体与另一个人接触引起的,为什么不在您的 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。

    【讨论】:

      【解决方案3】:

      最后,我只是将每次渲染调用的增量传递给 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 必须在某处拥有此信息才能确定零速度的物体是否没有作用力,或者它是否只是在撞击后改变方向。

      【讨论】:

        猜你喜欢
        • 2012-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多