How do I apply a force to a body in the direction it is traveling (Box2D)?

|▌冷眼眸甩不掉的悲伤 提交于 2019-11-27 03:35:31

问题


I'm using AndEngine/Box2d to develop a game. I have a ball that bounces around the screen. I've successfully made it ignore gravity by applying an opposite force, but it has a tenancy to slow down after the initial impulse, even with the elasticity set to 1. Essentially I want to:

if(speed < a number) apply force or impulse (which is better?) in direction of motion

How might I do this?


回答1:


Well unfortunately, the ball is interacting with other objects so setting velocity did not work, but I have found a solution!

Using forces and fairly extensive trig, I finally came up with:

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

Essentially what this does is create a body and apply an impulse to it to get it moving (worked a lot better than a force for some reason). On every update, a force is applied opposite that of gravity in order to keep the ball aloft (floating, essentially). The elasticity is set to 1, so collisions are almost perfectly elastic. And now the tricky part: I calculated the x and y velocities (vx and vy respectively) and used these to calculate the resultant velocity (vr) and the angle that they were traveling in (t).

If vr is less than the velocity I want (destroyerVelocity), then a force is applied to bump it back up to the destroyer velocity. Since F=mv/t and I just used t=1, the force applied in one direction is equal to the wanted velocity - the actual velocity * the x/y (that's the cos/sin) * the mass of the object. If the object is traveling in the positive x direction and negative y direction, then a force of (x,-y) is applied, and so on.

In order to keep the velocity as close to the destroyerVelocity as possible, I had to apply a force in the opposite direction if it happened to be greater than the destroyerVelocity. This was done in the same manner, just with an opposite force.

Run a log statement of the resultant velocity (with the destroyerVelocity being 7) and it reads something like: 6.900001, 6.9995001, 7.00028, 7.13005,...

Although the ball will spike to like 15 or 20 sometimes, it usually only takes 2 or 3 loops to get it within +- .5 of 7, which is good enough for me! Hope this helps anyone else looking for the same thing.




回答2:


To apply continuous force in the moving direction you can use following code.

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

EDIT :

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

This was the another code that you have to write in manage update method or in update handler.




回答3:


If the ball is not interacting with anything else then using SetLinearVelocity should be fine, and it would be simpler than calculating a force/impulse (C++):

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

The caveat is that this should only be done while the ball is in free motion. If it's right in the middle of bouncing off a wall or something else in the world, this will cause problems. So you'll need to make sure it's not touching anything before doing this. If GetContactList() returns null then the body is not touching anything.

This adjustment should be cheap enough to make in every time step, but since the velocity will not change until it hits something again, you could even get away with doing it just once after the bounce ends. You could use a contact listener and in the EndContact callback, check if the ball is still touching something and if not, flag the body for adjustment after the time step ends.




回答4:


i use this in my update function to maintain the speed of a ball.

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


来源:https://stackoverflow.com/questions/11257462/how-do-i-apply-a-force-to-a-body-in-the-direction-it-is-traveling-box2d

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!