英文:
LibGDX camera.project() is not working properly
问题
我使用Box2D来管理我的游戏世界。我想在物体上方显示文本。我计划通过适当设置Label来实现这一点。问题是,物体在游戏世界中,而Label在舞台(UI)中。所以我尝试使用*camera.project()方法将世界坐标转换为屏幕坐标。不幸的是,由于某种原因,我无法做到这一点 - Label显示在与目标位置不同的位置上 - 它相对于目标位置向下和向左偏移。调整窗口大小时,这个位置也会改变,但也是错误的。
我要补充说明的是,我在同样的方式下也使用了camera.unproject()*方法,这里没有任何问题 - 一切都正常工作。我不知道为什么反过来就不同了。
this.camera = new OrthographicCamera(ScreenManager.WIDTH * SCALE, ScreenManager.HEIGHT * SCALE);
this.viewport = new FitViewport(ScreenManager.WIDTH, ScreenManager.HEIGHT);
this.stage = new Stage(viewport, batch);
this.world = new World(Vector2.Zero, true);
// ...
Vector3 worldPosition = new Vector3(x, y, 0);
Vector3 screenPosition = camera.project(worldPosition,
viewport.getScreenX(), viewport.getScreenY(),
viewport.getScreenWidth(), viewport.getScreenHeight());
label.setPosition(screenPosition.x, screenPosition.y);
我的resize方法如下:
@Override
public void resize(int width, int height) {
viewport.update(width, height, true);
}
当然,在渲染方法中,我调用了Stage的*act()和draw()*方法。我做错了什么?
英文:
I use Box2D to manage my game world. I want to display text above the body. I plan to do this by setting the Label appropriately. The problem is that the body is in the game world and the Label is in the Stage (UI). So I tried to use the camera.project() method to convert world coordinates to screen coordinates. Unfortunately, for some reason I couldn't do it - the Label is displayed in a different place than it should be - it is shifted down and to the left relative to the target position. When resizing the window, this position also changes, but also incorrectly.
I will add that otherwise I use the camera.unproject() method in the same way and I have no problems here - everything works fine. I don't know why it's different the other way around.
this.camera = new OrthographicCamera(ScreenManager.WIDTH * SCALE, ScreenManager.HEIGHT * SCALE);
this.viewport = new FitViewport(ScreenManager.WIDTH, ScreenManager.HEIGHT);
this.stage = new Stage(viewport, batch);
this.world = new World(Vector2.Zero, true);
// ...
Vector3 worldPosition = new Vector3(x, y, 0);
Vector3 screenPosition = camera.project(worldPosition,
viewport.getScreenX(), viewport.getScreenY(),
viewport.getScreenWidth(), viewport.getScreenHeight());
label.setPosition(screenPosition.x, screenPosition.y);
And my resize method:
@Override
public void resize(int width, int height) {
viewport.update(width, height, true);
}
Of course I call act() and draw() for Stage in the render method. What am I doing wrong?
答案1
得分: 1
为了将World
坐标转换为Stage
坐标,你需要首先将World
位置投影到屏幕空间,然后再将其反投影回*Stage
空间*。
Vector2 worldPosition = body.getPosition();
Vector3 screenPosition = worldCamera.project(new Vector3(worldPosition.x, worldPosition.y, 1.0f));
Vector3 stagePosition = stage.getCamera().unproject(screenPosition);
label.setPosition(stagePosition.x, stage.getHeight() - stagePosition.y)); // stage.getHeight() - because UP is flipped.
这个过程将允许你跟踪一个Box2D的Body
并设置一个Label
的位置以匹配它:
上面示例的完整源代码如下,它使用了libGDX测试中的默认字体(font和font image)。
package com.bornander.sandbox;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.*;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.utils.ScreenUtils;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
public class MyGdxSandbox extends ApplicationAdapter {
World world;
OrthographicCamera worldCamera;
Box2DDebugRenderer box2DDebugRenderer;
Body ballBody;
Stage stage;
Label label;
@Override
public void create () {
world = new World(new Vector2(0.0f, -10.0f), false);
float aspectRatio = (float)Gdx.graphics.getWidth() / Gdx.graphics.getHeight();
float worldCameraViewportWidth = 100.0f;
worldCamera = new OrthographicCamera(worldCameraViewportWidth, worldCameraViewportWidth / aspectRatio);
worldCamera.position.set(worldCamera.viewportWidth / 2.0f, worldCamera.viewportHeight / 2.0f, 1.0f);
box2DDebugRenderer = new Box2DDebugRenderer();
CircleShape ballShape = new CircleShape();
ballShape.setRadius(4.0f);
FixtureDef ballFixtureDef = new FixtureDef();
ballFixtureDef.shape = ballShape;
ballFixtureDef.friction = 0.2f;
ballFixtureDef.density = 1.0f;
ballFixtureDef.restitution = 0.65f;
BodyDef ballBodyDef = new BodyDef();
ballBodyDef.type = BodyDef.BodyType.DynamicBody;
ballBody = world.createBody(ballBodyDef);
ballBody.createFixture(ballFixtureDef);
ballBody.setTransform(25, 50, 0);
ballBody.applyLinearImpulse(200, 0, ballBody.getPosition().x, ballBody.getPosition().y, true);
PolygonShape groundShape = new PolygonShape();
groundShape.setAsBox(25, 2);
FixtureDef groundFixtureDef = new FixtureDef();
groundFixtureDef.shape = groundShape;
groundFixtureDef.friction = 0.2f;
groundFixtureDef.density = 1.0f;
groundFixtureDef.restitution = 0.2f;
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.type = BodyDef.BodyType.StaticBody;
Body ground = world.createBody(groundBodyDef);
ground.createFixture(groundFixtureDef);
ground.setTransform(50, 20, 0);
stage = new Stage(new ScreenViewport());
stage.setDebugAll(true);
label = new Label("TEXT", new Label.LabelStyle(new BitmapFont(Gdx.files.internal("default.fnt")), Color.WHITE));
label.setPosition(10, 10);
stage.addActor(label);
}
@Override
public void render () {
ScreenUtils.clear(0, 0, 0, 1);
float delta = Gdx.graphics.getDeltaTime();
worldCamera.update();
world.step(delta, 8, 8);
Vector2 ballPositionWorld = ballBody.getPosition();
Vector3 ballScreenPosition = worldCamera.project(new Vector3(ballPositionWorld.x, ballPositionWorld.y, 1.0f));
Vector3 ballStagePosition = stage.getCamera().unproject(ballScreenPosition);
label.setText(String.format("(%.3f, %.3f)", ballPositionWorld.x, ballPositionWorld.y));
label.pack();
label.setPosition(ballStagePosition.x, stage.getHeight() - ballStagePosition.y);
box2DDebugRenderer.render(world, worldCamera.combined);
stage.act();
stage.draw();
}
}
英文:
In order to translate from World
-coordinates to Stage
-coordinates you need to first project the World
position onto screen-space, and then unproject it back into Stage
-space.
Vector2 worldPosition = body.getPosition();
Vector3 screenPosition = worldCamera.project(new Vector3(worldPosition.x, worldPosition.y, 1.0f));
Vector3 stagePosition = stage.getCamera().unproject(screenPosition);
label.setPosition(stagePosition.x, stage.getHeight() - stagePosition.y)); // stage.getHeight() - because UP is flipped.
That process will allow you to track a Box2D Body
and set a Label
s position to match it:
Full source for the example above is included below, it uses the default font from the libGDX tests (font and the font image).
package com.bornander.sandbox;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.*;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.utils.ScreenUtils;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
public class MyGdxSandbox extends ApplicationAdapter {
World world;
OrthographicCamera worldCamera;
Box2DDebugRenderer box2DDebugRenderer;
Body ballBody;
Stage stage;
Label label;
@Override
public void create () {
world = new World(new Vector2(0.0f, -10.0f), false);
float aspectRatio = (float)Gdx.graphics.getWidth() / Gdx.graphics.getHeight();
float worldCameraViewportWidth = 100.0f;
worldCamera = new OrthographicCamera(worldCameraViewportWidth, worldCameraViewportWidth / aspectRatio);
worldCamera.position.set(worldCamera.viewportWidth / 2.0f, worldCamera.viewportHeight / 2.0f, 1.0f);
box2DDebugRenderer = new Box2DDebugRenderer();
CircleShape ballShape = new CircleShape();
ballShape.setRadius(4.0f);
FixtureDef ballFixtureDef = new FixtureDef();
ballFixtureDef.shape = ballShape;
ballFixtureDef.friction = 0.2f;
ballFixtureDef.density = 1.0f;
ballFixtureDef.restitution = 0.65f;
BodyDef ballBodyDef = new BodyDef();
ballBodyDef.type = BodyDef.BodyType.DynamicBody;
ballBody = world.createBody(ballBodyDef);
ballBody.createFixture(ballFixtureDef);
ballBody.setTransform(25, 50, 0);
ballBody.applyLinearImpulse(200, 0, ballBody.getPosition().x, ballBody.getPosition().y, true);
PolygonShape groundShape = new PolygonShape();
groundShape.setAsBox(25, 2);
FixtureDef groundFixtureDef = new FixtureDef();
groundFixtureDef.shape = groundShape;
groundFixtureDef.friction = 0.2f;
groundFixtureDef.density = 1.0f;
groundFixtureDef.restitution = 0.2f;
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.type = BodyDef.BodyType.StaticBody;
Body ground = world.createBody(groundBodyDef);
ground.createFixture(groundFixtureDef);
ground.setTransform(50, 20, 0);
stage = new Stage(new ScreenViewport());
stage.setDebugAll(true);
label = new Label("TEXT", new Label.LabelStyle(new BitmapFont(Gdx.files.internal("default.fnt")), Color.WHITE));
label.setPosition(10, 10);
stage.addActor(label);
}
@Override
public void render () {
ScreenUtils.clear(0, 0, 0, 1);
float delta = Gdx.graphics.getDeltaTime();
worldCamera.update();
world.step(delta, 8, 8);
Vector2 ballPositionWorld = ballBody.getPosition();
Vector3 ballScreenPosition = worldCamera.project(new Vector3(ballPositionWorld.x, ballPositionWorld.y, 1.0f));
Vector3 ballStagePosition = stage.getCamera().unproject(ballScreenPosition);
label.setText(String.format("(%.3f, %.3f)", ballPositionWorld.x, ballPositionWorld.y));
label.pack();
label.setPosition(ballStagePosition.x, stage.getHeight() - ballStagePosition.y);
box2DDebugRenderer.render(world, worldCamera.combined);
stage.act();
stage.draw();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论