我正在使用带有 Java 和 LibGDX 的 Android Studio。
我需要根据它们在 Box2d 世界中的位置正确绘制旋转的九个面片。
Spritebatch.draw
方法不能处理九个补丁,所以我必须使用NinePatch.draw
方法。
Box2d 使用中心作为原点来应用主体旋转。
问题在于
NinePatch.draw
围绕身体的左下角而不是围绕中心旋转对象(如 SpriteBatch.draw
那样),导致绘制位置不正确。对于带有 RevoluteJoint
和电机的物体,这种差异还会产生不正确的旋转。
请注意,我将砖块放置在世界中,将其中心作为原点,因此在
draw
方法中,我将执行 body.getPosition().x - widthInWorld / 2
。
game.getBatch()
返回使用 SpriteBatch
设置的 setProjectionMatrix(viewport.getCamera().combined);
,视口设置为 new FitViewport(WORLD_WIDTH, WORLD_HEIGHT);
砖块有一个 130w x 34h 像素的 ninepatch png(因此没有补丁边框的 128x32),它的世界大小是 4 x 1(如您在屏幕截图中看到的)。
我找到了这个可行的解决方案,但它导致 Android Studio 警告“可能在循环内刷新”(说得好,因为
batch.setTransformMatrix
在内部进行刷新。更多信息见这个问题)。
Matrix4 tempMatrix = new Matrix4();
Matrix4 originalMatrix = new Matrix4();
DelayedRemovalArray<Brick> bricks = ...;
bricks.begin();
for(Brick brick : bricks) {
brick.draw(game.getBatch());
}
bricks.end();
Brick.draw
方法:
NinePatchDrawable ninePatchDrawable = ...;
public void draw(SpriteBatch batch) {
originalMatrix.set(batch.getTransformMatrix());
tempMatrix.set(originalMatrix);
tempMatrix
.translate(widthInWorld/2, heightInWorld/2, 0)
.rotate(0, 0, angle, 10)
.translate(-widthInWorld/2, -heightInWorld/2, 0);
batch.setTransformMatrix(tempMatrix);
ninePatchDrawable.draw(
batch,
0,
0,
0,
0,
widthInWorld,
heightInWorld,
1f,
1f,
0
);
batch.setTransformMatrix(originalMatrix);
}
public void draw(SpriteBatch batch) {
ninePatchDrawable.draw(
batch,
body.getPosition().x - widthInWorld / 2,
body.getPosition().y - heightInWorld / 2,
0,
0,
widthInWorld,
heightInWorld,
1,
1,
body.getAngle() * MathUtils.radiansToDegrees
);
}
既然批量刷新会影响性能,有没有更好的方法来实现这一点?
诗。我已经搜索并阅读了其他 SO 的答案和网络上有关类似问题的信息,但没有找到更好的解决方案。
非常感谢
绘制旋转的
NinePatchDrawable
时,将原点调整为NinePatchDrawable
大小的一半。
public void render(SpriteBatch batch) {
position.set(body.getPosition());
float angle = body.getAngle() * MathUtils.radiansToDegrees;
npd.draw(batch,
position.x - 0.5f * size.x,
position.y - 0.5f * size.y,
0.5f * size.x,
0.5f * size.y,
size.x,
size.y,
1.0f,
1.0f,
angle);
}
这将确保旋转围绕
NinePatchDrawable
的中心进行,同时仍然匹配 Box2D Body
的旋转和位置。
在下面的示例中,我淡出
NinePatchDrawable
以显示 Box2DDebugRenderer
认为的位置:
上述动画的完整源代码:
public class SomeCoolGameAboutRotatedBricks extends Game {
public static class MyEntity {
private final Vector2 position = new Vector2();
private final Vector2 size = new Vector2();
private final NinePatchDrawable npd;
private final Body body;
public MyEntity(float px, float py, float w, float h, NinePatchDrawable npd, World world) {
this.size.set(w, h);
this.npd = npd;
var shape = new PolygonShape();
shape.setAsBox(w / 2.0f, h / 2.0f);
var fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
var bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.KinematicBody;
bodyDef.position.set(px, py);
body = world.createBody(bodyDef);
body.createFixture(fixtureDef);
shape.dispose();
}
public void spin(float rotation) {
body.setAngularVelocity(rotation);
}
public void render(SpriteBatch batch) {
position.set(body.getPosition());
float angle = body.getAngle() * MathUtils.radiansToDegrees;
npd.draw(batch,
position.x - 0.5f * size.x,
position.y - 0.5f * size.y,
0.5f * size.x,
0.5f * size.y,
size.x,
size.y,
1.0f,
1.0f,
angle);
}
}
private World world;
private Box2DDebugRenderer box2DDebugRenderer;
private SpriteBatch batch;
private OrthographicCamera camera;
private Array<MyEntity> entities = new Array<>();
@Override
public void create() {
world = new World(new Vector2(0, 0), false);
box2DDebugRenderer = new Box2DDebugRenderer();
// Camera setup with fixed world size, aspect-ratio of the window and positioned so that (0, 0) is center screen
var WORLD_WIDTH = 1000.0f;
var WORLD_HEIGHT = WORLD_WIDTH * Gdx.graphics.getHeight() / Gdx.graphics.getWidth();
camera = new OrthographicCamera(WORLD_WIDTH, WORLD_HEIGHT);
camera.position.set(0, 0, 1);
camera.update();
batch = new SpriteBatch();
var npd = /* Load this somehow */ Assets.instance.ui.buttonBackground;
entities.add(new MyEntity(-(WORLD_WIDTH * 0.25f), WORLD_HEIGHT * 0.25f, 100, 25.0f, npd, world));
entities.add(new MyEntity(-(WORLD_WIDTH * 0.00f), WORLD_HEIGHT * 0.25f, 100, 25.0f, npd, world));
entities.add(new MyEntity((WORLD_WIDTH * 0.25f), WORLD_HEIGHT * 0.25f, 100, 25.0f, npd, world));
entities.add(new MyEntity(-(WORLD_WIDTH * 0.25f), WORLD_HEIGHT * 0.00f, 100, 25.0f, npd, world));
entities.add(new MyEntity(-(WORLD_WIDTH * 0.00f), WORLD_HEIGHT * 0.00f, 100, 25.0f, npd, world));
entities.add(new MyEntity((WORLD_WIDTH * 0.25f), WORLD_HEIGHT * 0.00f, 100, 25.0f, npd, world));
entities.add(new MyEntity(-(WORLD_WIDTH * 0.25f), -WORLD_HEIGHT * 0.25f, 100, 25.0f, npd, world));
entities.add(new MyEntity(-(WORLD_WIDTH * 0.00f), -WORLD_HEIGHT * 0.25f, 100, 25.0f, npd, world));
entities.add(new MyEntity((WORLD_WIDTH * 0.25f), -WORLD_HEIGHT * 0.25f, 100, 25.0f, npd, world));
for (var entity : entities)
entity.spin(MathUtils.random(-4, 4));
}
float alphaTimer = 0.0f;
@Override
public void render() {
alphaTimer += Gdx.graphics.getDeltaTime();
float alpha = 0.5f + MathUtils.sin(alphaTimer * 2);
ScreenUtils.clear(Color.BLACK);
world.step(Gdx.graphics.getDeltaTime(), 8, 8);
box2DDebugRenderer.render(world, camera.combined);
batch.setProjectionMatrix(camera.combined);
batch.setColor(1, 1, 1, alpha);
batch.begin();
for (var entity : entities)
entity.render(batch);
batch.end();
}
}