0
\$\begingroup\$

I am currently having problems getting an easy to manage system of scaling my units for use with box2D. Normally I would create virtual units to go along with me viewport and camera but since I am using a screen viewport this is not as easy as it seems. I have tried scaling my viewport by setting the units per pixel to 1/100 which will then scales my world by 100 times. Then I set set my sprite batch projection to be 1/100 as well as my tiles map renderer scale to be 1/100 this led to my box2D elements of size .64 to be 64 pixels large which leads to a good looking simulation as well as a good size. The only problem is then any sprite I use must also be scaled with a scale of 1/100. Also my scene 2d HUD must also be scaled. This works but it is confusing to manage and very time consuming to write out all the scaling etc. I am looking to see if there is a way to use world units where 1 world unit is 100 pixels to make my box2D elements and use screen coordinates for the rest of my drawing etc. Also if there is an easier way to do this that I am not thinking of please let me know. I will try to post code later tonight when I am back at my computer.

EDIT: I created a test project to work on just the scaling factor of my game and here is my test code.

@Override
public void create () {
    camera = new OrthographicCamera();
    camera.translate(new Vector3(camera.viewportWidth / 2, camera.viewportHeight / 2, 0));
    vp = new ScreenViewport(camera);
    vp.setUnitsPerPixel(1/100f);

    batch = new SpriteBatch();
    img = new Texture("badlogic.jpg");
    sprite = new Sprite(img);
    sprite.setScale(1/100f);

    world = new World(new Vector2(0, (float) -9.8), true);
    BodyDef bdef = new BodyDef();
    bdef.type = BodyType.DynamicBody;
    bdef.position.set(1, 1);
    body = world.createBody(bdef);
    FixtureDef fdef = new FixtureDef();
    PolygonShape shape = new PolygonShape();
    shape.setAsBox(1, 1);
    fdef.shape = shape;
    body.createFixture(fdef);

    dr = new Box2DDebugRenderer();

    map = new TmxMapLoader().load("Levels/bricksTest.tmx");
    mapRenderer = new OrthogonalTiledMapRenderer(map, 1 / 100f);
}

@Override
public void render () {
    camera.update();
    sprite.setX(body.getPosition().x - sprite.getWidth() / 2);
    sprite.setY(body.getPosition().y - sprite.getHeight() / 2);
    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    mapRenderer.setView(camera);
    mapRenderer.render();
    batch.setProjectionMatrix(camera.combined);
    batch.begin();
    sprite.draw(batch);
    batch.end();
    world.step(Gdx.graphics.getDeltaTime(), 6, 2);
    dr.render(world, camera.combined);
}

@Override
public void resize(int width, int height) {
    vp.update(width, height, true);
}

This code runs and produces the expected results, but I feel there has to be some easier way. Also ignore any of the size numbers/horrible sloppiness of this code because this is only a test project and is not from my actual game.

This is a picture of the final result. Note: The box2D box is not the same size as the image because I wanted the box to be about the size of a character from my game for this test. I also moved the position of the box to x = 5 and y = 5 so it is easier to see. enter image description here

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

I think the only way to achieve a good result with Box2D is scaling things down. I use two OrthographicCameras for my games, one for HUD stuff and another to Box2D related stuff. Also, I use methods for scaling images and elements down.

Here's an example of what I'm talking about. (It's not a working example. Just illustrating...)

private final float SCALE = 10;//WORLD_TO_STAGE_SCALE
private OrthographicCamera camera, hudCamera;
private SpriteBatch batch;
private Sprite background;
private Sprite ball;

private static final int WIDTH = 1280;
private static final int HEIGHT = 720;

@Override
public void create() {
    batch = new SpriteBatch();

    //Camera used for Box2D related images
    camera = new OrthographicCamera(scale(WIDTH), scale(HEIGHT));       
    camera.position.set(scale(WIDTH)/2, scale(HEIGHT)/2, 0);
    //Camera used for HUD
    hudCamera = new OrthographicCamera(WIDTH, HEIGHT);
    hudCamera.position.set(WIDTH/2, HEIGHT/2, 0);

    //This ball is related to a Box2D body, so I need to scale it 
    ball = new Sprite(new Texture(Gdx.files.internal("ball.png")));
    adjustSize(ball);
    //Background is drawn by hudCamera, so no need to change its size 
    background = new Sprite(new Texture(Gdx.files.internal("background.png")));
}

@Override
public void render() {      
    camera.update();
    hudCamera.update();

    //Drawing Box2D related images
    batch.getProjectionMatrix().set(camera.combined);
    batch.begin();
    ball.draw(batch);
    batch.end();

    //Drawing HUD stuff
    batch.getProjectionMatrix().set(hudCamera.combined);
    batch.begin();
    background.draw(batch);
    batch.end();
}

//methods for making scaling easier
public float scale(float valueToBeScaled) {
    return valueToBeScaled/SCALE;
}

public Image adjustSize(Image imageToBeScaled) {
    imageToBeScaled.setSize(scale(imageToBeScaled.getWidth()), scale(imageToBeScaled.getHeight()));
    return imageToBeScaled;
}

public Sprite adjustSize(Sprite spriteToBeScaled) {
    spriteToBeScaled.setSize(scale(spriteToBeScaled.getWidth()), scale(spriteToBeScaled.getHeight()));
    return spriteToBeScaled;
}

public Button adjustSize(Button buttonToBeScaled) {
    buttonToBeScaled.setSize(scale(buttonToBeScaled.getWidth()), scale(buttonToBeScaled.getHeight()));;
    return buttonToBeScaled;
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .