Libgdx 的世界单位

2022-01-12 00:00:00 java libgdx

我一直在尝试学习一些 libgdx,但现在我对那里的世界单位有点困惑,我什至不知道我应该问什么确切的问题.

无论如何,我一直在阅读这个示例游戏代码

但是,我仍然不明白如何检查这个圆圈上的碰撞,当我在渲染方法中执行此操作并按下圆圈时什么都没有发生,就像我没有得到那个日志,我认为那是因为圆圈不知道世界比例(没有像 setprojectionmatrix 这样的东西)和渲染坐标以及实际圆圈认为"它的位置不匹配.如何检查该圆上的碰撞?

if(Gdx.input.justTouched()) {if(circle.contains(Gdx.input.getX(), Gdx.input.getY())) {Gdx.app.log("屏幕", "触摸并包含");}}

我现在明白了,一切正常,我只是没有在触摸坐标上使用 camera.unproject

解决方案

假设你有一个 1200 x 800 像素大的设备.

现在您说您将在位置:100、100 和大小:100,100 上创建一个 box2d 矩形

当您现在使用 SpriteBatch (Box2dDebugRenderer) 渲染它时,您将看到一个 1200 x 800 单位大的世界.您必须尝试忘记以像素为单位进行思考.重要的是,SpriteBatch 始终绘制 1200 x 800 像素,但并不总是 1200 x 800 单位,以后会更多.

这就是你所看到的:

当我们现在使用相机时,我们会说我们的世界只有 600 x 400 单位.
我们创建一个尺寸为:600 x 400 units的相机!

camera = new OrthographicCamera(600, 400);

批次仍然绘制 1200 x 800 单位和 1200 x 800 像素

现在使用 batch.setProjectionMatrix(camera.combined); 你给批处理一个矩阵来计算一个单元的大小.有了这个矩阵,他可以计算出它必须画出多大的 1 unit.
之后,SpriteBatch 绘制 600 x 400 单位,但仍为 1200 x 800 像素.
然后你会看到:

因此,您可能会忘记以像素为单位思考,相机矩阵会为您从像素到单位进行计算,您可以专注于以单位思考.

接下来你必须以单位思考的原因是 box2D 以米为单位计算.所以矩形的大小为 100 x 100 米,底部高出 100 米.因此,在 g = 9,81m/s 的重力下,矩形下落的速度并没有您想象的那么快.

SpriteBatch 始终绘制 1200 x 800 像素,否则您只能在屏幕的一半上看到游戏.但是通过相机矩阵,批次知道他必须绘制 1 个单位的像素数,因此您会在 1200 x 800 像素的屏幕上看到 600 x 400 单位的世界.

注意:1 个单位并不总是等于 1 米.例如,在宇宙飞船游戏中,两艘船之间有 5 公里.所以请不要创建一个 10000 x 10000 单位的世界,因为 Box2d 不会计算.
然后你可以说 1 个单位 = 100 米,所以你有一个 100 x 100 个单位的世界.现在你必须记住,当一艘船应该快 200 m/s 时,你必须设置 body.setLinearVelocity(2,0) 因为 1 个单位 = 100 米.

所以总是以单位思考!


有时我们不会绕过像素,例如 Gdx.input
Gdx.input 以像素为单位返回位置.
现在我们的相机有两种方法可以将像素中的 pos 转换为我们单位的 pos,反之亦然.

float x = Gdx.input.getX();浮动 y = Gdx.input.getY();camera.unproject(new Vector3(x, y, 0));//这会将像素的 Vector3 位置转换为单位的 Vector3 位置camera.project(new Vector3(10,10,0));//这会将单位的 Vector3 位置转换为像素的 Vector3 位置

希望能帮到你

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) :

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

解决方案

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:

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);

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:

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

相关文章