admin管理员组文章数量:1026343
I'm using Android Studio with Java and LibGDX.
I need to draw the rotated ninepatches correctly, based on their position in the Box2d world.
The Spritebatch.draw
method does not handle ninepatches so I have to use NinePatch.draw
method.
Box2d uses the center as the origin to apply body rotation.
The problem is that NinePatch.draw
rotates the object around the body's lower left corner instead of around the center (as SpriteBatch.draw
does), resulting in an incorrect drawing position. This difference also produces an incorrect rotation for bodies with a RevoluteJoint
and a motor.
Note that I place the bricks in the world considering their center as the origin, so in the draw
method I'll do body.getPosition().x - widthInWorld / 2
.
game.getBatch()
returns a SpriteBatch
set with setProjectionMatrix(viewport.getCamera()bined);
and the viewport is set as new FitViewport(WORLD_WIDTH, WORLD_HEIGHT);
The brick has a ninepatch png of 130w x 34h pixels (so 128x32 without the patch borders) and its world size is 4 x 1 (as you can see in the screenshots).
These are the rotation angles of the bricks:
Actual working solution
I found this working solution but it causes Android Studio to warn "Possible flush inside a loop" (well said because batch.setTransformMatrix
does a flush internally. More info in this SO question).
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
method:
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);
}
Without 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
);
}
Since batch flush affects performance, is there a better way to achieve this?
Ps. I've already searched and read other SO's answers and web regarding similar issues but haven't found any better solution.
Thanks a lot
I'm using Android Studio with Java and LibGDX.
I need to draw the rotated ninepatches correctly, based on their position in the Box2d world.
The Spritebatch.draw
method does not handle ninepatches so I have to use NinePatch.draw
method.
Box2d uses the center as the origin to apply body rotation.
The problem is that NinePatch.draw
rotates the object around the body's lower left corner instead of around the center (as SpriteBatch.draw
does), resulting in an incorrect drawing position. This difference also produces an incorrect rotation for bodies with a RevoluteJoint
and a motor.
Note that I place the bricks in the world considering their center as the origin, so in the draw
method I'll do body.getPosition().x - widthInWorld / 2
.
game.getBatch()
returns a SpriteBatch
set with setProjectionMatrix(viewport.getCamera()bined);
and the viewport is set as new FitViewport(WORLD_WIDTH, WORLD_HEIGHT);
The brick has a ninepatch png of 130w x 34h pixels (so 128x32 without the patch borders) and its world size is 4 x 1 (as you can see in the screenshots).
These are the rotation angles of the bricks:
Actual working solution
I found this working solution but it causes Android Studio to warn "Possible flush inside a loop" (well said because batch.setTransformMatrix
does a flush internally. More info in this SO question).
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
method:
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);
}
Without 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
);
}
Since batch flush affects performance, is there a better way to achieve this?
Ps. I've already searched and read other SO's answers and web regarding similar issues but haven't found any better solution.
Thanks a lot
Share Improve this question edited Nov 17, 2024 at 15:10 user2342558 asked Nov 17, 2024 at 15:05 user2342558user2342558 6,6456 gold badges41 silver badges64 bronze badges1 Answer
Reset to default 2Adjust the origin when drawing the rotated NinePatchDrawable
by half the size of the 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);
}
That will make sure the rotation is about the center of the NinePatchDrawable
whilst still matching the rotation and position of the Box2D Body
.
In the example below I am fading out the NinePatchDrawable
to show what the Box2DDebugRenderer
considers the position to be:
Full source code for the animation above:
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, camerabined);
batch.setProjectionMatrix(camerabined);
batch.setColor(1, 1, 1, alpha);
batch.begin();
for (var entity : entities)
entity.render(batch);
batch.end();
}
}
I'm using Android Studio with Java and LibGDX.
I need to draw the rotated ninepatches correctly, based on their position in the Box2d world.
The Spritebatch.draw
method does not handle ninepatches so I have to use NinePatch.draw
method.
Box2d uses the center as the origin to apply body rotation.
The problem is that NinePatch.draw
rotates the object around the body's lower left corner instead of around the center (as SpriteBatch.draw
does), resulting in an incorrect drawing position. This difference also produces an incorrect rotation for bodies with a RevoluteJoint
and a motor.
Note that I place the bricks in the world considering their center as the origin, so in the draw
method I'll do body.getPosition().x - widthInWorld / 2
.
game.getBatch()
returns a SpriteBatch
set with setProjectionMatrix(viewport.getCamera()bined);
and the viewport is set as new FitViewport(WORLD_WIDTH, WORLD_HEIGHT);
The brick has a ninepatch png of 130w x 34h pixels (so 128x32 without the patch borders) and its world size is 4 x 1 (as you can see in the screenshots).
These are the rotation angles of the bricks:
Actual working solution
I found this working solution but it causes Android Studio to warn "Possible flush inside a loop" (well said because batch.setTransformMatrix
does a flush internally. More info in this SO question).
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
method:
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);
}
Without 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
);
}
Since batch flush affects performance, is there a better way to achieve this?
Ps. I've already searched and read other SO's answers and web regarding similar issues but haven't found any better solution.
Thanks a lot
I'm using Android Studio with Java and LibGDX.
I need to draw the rotated ninepatches correctly, based on their position in the Box2d world.
The Spritebatch.draw
method does not handle ninepatches so I have to use NinePatch.draw
method.
Box2d uses the center as the origin to apply body rotation.
The problem is that NinePatch.draw
rotates the object around the body's lower left corner instead of around the center (as SpriteBatch.draw
does), resulting in an incorrect drawing position. This difference also produces an incorrect rotation for bodies with a RevoluteJoint
and a motor.
Note that I place the bricks in the world considering their center as the origin, so in the draw
method I'll do body.getPosition().x - widthInWorld / 2
.
game.getBatch()
returns a SpriteBatch
set with setProjectionMatrix(viewport.getCamera()bined);
and the viewport is set as new FitViewport(WORLD_WIDTH, WORLD_HEIGHT);
The brick has a ninepatch png of 130w x 34h pixels (so 128x32 without the patch borders) and its world size is 4 x 1 (as you can see in the screenshots).
These are the rotation angles of the bricks:
Actual working solution
I found this working solution but it causes Android Studio to warn "Possible flush inside a loop" (well said because batch.setTransformMatrix
does a flush internally. More info in this SO question).
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
method:
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);
}
Without 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
);
}
Since batch flush affects performance, is there a better way to achieve this?
Ps. I've already searched and read other SO's answers and web regarding similar issues but haven't found any better solution.
Thanks a lot
Share Improve this question edited Nov 17, 2024 at 15:10 user2342558 asked Nov 17, 2024 at 15:05 user2342558user2342558 6,6456 gold badges41 silver badges64 bronze badges1 Answer
Reset to default 2Adjust the origin when drawing the rotated NinePatchDrawable
by half the size of the 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);
}
That will make sure the rotation is about the center of the NinePatchDrawable
whilst still matching the rotation and position of the Box2D Body
.
In the example below I am fading out the NinePatchDrawable
to show what the Box2DDebugRenderer
considers the position to be:
Full source code for the animation above:
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, camerabined);
batch.setProjectionMatrix(camerabined);
batch.setColor(1, 1, 1, alpha);
batch.begin();
for (var entity : entities)
entity.render(batch);
batch.end();
}
}
本文标签: javaDraw rotated ninepatches with LibGDX and Box2dStack Overflow
版权声明:本文标题:java - Draw rotated ninepatches with LibGDX and Box2d - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745634989a2160391.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论