user6135822 user6135822 - 5 months ago 62x
Java Question

Huge Image when Using Pixel Per Meter on Libgdx Box2d World

hi guys I am trying to implement a box2d world. I have read that box2d uses meters. and You need to convert it from pixels to meters.

I tried to draw an image but do I have to scale down also the image? I think that is a bad I idea to draw the image, the image are very huge and can't figure what to do to make it work with the box2d pixel per meter

public class TestScreen extends ScreenAdapter {

private final Body body;
private int V_WIDTH = 320;
private int V_HEIGHT = 480;
private int PPM = 100;

private SpriteBatch batch;
private OrthographicCamera camera;
private World world;
private Sprite sprite;
Box2DDebugRenderer box2DDebugRenderer;

public TestScreen(){
batch = new SpriteBatch();
camera = new OrthographicCamera();
camera.setToOrtho(false, V_WIDTH / PPM, V_HEIGHT / PPM);
world = new World(new Vector2(0,0) , true);

sprite = new Sprite(new Texture("test/player.png"));
box2DDebugRenderer = new Box2DDebugRenderer();

BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.KinematicBody;
body = world.createBody(bodyDef);
FixtureDef fixtureDef = new FixtureDef();

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

sprite.setPosition(body.getPosition().x - sprite.getWidth() /2 ,body.getPosition().y - sprite.getHeight() / 2 );

public void render(float delta) {
camera.position.set( body.getPosition().x, body.getPosition().y , 0);
world.step(1/60.0f, 6, 2);
box2DDebugRenderer.render(world, camera.combined);

with out ppm

with PPm

should I scale down the image? what is the best way to draw the image


You don't need to convert from pixel to meter. As a matter of fact you should forget about pixels. They exist only on your screen and you game logic should not know anything about your screen. That is what a camera or viewport is for, you specify how much of the world to show and if the display should be stretched or blackboxed or whatever. So no pixels, period. They are evil and give you wrong ideas.

Now if you create your own game you can say that a single unit represents 1mm, 34cm or a couple of lightyears. You tell the object responsible for displaying your game how much of these units to display. However you are using Box2D, and Box2D has already filled in the unit for you 1 unit == 1m. It is probably possible to change this or at least create a wrapper class that converts you units to the Box2D unit.

The reason why it is important to keep true to the Box2D unit is the following. If you drop a marble on the ground it seems to be moving faster then the sun in the sky. But believe me, the sun is moving a lot faster but since it is a lot further away it seems to move slowly. Since Box2D is all about movement you should keep true to the unit or things will start to act strange.

Let's just use 1 unit == 1m for now and suddenly everything should become a lot simpler by asking a view questions.

  • how much of your game world do you want to show in meters?

    float width = 20; // 20 meters
    //You can calculate on your chosen width or height to maintain aspect ratio
    float height = ( / * width;        
    camera = new OrthographicCamera(width, height);
    //Now the center of the camera is on 0,0 in the game world. It's often more desired and practical to have it's bottom left corner start out on 0,0
    //All we need to do is translate it by half it's width and height since that is the offset from it's center point (and that is currently set to 0,0.
    camera.translate(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
  • How large is our object? Keep in mind that mass, weight and size are completely different things.

    Sprite mySprite = new Sprite(myTexture);
    //position it somewhere within the bounds of the camera, in the below case the center
    //This sprite also gets a size of 1m by 1m
    mySprite.setBounds(width / 2, height / 2, 1, 1);
  • How do we want the SpriteBatch to draw to the screen?

    //We tell the SpriteBatch to use out camera settings to draw
    //And draw the sprite using this SpriteBatch

Same counts for the Box2dDebugRenderer implemenation. If you want shapes to show you need to use that combined matrix from your camera again to draw it.

    box2DDebugRenderer.render(world, camera.combined);

Of course, when things move around you need to update your sprite position accordingly. You can get this information from the box2d.Body object. But this is beyond the scope of your question.

To finally show you what is going wrong:

camera.setToOrtho(false, V_WIDTH / PPM, V_HEIGHT / PPM);

Your camera shows 320/100 == 3.2f x 480/100 == 4.8f of your game world. Your sprite might be 64x64 pixels. You are not telling anywhere at what size to draw your sprite so it will assume 1 pixel = 1 unit and you set your camera to show 3.2f units in width. We can and should leave pixels out of the equation and just ask what size you want your object to be. Then set the Sprite to that size. Here you see that thinking in pixels just gives you problems.

For a space game where you fly a ship of 100x20 meters in 3th person you probably want your camera viewport to be very large. But for a ant game where your ants are real size you want a very small camera viewport. Do think about physics in real life. Galileo Galilei discovered that objects fall at the same speed, disregarding resistance. So if that ant would drop a sand grain it would look like it would fall very fast because your screen represents much less meters.

For a implementation of a dropping soccer ball look at my answer here. It creates a box2D body and attaches a image to it. I keep the functionality of the ball encapsulated within the Ball() class. (disclaimer: I have just played around a bit with Box2D and I don't know the exact physical behaviors of a soccer ball so I am not stating this is a correct implementation, but it does show how to setup your scene and have a image represent your Box2D body).