使用 LibGDX 和 Box2d 绘制旋转的九个面片

问题描述 投票:0回答:1

我正在使用带有 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(如您在屏幕截图中看到的)。

这些是积木的旋转角度: brick's rotation angle

实际工作解决方案

我找到了这个可行的解决方案,但它导致 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); 
}

working with Matrix4

没有 Matrix4


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

Without Matrix4


既然批量刷新会影响性能,有没有更好的方法来实现这一点?

诗。我已经搜索并阅读了其他 SO 的答案和网络上有关类似问题的信息,但没有找到更好的解决方案。

非常感谢

java libgdx
1个回答
0
投票

绘制旋转的

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
认为的位置:

NinePatchDrawable centered rotation

上述动画的完整源代码:

    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();
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.