4
votes

This is a very simple scene with box2d. I tried different viewports and different screen sizes. I could not figure out why the body drops very slow. Actually, I am not quite sure that it is slow, the reason is maybe the viewport settings etc. This is main class:

public class Main extends Game {
LevelScreen levelScreen;

@Override
public void create () {
    levelScreen = new LevelScreen();
    setScreen(levelScreen);

}

@Override
public void render () {
    super.render();
}
}

And level screen:

public class LevelScreen extends Stage implements Screen {

private Batch batch;
private Camera camera;
private Texture ballTexture;
private Sprite ball;
private Viewport viewport;
//com

private Vector3 point = new Vector3();

private World world;
private Box2DDebugRenderer box2DDebugRenderer;

private CircleShape circleShape;
private FixtureDef fixtureDef;
private BodyDef bodyDef;
private Body circleBody;

private static final float SCENE_WIDTH = 1080;
private static final float SCENE_HEIGHT = 1920f;


public LevelScreen() {
    super(new FitViewport(SCENE_WIDTH, SCENE_HEIGHT,  new OrthographicCamera(SCENE_WIDTH, SCENE_HEIGHT)));


    batch = getBatch();
    camera = getCamera();
    viewport = getViewport();

    world = new World(new Vector2(0,-9.8f), true);
    box2DDebugRenderer = new Box2DDebugRenderer();
    bodyDef = new BodyDef();
    bodyDef.type = BodyDef.BodyType.DynamicBody;
    bodyDef.position.set(600, 1000);

    ballTexture = new Texture("ball.png");
    ball = new Sprite(ballTexture);
    ball.setPosition(0,0);

    circleShape = new CircleShape();
    circleShape.setRadius(25f);

    fixtureDef = new FixtureDef();
    fixtureDef.shape = circleShape;
    fixtureDef.density = 0.5f;
    fixtureDef.friction = 0.4f;
    fixtureDef.restitution = 0.6f;

    circleBody = world.createBody(bodyDef);
    circleBody.createFixture(fixtureDef);

    box2DDebugRenderer = new Box2DDebugRenderer(
            true, /* draw bodies */
            false, /* don't draw joints */
            true, /* draw aabbs */
            true, /* draw inactive bodies */
            false, /* don't draw velocities */
            true /* draw contacts */);

    Gdx.input.setInputProcessor(this);


}

@Override
public void show() {
    System.out.println("show");
}

@Override
public void render(float delta) {
    Gdx.gl.glClearColor(1, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.setProjectionMatrix(camera.combined);

    batch.begin();
    ball.draw(batch);

    batch.end();

    world.step(1 / 60f, 6, 2);
    ball.setPosition(circleBody.getPosition().x - 25f, circleBody.getPosition().y - 25f);
    box2DDebugRenderer.render(world, viewport.getCamera().combined);

}

@Override
public void resize(int width, int height) {
    viewport.update(width, height);
    System.out.println("resize");
}

@Override
public void pause() {
    System.out.println("pause");
}

@Override
public void resume() {
    System.out.println("resume");
}

@Override
public void hide() {
    System.out.println("hide");
}

@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
    viewport.getCamera().unproject(point.set(screenX, screenY, 0));
    return false;
}
}

enter image description here

2
1 unit in Box2D stands for a meter I believe. So if you give a soccer ball a diameter of 50 meter it will look like it's falling slowly. It's like you view the earth rotate around the sun which takes 365 days yet with a speed of 108.000 km/h. Would you zoom in on the earth it would disappear in nanoseconds but if you zoom out of the solar system you won't even see it move. Try circleShape.setRadius(.11f); and zoom in. Or increase the gravity, but that can cause issues later on.Madmenyo
what about textures? they will be huge if I zoom in.Hakan Genç
Your red background will be huge yes but the ball texture will be scaled properly since you make it so small. So you need to make everything smaller. Size is a relative thing in game development.Madmenyo
Can you give an exapmle code? My ball texture is 50x50px. How should I draw it to be not huge. Should I do something like sprite.scale(1/100)?Hakan Genç
You do not understand. Stop thinking about pixels and size, everything is relative. Your ball should remain the same size on the screen. First you shrink it and then you bring in the camera close. Read my first comment again. If I would drop a coin right in front of my face it would pass my view in a couple of millisecond. Now stand in front of a tall building so you can see the top balcony and the ground in a single view. Here you ask a friend to drop another object. Now that object travels a lot longer in you because you are zoomed out.Madmenyo

2 Answers

2
votes

You should use small camera for box2d because box2d works better in 0-10 values. Here is your level screen class.Try it.

    public class LevelScreen extends Stage implements Screen {

         private Batch batch;
         private Camera camera;
         private Texture ballTexture;
         private Sprite ball;
         private Viewport viewport;


         private Vector3 point = new Vector3();

private World world;
private Box2DDebugRenderer box2DDebugRenderer;

private CircleShape circleShape;
private FixtureDef fixtureDef;
private BodyDef bodyDef;
private Body circleBody;

private static final float SCENE_WIDTH = 28;
private static final float SCENE_HEIGHT = 48f;


public LevelScreen() {
    super(new FitViewport(SCENE_WIDTH, SCENE_HEIGHT,  new OrthographicCamera(SCENE_WIDTH, SCENE_HEIGHT)));


    batch = getBatch();
    camera = getCamera();
    viewport = getViewport();

    world = new World(new Vector2(0,-9.8f), true);
    box2DDebugRenderer = new Box2DDebugRenderer();
    bodyDef = new BodyDef();
    bodyDef.type = BodyDef.BodyType.DynamicBody;
    bodyDef.position.set(10, 28);

    ballTexture = new Texture("ball.png");
    ball = new Sprite(ballTexture);
    ball.setPosition(0,0);

    circleShape = new CircleShape();
    circleShape.setRadius(1f);

    fixtureDef = new FixtureDef();
    fixtureDef.shape = circleShape;
    fixtureDef.density = 0.5f;
    fixtureDef.friction = 0.4f;
    fixtureDef.restitution = 0.6f;

    circleBody = world.createBody(bodyDef);
    circleBody.createFixture(fixtureDef);

    box2DDebugRenderer = new Box2DDebugRenderer(
            true, /* draw bodies */
            false, /* don't draw joints */
            true, /* draw aabbs */
            true, /* draw inactive bodies */
            false, /* don't draw velocities */
            true /* draw contacts */);

    Gdx.input.setInputProcessor(this);


}

@Override
public void show() {
    System.out.println("show");
}

@Override
public void render(float delta) {
    Gdx.gl.glClearColor(1, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.setProjectionMatrix(camera.combined);

    batch.begin();
    ball.draw(batch);

    batch.end();

    world.step(1 / 60f, 6, 2);
    ball.setPosition(circleBody.getPosition().x - 25f, circleBody.getPosition().y - 25f);
    box2DDebugRenderer.render(world, viewport.getCamera().combined);

}

@Override
public void resize(int width, int height) {
    viewport.update(width, height);
    System.out.println("resize");
}

@Override
public void pause() {
    System.out.println("pause");
}

@Override
public void resume() {
    System.out.println("resume");
}

@Override
public void hide() {
    System.out.println("hide");
}

@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
    viewport.getCamera().unproject(point.set(screenX, screenY, 0));
    return false;
}
}
3
votes

Ok I cannot seem to make you understand by a simple comment. Let's try by code:

Screen

public class TestScreen implements Screen {
    SpriteBatch batch;
    OrthographicCamera camera;

    World world;
    Box2DDebugRenderer dr;

    Ball ball;


    public TestScreen() {
        batch = new SpriteBatch();
        camera = new OrthographicCamera(1.6f, 1f); // <---- Very small camera so it passes by fast since less surface is being shown

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

        ball = new Ball(.11f, world); // <---- Create ball and pass in the diameter

        //Try playing with the value of camera let's say we have a ball the size of planet earth:
        //ball = new Ball(6371, world);

        //Now zoom out the screen so we can see our planet sized ball
        //camera = new OrthographicCamera(16000, 10000);

        //Believe me, our planet ball falls as fast as the little soccer ball.       //But since you zoomed out each pixel represents so much more distance.


        dr = new Box2DDebugRenderer(true, false, false, false, false, false);
    }

    @Override
    public void show() {

    }

    @Override
    public void render (float delta) {
        Gdx.gl.glClearColor(.1f, .1f, .14f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.setProjectionMatrix(camera.combined);

        world.step(Gdx.graphics.getDeltaTime(), 6, 2);

        batch.begin();
        ball.draw(batch);
        batch.end();

        dr.render(world, camera.combined);

    }
 //... Other mandatory screen methods
}

Ball.java

public class Ball {

    private float radius;

    private CircleShape shape;
    private FixtureDef fixtureDef;
    private BodyDef bodyDef;
    private Body circleBody;

    private Texture ballTexture;

    public Ball(float radius, World world) {
        this.radius = radius;

        ballTexture = new Texture("sprites/soccerball.png");

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

        shape = new CircleShape();
        shape.setRadius(radius);

        fixtureDef = new FixtureDef();
        fixtureDef.shape = shape;
        fixtureDef.density = 0.5f;
        fixtureDef.friction = 0.4f;
        fixtureDef.restitution = 0.6f;

        circleBody = world.createBody(bodyDef);
        circleBody.createFixture(fixtureDef);
    }

    public void draw(SpriteBatch batch)
    {
        batch.draw(ballTexture, circleBody.getPosition().x - radius, circleBody.getPosition().y - radius,
                radius * 2, radius * 2); // <---- draw the size you give it in the physics engine
    }
}

Let's start what you did wrong:

You gave the world a gravity of 9,8 units. These are not pixels, nor distance, nor anything yet. When you decide these are meters (represents the gravity on planet earth) only then they will be meters and you should stay true to that scale.

Next you make a ball with a radius of 25 meters and zoom the camera out to show a region of 1920 meters high. In real life it takes a while to drop a object from 2km to reach the ground. And so it will take some time to hit the ground in your app as well since you are using the 9,8m per second in the real world.

So what you want to do is scale things down. A soccer ball has just a radius of 11cm so I create a ball with a radius of 0.11m. But you would not be able to see that since it will be just a fraction of a pixel with your camera zoomed out so far. So zoom back in your camera (since it's orthographic you just set it's viewport dimensions).

For your follow up question about drawing the ball the correct size you simply use the radius given earlier. As you can see for the ball the camera vp is very small and the ball is very small too. For the planet size example the camera is zoomed out very far and the planet is the size of our own earth. Yet I just draw the ball by it's radius and everything will be fine.

Hope you understand, perhaps you have to stop thinking too much about it since the concept is very simple.