2
\$\begingroup\$

I want to maintain the speed of a free-falling Box2D body using LibGDX. I'd like the vertical velocity increase with the level. I've applied a linear impulse and velocity actually increases like this:

fruitBody.applyLinearImpulse(0, -800, fruitBody.getLocalCenter().x, fruitBody.getLocalCenter().y, true);

I think this is a bad approach, because the speed has increased only a little and I applied as much as -800 units of impulse.

Below is my render function of game play screen class:

    public void render(float delta) {

        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

//      camera.update();
//      batch.setProjectionMatrix(camera.combined);
        batch.begin();

        backgroundSprite.draw(batch);
        grassSprite.draw(batch);

//      bucketSprite.draw(batch);

        Iterator<Body> bodies = world.getBodies();

        while(bodies.hasNext()){

            Body body = bodies.next();

            if(body.getUserData() != null){

                String spriteName = (String) body.getUserData();

                Sprite sprite = (Sprite) userDataMap.get(spriteName);

                if(spriteName.equals("bucket"))
                    sprite.setPosition(body.getPosition().x + 345, body.getPosition().y + 230);
                else
                    sprite.setPosition(body.getPosition().x + 375, body.getPosition().y + 225);

                sprite.draw(batch);

            }
        }

        batch.end();

        fruitBody.applyLinearImpulse(0, -800, fruitBody.getLocalCenter().x, fruitBody.getLocalCenter().y, true);
//      debugRenderer.render(world, camera.combined);
        world.step(1/60f, 8, 3);
//      world.clearForces();
    }

My world is:

world = new World(new Vector2(0, -10), true);

Orthographic camera:

camera = new OrthographicCamera(800, 480);

My fruitBody fixture def is:

bodyDef.type = BodyType.DynamicBody;
        bodyDef.position.set(0, 100);

        // shape
        CircleShape ballShape = new CircleShape();
        ballShape.setRadius(15f);

        //fixture
        fixtureDef.friction = .1f;
        fixtureDef.restitution = .7f;
        fixtureDef.shape = ballShape;
        fixtureDef.density = .2f;

        fruitBody = world.createBody(bodyDef);
//      fruitBody.setUserData(fruitSprite);
        fruitBody.setUserData("fruit");
        fruitBody.createFixture(fixtureDef);

I just want to increase the fruit's speed (free falling body) at each level by a given value.

\$\endgroup\$
  • \$\begingroup\$ The answer to this question depends on what happens to the fruit. If you just want them all to move across the screen at a constant velocity then I would set the BodyDef.BodyType to KinematicBody and call setLinearVelocity with some velocity vector for each body you create. The fruit will move at a constant speed across the screen. If you want the bodies to fall (accelerate) under the influence of gravity and reach terminal velocity, that is a different story. I can provide a complete answer if this is what you are looking for. \$\endgroup\$ – NauticalMile Mar 16 '14 at 18:40
  • \$\begingroup\$ @NauticalMile.... thanks for your comment. Yeah, I need bodies should fall under the influence of gravity. \$\endgroup\$ – ved Mar 18 '14 at 7:23
  • \$\begingroup\$ If you set a certain velocity at first like @NauticalMile suggested and then let gravity do it's thing then you get both. \$\endgroup\$ – AturSams Mar 18 '14 at 9:21
  • \$\begingroup\$ This question confuses me. Are you asking how to implement gravity? \$\endgroup\$ – Anko Mar 18 '14 at 22:52
  • \$\begingroup\$ @Anko... why would I implement gravity?? It's already implemented . I just want to increase free falling speed at each level of game to increase difficulty. \$\endgroup\$ – ved Mar 19 '14 at 4:06
6
+50
\$\begingroup\$

Disclaimer: I have not used libgdx or Java before, this answer borrows syntax from the question and official documentation, and the code is untested

To make the bodies 'fall' under the influence of gravity in box2d, you must first pass a non-zero gravity vector to the b2World when constructing the world

World world = new World(new Vector2(0, -10), true);

This means that every time World::step is called the gravitational force will be applied to every DynamicBody and the velocity will be updated accordingly.

Drag Force

This is all well and good if all you want is constant acceleration, without any opposing forces. To add a little realism we would like to add a drag force to simulate air resistance. I know there are a lot of resources out there explaining how air drag works, and the wikipedia article is the obvious starting point.

In a nutshell, the magnitude of the drag force is most frequently modelled like this:

Drag Force

Whenever you encounter a new formula like this, I find the best way to get an understanding of it is to see what happens when specific variables are set to zero, become negative, or take on large values.

In this case I just want to consider what happens to the drag force when the velocity, v, changes and all other parameters are set to positive, real values.

  • If v -> 0 then Fd will also tend to zero.
  • If v >> 0 then Fd will take on a large positive value.
  • If v << 0 then Fd will take on a large positive value.

Notice Fd will never be negative. This is an important feature.

The direction of the drag force is frequently assumed to be opposite to the velocity vector. This is a simple assumption that is easy to implement and unconditionally stable in code.

So if our fruit is falling (accelerating) under the influence of gravity it's gaining speed. But while that's happening the drag force must be increasing because the velocity magnitude is increasing. The drag force will continue to increase until terminal velocity is reached.

Terminal velocity is the point at which the drag force is equal in magnitude and opposite in direction to the gravity force. If the forces are equal, the acceleration of the fruit must be zero, and the velocity of the fruit which satisfies this condition is the terminal velocity.

You can even determine what the terminal velocity must be at the outset if you set Fd = Fg and solve for v.

Implementation

This is great in theory, but how can we implement this in code?

box2d does not provide any built-in features to implement drag, so we have to do it ourselves.

To do this we need to get all the bodies and cycle through them at each time step, calling Body::applyForceToCenter on each one. I'm going to borrow the code from your rendering function as an example.

Iterator<Body> bodies = world.getBodies();

//This constant H is the lump constant of 1/2*rho*Cd*A from the wikipedia formula
//Play with this parameter to get the results you want
float H = 0.5; 

while(bodies.hasNext()){

   Body body = bodies.next();

   Vector2 v = body.getLinearVelocity();

   //Get the square of the velocity by computing the square of the distance from the origin
   float vSqrd = v.dst2(Vector2());

   //Calculate the magnitude of the drag force
   float fMag = H*vSqrd;

   //Calculate the drag force vector to apply
   //We do this by taking the norm of the velocity and negating it to get the direction.
   //That vector is multiplied by the magnitude to get the drag force we want to apply
   Vector2 fd = -v.norm()*fMag;

   //Finally we communicate this to box2d by calling applyForceToCenter
   body.applyForceToCenter(fd);

   }

That should be it. I don't have anything setup right now to run that code, but that is the is the general idea. I judged from your language that you were having more trouble understanding the physics than the code, which is why I expounded on that first.

As a side note applying linear impulses at every time step can produce some funky results because it applies the same impulse regardless of the timestep size (which can vary) whereas applying a force takes into account the timestep size.

\$\endgroup\$
  • \$\begingroup\$ If you want any clarification or additional explanation let me know \$\endgroup\$ – NauticalMile Mar 18 '14 at 16:06
  • \$\begingroup\$ @NauticalMile.... thanks for u'r valuable effort. Let me implement this concept into my application. \$\endgroup\$ – ved Mar 19 '14 at 6:38
  • \$\begingroup\$ btw if you want to change the terminal velocity at each level, just reduce H a little each level and optionally increase the initial velocity of the fruit \$\endgroup\$ – NauticalMile Mar 20 '14 at 19:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.