Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
132 views
in Technique[技术] by (71.8m points)

java - Libgdx's World Units

I've been trying to learn libgdx a little but now i'm kinda confused about world units there that i don't even know what exact question i should be asking.

Anyway, i've been reading this example game code here and from what i understand, when there's no camera, SpriteBatch renders everything in relation to device resolution, so pixels, but when we make a camera, set it's "size", position and then use batch.setProjectionMatrix(camera.combined), batch translates pixels to units and then it knows where to render , but in this game there's collision detection between Rectangle objects, and when you're setting position of this Rectangle with rect.set(x, y, 1, 1); where x and y are world units and not pixels, how does rectangle know if it should use those x and y as units and not as pixels if there's nothing used like setProjectionMatrix to let it know that now we're working in units and not in pixels, and then there's this 1, 1); at the end, is it in units too? if so, then how does Rectangle know how big those units shoud be (Scale in game is 1 / 16 for example) renderer = new OrthogonalTiledMapRenderer(map, 1 / 16f);.

I don't even know if this question really makes sense, but that's where i'm at and i'm really lost here

EDIT: Ok, i understand how units work now, but collision still confuses me a bit, here's example, i created this

// onCreate
    mapSprite = new Sprite(new Texture(Gdx.files.internal("space.png")));
            planetTexture = new Texture(Gdx.files.internal("planet.png"));
            shapeRenderer = new ShapeRenderer();


           //Planet(X,Y,Radius)
        planet = new Planet(20, 30, 7);
        planet2 = new Planet(70, 50, 8);
        circle = new Circle(planet.getPosition().x + planet.radius, planet.getPosition().y + planet.radius, planet.radius);
        circle2 = new Circle(planet2.getPosition().x + planet2.radius, planet2.getPosition().y + planet2.radius, planet2.radius);


        mapSprite.setPosition(0,0);
        mapSprite.setSize(133, 100);

        stateTime = 0;

        camera = new OrthographicCamera();
        camera.setToOrtho(false, 133, 100);

        camera.position.set(133 /2, 100 /2, 0);
        camera.update();

        batch = new SpriteBatch();
...
// Render Method

batch.setProjectionMatrix(camera.combined);
        batch.begin();
        mapSprite.draw(batch);
        batch.draw(planetTexture, planet.getPosition().x, planet.getPosition().y, planet.radius * 2, planet.radius * 2);
        batch.draw(planetTexture, planet2.getPosition().x, planet2.getPosition().y, planet2.radius * 2, planet2.radius * 2);

        batch.end();

        shapeRenderer.setProjectionMatrix(camera.combined);
        shapeRenderer.setColor(Color.RED);
        shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);

        shapeRenderer.circle(circle.x, circle.y, circle.radius + 1);
        shapeRenderer.end();

And everything renders fine, the ShapeRenderer circle is where it's supposed to be after setting ShapeRenderer's projection matrix shapeRenderer.setProjectionMatrix(camera.combined) : enter image description here

But, i still don't understand how to check collision on this circle, when i do this in render method and press on the circle nothing is happening, like i don't get that log and i assume that's because the circle doesn't know the world scale (no nothing like setprojectionmatrix ) and render coordinates and where the actual circle "thinks" it is don't match. How do i check collision on that circle?

if(Gdx.input.justTouched()) {

    if(circle.contains(Gdx.input.getX(), Gdx.input.getY())) {

                    Gdx.app.log("SCREEN", "TOUCHED AND CONTAINS");
    }
}

EDIT2: I get it now, everything works, i just wasn't using camera.unproject on touch coordinates

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Imagine you have a device 1200 x 800 pixel big.

Now you say you will have a box2d rectangle on position: 100, 100 and size: 100,100

When you now use your SpriteBatch (Box2dDebugRenderer) to render this you will see a world 1200 x 800 units big. You must try to forget to think in pixels. Important is that the SpriteBatch always draw 1200 x 800 pixels but not always 1200 x 800 units, later more.

So this is what you see:

enter image description here

When we now use a Camera we say we will only our world of 600 x 400 units.
We create a Camera with size: 600 x 400 units!

camera = new OrthographicCamera(600, 400);

enter image description here

The batch still draw 1200 x 800 units and 1200 x 800 pixels

Now with batch.setProjectionMatrix(camera.combined); you give the batch a Matrix to calculate the size of a unit. With this Matrix he can calculate how big it musst draw 1 unit.
And after that the SpriteBatch draw 600 x 400 units but still 1200 x 800 pixels.
And then you see:

enter image description here

For this reason you can forget to think in pixels the Matrix of camera makes the calculation for you from pixel to units and you can focus on thinking in units.

The next thing why you must think in units is that box2D calculates in meters. So the rectangle is 100 x 100 meters big and 100 meter above the bottom. So by gravity of g = 9,81m/s the rectangle falls not so fast as you might expect.

The SpriteBatch always draw 1200 x 800 pixels otherwise you only see the game on the half of your screen. But with the Matrix of camera the batch know how many pixel he must draw 1 unit and so you see a World of 600 x 400 units on a Screen of 1200 x 800 pixels.

Attention: 1 unit is not always equal 1 meter. For example in a spaceship game there are for example 5 kilometre between two ships. So please don't create a World of 10000 x 10000 units because Box2d won't calculate that.
Then you can say 1 unit = 100 meter, so you have a World of 100 x 100 units. Now you must remember when a ship should be 200 m/s fast you must set body.setLinearVelocity(2,0) because 1 unit = 100 meter.

So always think in units!

Edit:
Sometimes we do not come around pixel for example Gdx.input
Gdx.input return the position in pixel.
Now our camera have two methods to convert from pos in pixel to pos of our units and vice versa.

float x = Gdx.input.getX();
float y = Gdx.input.getY();
camera.unproject(new Vector3(x, y, 0));//this converts a Vector3 position of pixels to a Vector3 position of units
camera.project(new Vector3(10,10,0));//this converts a Vector3 position of units to a Vector3 position of pixels

I hope I could help you


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...