Search code examples
javaandroidlibgdxbox2d

Libgdx Box2d body losing speed while falling


I'm very new to Box2d, and I am trying to make a simple program for a body to fall with a sprite drawn over it. When I run my program, everything works except the body keeps jumping down in speed, but still accelerating down. For example, the speed goes 0, -10, -20, -30, -40, -50, -60, -20, -30, -40, -50... and so on. It's very possible that I am doing something very wrong, as I am very new to this. Thank you for the help!

package com.davejones.spritetest;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class SpriteTest extends ApplicationAdapter {

private SpriteBatch batch;
private World world;
private BodyDef bodyDef;
private Body body;
private Texture texture;
private Sprite sprite;
private OrthographicCamera camera;
private Box2DDebugRenderer debugRenderer;

@Override
public void create () {
    debugRenderer = new Box2DDebugRenderer();
    debugRenderer.setDrawBodies(false);
    camera = new OrthographicCamera();
    batch = new SpriteBatch();
    texture = new Texture("badlogic.jpg");
    sprite = new Sprite(texture);

    sprite.setPosition(Gdx.graphics.getWidth() / 2 - sprite.getWidth() / 2, Gdx.graphics.getHeight() / 2);

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

    bodyDef = new BodyDef();

    bodyDef.type = BodyDef.BodyType.DynamicBody;

    bodyDef.position.set(sprite.getX(), sprite.getY());
    body = world.createBody(bodyDef);

    PolygonShape shape = new PolygonShape();
    shape.setAsBox(sprite.getWidth()/2, sprite.getHeight()/2);

    FixtureDef fixtureDef = new FixtureDef();
    fixtureDef.shape = shape;
    fixtureDef.density = 1f;

    Fixture fixture = body.createFixture(fixtureDef);

    shape.dispose();
}

@Override
public void resize(int width, int height) {
    super.resize(width, height);
    float cameraWidth = Gdx.graphics.getWidth();
    float cameraHeight = Gdx.graphics.getHeight();
    camera.setToOrtho(false, cameraWidth, cameraHeight);
    camera.position.set(Gdx.graphics.getWidth() / 2 - sprite.getWidth() / 2, Gdx.graphics.getHeight() / 2 - sprite.getHeight() / 2, 0);
}


@Override
public void render () {
    world.step(Gdx.graphics.getDeltaTime(), 8, 3);

    sprite.setPosition(body.getPosition().x, body.getPosition().y);

    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.begin();
    batch.draw(sprite, sprite.getX(), sprite.getY());
    batch.end();

    System.out.println(body.getLinearVelocity().y);

    camera.update();
    debugRenderer.render(world, camera.combined);
}

@Override
public void dispose () {
    batch.dispose();
    texture.dispose();
    world.dispose();
    debugRenderer.dispose();
}

}


Solution

  • Box2d is best used with values from 0.1 to 10 as values over these sizes start to loose precision. Each Box2D unit is supposed to represent 1 meter so your object is 256 meters by 256 meters which is huge. This coupled with the fact that Box2D has a limit on the maximum velocity for an object at 2 meters per step means your 256m square object is falling at 120 meters per second at 60 FPS which is over 250 mph.

    This is usually countered by using some form of conversion between screen units and box2d units. Either using a constant value to convert your 256 to a range inside 0.1 to 10 or by using a camera matrix to render the object larger on screen.

    I have updated your example to use a viewport. This won't fix the issu with Box2D maximum velocity but it should help reduce the effects by using smaller objects which match the units Box2D is designed to work best with.

    package com.mygdx.gtest;
    
    import com.badlogic.gdx.ApplicationAdapter;
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.graphics.GL20;
    import com.badlogic.gdx.graphics.Texture;
    import com.badlogic.gdx.graphics.g2d.Sprite;
    import com.badlogic.gdx.graphics.g2d.SpriteBatch;
    import com.badlogic.gdx.math.Vector2;
    import com.badlogic.gdx.physics.box2d.Body;
    import com.badlogic.gdx.physics.box2d.BodyDef;
    import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
    import com.badlogic.gdx.physics.box2d.Fixture;
    import com.badlogic.gdx.physics.box2d.FixtureDef;
    import com.badlogic.gdx.physics.box2d.PolygonShape;
    import com.badlogic.gdx.physics.box2d.World;
    import com.badlogic.gdx.utils.viewport.ExtendViewport;
    
    public class Test extends ApplicationAdapter {
    
        private SpriteBatch batch;
        private World world;
        private BodyDef bodyDef;
        private Body body;
        private Texture texture;
        private Sprite sprite;
        private Box2DDebugRenderer debugRenderer;
        private ExtendViewport viewport;
    
        @Override
        public void create() {
            debugRenderer = new Box2DDebugRenderer(true,true,true,true, true, true);
            viewport = new ExtendViewport(64,48); // adding viewport so we can use multiple resolutions easily
            viewport.getCamera().position.set(32, 24, 0); // set cam position to make 0,0 bottom left corner
            batch = new SpriteBatch();
            batch.setProjectionMatrix(viewport.getCamera().combined); // tell the batch how things should be rendered 
            texture = new Texture("badlogic.jpg");
            sprite = new Sprite(texture);
            sprite.setSize(10,10); // set the size of the sprite in box2d units (10 meters x 10 meters)
    
            // not needed as position is set in update method
            //sprite.setPosition(Gdx.graphics.getWidth() / 2 - sprite.getWidth() / 2,   Gdx.graphics.getHeight() / 2);
    
            world = new World(new Vector2(0, -10), true);
    
            bodyDef = new BodyDef();
    
            bodyDef.type = BodyDef.BodyType.DynamicBody;
    
            bodyDef.position.set(32 , 47);
            body = world.createBody(bodyDef);
    
            PolygonShape shape = new PolygonShape();
            shape.setAsBox(5,5);
    
            FixtureDef fixtureDef = new FixtureDef();
            fixtureDef.shape = shape;
            fixtureDef.density = 1f;
    
            Fixture fixture = body.createFixture(fixtureDef);
    
            shape.dispose();
        }
    
        @Override
        public void resize(int width, int height) {
            viewport.update(width, height); 
        }
    
        @Override
        public void render() {
            world.step(Gdx.graphics.getDeltaTime(), 8, 3);
    
            sprite.setPosition(body.getPosition().x - (sprite.getWidth()/2), body.getPosition().y -(sprite.getHeight()/2));
    
            Gdx.gl.glClearColor(0, 0, 0, 1);
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
            batch.setProjectionMatrix(viewport.getCamera().combined);  // tell the batch how things should be rendered incase of resized window
            batch.begin();
            batch.draw(sprite, sprite.getX(), sprite.getY(),sprite.getWidth(),sprite.getHeight());
            batch.end();
    
            System.out.println(body.getLinearVelocity().y);
    
            debugRenderer.render(world, viewport.getCamera().combined);
        }
    
        @Override
        public void dispose() {
            batch.dispose();
            texture.dispose();
            world.dispose();
            debugRenderer.dispose();
        }
    }