如何在物体行进方向上施加力(Box2D)?

5
我正在使用AndEngine/Box2d开发游戏。我有一个球在屏幕上弹跳。我已经成功地通过施加相反的力使其忽略重力,但是它有一种倾向,在初始冲量之后变慢,即使弹性设置为1。本质上,我想要做到以下几点:
如果速度小于某个数字, 在运动方向上施加力或冲量(哪一个更好?)
我该如何实现这个功能?
4个回答

6
很不幸,球体正在与其他物体交互,因此设置速度无效,但我找到了一个解决方案!使用力和相当复杂的三角函数,我终于想出了以下方法:
private static class Ball extends Sprite  {
    Body body;

    public Ball(final float pX, final float pY, final ITextureRegion pTextureRegion, final VertexBufferObjectManager pVertexBufferObjectManager) {
        super(pX, pY, pTextureRegion, pVertexBufferObjectManager);
        body = PhysicsFactory.createCircleBody(mPhysicsWorld, this, BodyType.DynamicBody, PhysicsFactory.createFixtureDef(0, 1, 0));
        body.applyLinearImpulse(((float)5),(float) 5, body.getWorldCenter().x,  body.getWorldCenter().y);
        mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(this, body, true, true));
        scene.attachChild(this);
    }

    @Override
    protected void onManagedUpdate(final float pSecondsElapsed) {       
        body.applyForce(new Vector2(0,-SensorManager.GRAVITY_EARTH), new Vector2(body.getWorldCenter()));

        float vx = body.getLinearVelocity().x, vy = body.getLinearVelocity().y, vr=(float) Math.sqrt(vx*vx+vy*vy);
        float t= (float) Math.atan(Math.abs(vy/vx));
        if(vr<destroyerVelocity){
            if(vx>0&&vy<0)
                body.applyForce((float) ((destroyerVelocity-vr)*Math.cos(t)*body.getMass()), (float) (-(destroyerVelocity-vr)*Math.sin(t)*body.getMass()), body.getWorldCenter().x,  body.getWorldCenter().y);
            else if(vx>0&&vy>0)
                body.applyForce((float) ((destroyerVelocity-vr)*Math.cos(t)*body.getMass()), (float) ((destroyerVelocity-vr)*Math.sin(t)*body.getMass()), body.getWorldCenter().x,  body.getWorldCenter().y);
            else if(vx<0&&vy>0)
                body.applyForce((float) (-(destroyerVelocity-vr)*Math.cos(t)*body.getMass()), (float) ((destroyerVelocity-vr)*Math.sin(t)*body.getMass()), body.getWorldCenter().x,  body.getWorldCenter().y);
            else if(vx<0&&vy<0)
                body.applyForce((float) (-(destroyerVelocity-vr)*Math.cos(t)*body.getMass()), (float) (-(destroyerVelocity-vr)*Math.sin(t)*body.getMass()), body.getWorldCenter().x,  body.getWorldCenter().y);
        }
        if(vr>destroyerVelocity){
            if(vx>0&&vy<0)
                body.applyForce((float) (-(vr-destroyerVelocity)*Math.cos(t)*body.getMass()), (float) ((vr-destroyerVelocity)*Math.sin(t)*body.getMass()), body.getWorldCenter().x,  body.getWorldCenter().y);
            else if(vx>0&&vy>0)
                body.applyForce((float) (-(vr-destroyerVelocity)*Math.cos(t)*body.getMass()), (float) (-(vr-destroyerVelocity)*Math.sin(t)*body.getMass()), body.getWorldCenter().x,  body.getWorldCenter().y);
            else if(vx<0&&vy>0)
                body.applyForce((float) ((vr-destroyerVelocity)*Math.cos(t)*body.getMass()), (float) (-(vr-destroyerVelocity)*Math.sin(t)*body.getMass()), body.getWorldCenter().x,  body.getWorldCenter().y);
            else if(vx<0&&vy<0)
                body.applyForce((float) ((vr-destroyerVelocity)*Math.cos(t)*body.getMass()), (float) ((vr-destroyerVelocity)*Math.sin(t)*body.getMass()), body.getWorldCenter().x,  body.getWorldCenter().y);
        }
        super.onManagedUpdate(pSecondsElapsed);
    }
}

基本上,这个程序创建了一个主体并对其施加一个冲量来使其运动(因为某种原因,这比力量更有效)。在每次更新时,施加与重力相反的力以使球保持悬浮状态(基本上是漂浮)。弹性设置为1,因此碰撞几乎完全弹性。现在有点棘手:我计算了x和y速度(分别为vx和vy),并使用它们来计算结果速度(vr)和它们所行进的角度(t)。
如果vr小于我想要的速度(destroyerVelocity),则会施加一个力将其推回到destroyer velocity。由于F = mv / t,而我只使用t = 1,所以单向施加的力等于想要的速度-实际速度* x / y(就是cos / sin)*对象的质量。如果对象朝着正x方向和负y方向移动,则施加(x,-y)的力,依此类推。
为了尽可能使速度接近destroyerVelocity,如果速度大于destroyerVelocity,我必须施加相反方向的力。这样做的方式相同,只是力是相反的。
记录结果速度的日志语句(destroyerVelocity为7),它的读数类似:6.900001, 6.9995001, 7.00028, 7.13005, ...
尽管球有时会突然增加到15或20,但通常只需要2或3个循环即可将其调整在7的正负0.5之间,这对我来说已经足够了!希望这能帮助其他寻找相同内容的人。

一种减少CPU负担的方法是将第一个if语句更改为:if((destroyerVelocity-vr)>acceptedVelocityError),并将第二个if语句更改为:if((vr-destroyerVelocity)>acceptedVelocityError)。然后,您将acceptedVelocityError设置为0.5之类的值。这样,如果速度与期望速度相差不到0.5,就不会施加力。这大大提高了fps。 - rphello101

3
为了在移动方向上施加持续的力,您可以使用以下代码。
Vector2 velocity = this.getBody().getLinearVelocity();
        preX = velocity.x / (Math.abs(velocity.x));
        preY = velocity.y / (Math.abs(velocity.y));

        if (Math.abs(velocity.x) < 5) {
            this.body.setLinearVelocity(preX * 5, velocity.y);
        }
        if (Math.abs(velocity.y) < 5) {
            this.body.setLinearVelocity(velocity.x, preY * 5);
        }

编辑:

currentVelocity = bulletBody.getLinearVelocity();

                    if (currentVelocity.len() < speed
                            || currentVelocity.len() > speed + 0.25f) {

                        velocityChange = Math.abs(speed
                                - currentVelocity.len());
                        currentVelocity.set(currentVelocity.x
                                * velocityChange, currentVelocity.y
                                * velocityChange);

                        bulletBody.applyForce(currentVelocity,
                                bulletBody.getWorldCenter());
                    }

这是另一段代码,您需要将其编写到“manage update”方法或更新处理程序中。

1
设置速度并不等同于施加力。 - Andrew
你可以像上面那样改变函数,将力施加到物体中心。 - Siddharth
我在想当碰撞发生时,这是否会导致一些奇怪的行为 - 对象可能会短暂减速,例如穿过墙壁。 - JohnEye

0
如果球没有与其他任何物体交互,则使用SetLinearVelocity应该是可以的,而且比计算力/冲量(C++)更简单:
b2Vec2 currentDirection = body->GetLinearVelocity();
currentDirection.Normalize();
body->SetLinearVelocity( 5 * currentDirection );

需要注意的是,这只能在球自由运动时进行。如果它正在弹跳到墙壁或其他物体中间,这将会导致问题。因此,在执行此操作之前,您需要确保它没有接触任何物体。如果GetContactList()返回null,则表示该物体没有接触任何物体。

这种调整应该足够便宜,可以在每个时间步骤中进行,但由于速度直到再次碰撞才会改变,因此您甚至可以在弹跳结束后仅执行一次。您可以使用联系人侦听器,在EndContact回调中检查球是否仍在接触某些物体,如果没有,请在时间步骤结束后标记该物体以进行调整。


0

我在我的更新函数中使用这个来保持球的速度。

    speed = ball.getLinearVelocity().length()
    maxSpeed = 10;

        if (speed > maxSpeed ){
            ball.setLinearDamping(0.5);
        }
        else if (speed < maxSpeed) {
            ball.setLinearDamping(0.0);
        }
        if (speed < maxSpeed ){

            var currentVelocity = ball.getLinearVelocity();

            currentVelocity.set(currentVelocity.x * 1.1, currentVelocity.y * 1.1);

            ball.applyForce(currentVelocity,ball.getWorldCenter());         
}

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