如何向身体的行进方向施加力(Box2D)?

时间:2012-06-29 07:04:10

标签: android box2d andengine

我正在使用AndEngine / Box2d来开发游戏。我有一个球在屏幕上反弹。我已经成功地通过施加相反的力来忽略重力,但是在初始冲动之后它有一个减速的速度,即使弹性设置为1.基本上我想:

if(speed< a number ) 在运动方向上施加力或冲动(哪个更好?)

我该怎么做?

4 个答案:

答案 0 :(得分: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),则施加一个力将其撞回到驱逐舰速度。由于F = mv / t且我只使用了t = 1,因此在一个方向上施加的力等于所需速度 - 实际速度* x / y(即cos / sin)*物体的质量。如果物体在正x方向和负y方向上行进,则施加(x,-y)力,依此类推。

为了使速度尽可能接近驱逐舰速度,如果它碰巧大于驱逐舰速度,我必须在相反方向施加一个力。这是以相同的方式完成的,只是用相反的力量。

运行合成速度的日志语句(destroyerVelocity为7),其内容如下:6.900001,6.9995001,7.00028,7.13005,...

虽然球有时会飙升到15或20,但通常只需要2到3圈就能达到+ - .5的7,这对我来说已经足够了!希望这有助于其他人寻找同样的事情。

答案 1 :(得分: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());
                    }

这是您必须在管理更新方法或更新处理程序中编写的另一个代码。

答案 2 :(得分:0)

如果球没有与其他任何东西相互作用,那么使用SetLinearVelocity应该没问题,并且它比计算力/脉冲(C ++)更简单:

b2Vec2 currentDirection = body->GetLinearVelocity();
currentDirection.Normalize();
body->SetLinearVelocity( 5 * currentDirection );

需要注意的是,这只能在球自由运动时完成。如果它正在从墙上或世界上的其他东西中弹出,那么这将导致问题。因此,在执行此操作之前,您需要确保它没有触及任何东西。如果GetContactList()返回null,那么正文不会触及任何内容。

这种调整应该足够便宜,以便在每个时间步长,但由于速度不会改变,直到它再次击中,你甚至可以在反弹结束后只做一次。您可以使用联系人监听器并在EndContact回调中检查球是否仍在触摸某些东西,如果没有,则在时间步骤结束后标记正文以进行调整。

答案 3 :(得分: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());         
}