Libgdx box2d body overlaps with sprite - java

I have been puzzling my mind with the code and I've made a huge amount of progress from not doing pixel to meter to now having a body and sprite almost in line however ive reached another point where i can't seem to figure out where I have gone wrong.
As you can see from the image the body is off from the actual sprite, I have a hunch that it might be origin as it the difference is very slight but I can't seem to work out how to get it in line.
My Create method
public class Physics1 extends ApplicationAdapter implements InputProcessor {
SpriteBatch batch;
Sprite sprite;
Texture img;
World world;
Body body;
Box2DDebugRenderer debugRenderer;
Matrix4 debugMatrix;
OrthographicCamera camera;
Vector2 bodyOrigin;
float torque = 0.0f;
boolean drawSprite = true;
final float PIXELS_TO_METERS = 100f;
final float WORLD_WIDTH =100;
final float WORLD_HEIGHT=100;
#Override
public void create() {
Assets.instance.init(new AssetManager());
batch = new SpriteBatch();
bodyOrigin = new Vector2();
sprite = new Sprite();
sprite.setRegion(Assets.instance.tyre.tyre);
sprite.setSize(12,12);
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
sprite.setPosition(50-sprite.getWidth()/2,25);
Gdx.app.log("Physics1", "Sprite positions"+ -sprite.getWidth()/2+ " ,"+ -sprite.getHeight()/2);
world = new World(new Vector2(0, 0f),true);
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
BodyEditorLoader load = new BodyEditorLoader(Gdx.files.internal("levels/pittstop.json"));
bodyDef.position.set(sprite.getX()/PIXELS_TO_METERS,sprite.getY()/PIXELS_TO_METERS);
Gdx.app.log("Physics1", "Body positions calculations"+ sprite.getX() +" "+ sprite.getWidth()/2);
Gdx.app.log("Physics1", "Body positions"+ bodyDef.position);
body = world.createBody(bodyDef);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.density = 0.1f;
fixtureDef.friction = 0.0f;
fixtureDef.restitution = 0.0f;
float scale =0.2f;
load.attachFixture(body, "tyre", fixtureDef, scale);
Gdx.app.log("Physics1"," Orgin of body" +load.getOrigin("tyre", scale).cpy());
bodyOrigin = load.getOrigin("tyre", scale).cpy();
body.setUserData("tyre");
Gdx.input.setInputProcessor(this);
debugRenderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(WORLD_WIDTH,WORLD_HEIGHT);
Gdx.app.log("Physics1", "camera "+ camera.viewportWidth+" "+camera.viewportHeight);
camera.position.set(WORLD_WIDTH / 2f, WORLD_HEIGHT / 2f, 0);
Gdx.app.log("Physics1", "camera "+ camera.viewportWidth+" "+camera.viewportHeight);
}
My render method
public void render() {
camera.update();
world.step(1f/60f, 6, 2);
body.applyTorque(torque,true);
sprite.setPosition((body.getPosition().x * PIXELS_TO_METERS),
(body.getPosition().y * PIXELS_TO_METERS))
;
sprite.setRotation((float)Math.toDegrees(body.getAngle()));
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
debugMatrix = batch.getProjectionMatrix().cpy().scale(PIXELS_TO_METERS,
PIXELS_TO_METERS, 0);
batch.begin();
if(drawSprite)
batch.draw(sprite, sprite.getX(), sprite.getY(),bodyOrigin.x,
bodyOrigin.y,
sprite.getWidth(),sprite.getHeight(),sprite.getScaleX(),sprite.
getScaleY(),sprite.getRotation());
batch.end();
debugRenderer.render(world, debugMatrix);
}

False alarm, turns out in physics editor you have the opportunity to change the origin of the body you are drawing up, just by clicking around I was able to move the origin to the centre and there you have it.

Related

Using regular libgdx shapes with box2d shapes

Before I decided that I wanted to use box2d for all my game physics, I had set up all the non-animated shapes using shaperenderer. I have converted my box2d world and shape to meters at a scale of 32 ppm but all my regular shapes are still drawn with pixels.
Since my box2d circle has been scaled along with the camera, it renders fine with the right size. However, all the shapes that I drew with pixels using shaperenderer are way out of view.
Do I need to redo all of my original shapes with box2d shapes?
Can I just scale all the original shapes with my PPM scaling?
From what I understand, I should be able to just scale all the dimensions of the original shapes by the PPM since I have already scaled the camera?
public class Controller extends Game {
public OrthographicCamera cam;
public ShapeRenderer shape;
public ShapeRenderer circle;
public FitViewport viewport;
public Stage stage;
public World world;
public Body ball;
public Box2DDebugRenderer box2DDebugRenderer;
#Override
public void create() {
cam = new OrthographicCamera();
viewport = new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), cam);
cam.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
shape = new ShapeRenderer();
circle = new ShapeRenderer(); //balls shot from cannon
world = new World(new Vector2(0, 0), true);
box2DDebugRenderer = new Box2DDebugRenderer();
setScreen(new GameScreen(this));
//setScreen(new TitleScreen(this));
}
#Override
public void dispose() {
shape.dispose();
stage.dispose();
circle.dispose();
box2DDebugRenderer.dispose();
world.dispose();
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void resize(int width, int height) {
viewport.update(width, height);
}
public Body createBall(){
Body bBody;
BodyDef def = new BodyDef();
def.type = BodyDef.BodyType.DynamicBody;
def.position.set(500,500);
def.fixedRotation = true;
bBody = world.createBody(def);
return bBody;
}
}
And here is is the actual game screen that implements all the original shapes and the box2d shapes:
import static com.mygdx.game.Utils.Constants.PPM;
public class GameScreen extends ScreenAdapter {
Controller game;
//x-axis length for top/bottom bar
private float goalWidth;
//y-axis height for back bar
private float goalHeight;
private float goalPostThickness;
//Screen height and width
private float screenWidth;
private float screenHeight;
//How far down/up posts are from edge of screen
private float goalPostTopOffset;
private float goalPostBottomOffset;
private float cannonOriginX; //Variables used for starting position of balls
private float cannonOriginY;
float ballX;
float ballY;
public GameScreen (Controller game){
this.game = game;
}
#Override
public void show(){
game.ball = game.createBall();
// Create a circle shape and set its radius to 6
CircleShape circle = new CircleShape();
circle.setRadius(32/2/PPM);
// Create a fixture definition to apply our shape to
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = 0.5f;
fixtureDef.friction = 0.4f;
fixtureDef.restitution = 0.6f; // Make it bounce a little bit
// Create our fixture and attach it to the body
Fixture fixture = game.ball.createFixture(fixtureDef);
circle.dispose();
}
#Override
public void render(float delta){
//Logic
screenWidth = Gdx.graphics.getWidth();
screenHeight = Gdx.graphics.getHeight();
goalPostTopOffset = screenHeight/7;
goalPostBottomOffset = goalPostTopOffset * 3;
goalHeight = screenHeight - (goalPostTopOffset + goalPostBottomOffset);
goalWidth = screenWidth / 6;
goalPostThickness = screenWidth / 75;
cannonOriginX = goalWidth / 2; //Variables used for starting position of balls
cannonOriginY = (goalPostThickness*5) / 2;
ballX = 0 + cannonOriginX;
ballY = (goalPostBottomOffset - (goalPostBottomOffset / 4)) + cannonOriginY;
game.world.step(1/60f, 6, 2);
//Draw
game.cam.update();
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.box2DDebugRenderer.render(game.world, game.cam.combined.scl(PPM));
game.shape.setProjectionMatrix(game.cam.combined);
drawNonAnimated();
drawAnimated();
}
#Override
public void hide(){
}
//draws stationary objects such as goal posts
public void drawNonAnimated(){
//Top goal bar
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(screenWidth - goalWidth, screenHeight - goalPostTopOffset, goalWidth, goalPostThickness);
game.shape.end();
//Bottom goal bar
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(screenWidth - goalWidth, goalPostBottomOffset, goalWidth, goalPostThickness);
game.shape.end();
//Back goal bar
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(screenWidth - goalPostThickness, goalPostBottomOffset, goalPostThickness, goalHeight);
game.shape.end();
//Cannon platform
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(0, goalPostBottomOffset - (goalPostBottomOffset / 4),goalWidth, goalPostThickness*2);
game.shape.end();
//Cannon
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(0, goalPostBottomOffset - (goalPostBottomOffset / 4), cannonOriginX,
cannonOriginY, goalWidth, goalPostThickness*5, 1, 1, 45);
game.shape.end();
}
public void drawAnimated(){
//Ball(s)
game.circle.setColor(1, 1, 1, 1);
game.circle.begin(ShapeRenderer.ShapeType.Filled);
game.circle.circle(ballX, ballY, goalPostThickness * 2.5f);
game.circle.end();
game.cam.update();
ballX += 1;
}
}
As I mentioned earlier, the circle shape rendered with box2d shows fine with the PPM scale. However, all the shapes that I drew with shaperenderer are off the screen.

Box2DBody not rendering LibGDX

I am trying to render a body, but it is not working. Other bodies render but this one won't. Here is the code in the Core class.
#Override
public void create () {
cam = new OrthographicCamera();
cam.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
b2dCam = new OrthographicCamera();
b2dCam.setToOrtho(false, Gdx.graphics.getWidth() / PPM, Gdx.graphics.getHeight() / PPM);
world = new World(new Vector2(0, -9.81f), true);
rend = new Box2DDebugRenderer();
batch = new SpriteBatch();
entity = new CircleEntity(world, 100, 200, 10, BodyType.DynamicBody);
entity.initBodyVariables(.1f, .1f, .1f);
//entity.attachSprite("badlogic.jpg");
BodyFactory.createBox(world, -1, 1f, 10000, .01f, BodyType.StaticBody, 1, 0, 0, "");
RopeJoint j = new RopeJoint(200,300, world);
j.create(4, 30);
}
#Override
public void render () {
b2dCam.update();
cam.update();
world.step(1/60f, 6, 2);
entity.update();
batch.setProjectionMatrix(b2dCam.combined);
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
rend.render(world, b2dCam.combined);
batch.begin();
entity.render(batch);
batch.end();
}
Here is the code for the bodyFactory:
public static Body createBox(World world, float x, float y, float width,
float height, BodyType type, float density, float friction, float restitution, Object data){
bodyDef = new BodyDef();
bodyDef.type = type;
bodyDef.position.set(new Vector2(x, y));
body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape();
shape.setAsBox(width / Core.PPM, height / Core.PPM);
fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
fixture = body.createFixture(fixtureDef);
fixture.setUserData(data);
shape.dispose();
return body;
}
When creating the box using this code, it renders it. I tried creating another body like this (in the CircleEntity class):
public class CircleEntity extends EntityParent {
/**
*
* #param world
* of game
* #param x
* position
* #param y
* position
* #param type
* of body (static, kinematic, dynamic)
*/
public CircleEntity(World world, float x, float y, float radius, BodyType type) {
super(world, x, y, radius, type);
createBody();
}
public void createBody() {
bodyDef = new BodyDef();
bodyDef.type = type;
bodyDef.position.set(position);
body = world.createBody(bodyDef);
CircleShape entityShape = new CircleShape();
entityShape.setRadius(radius / Core.PPM);
fixtureDef = new FixtureDef();
fixtureDef.shape = entityShape;
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
fixture = body.createFixture(fixtureDef);
entityShape.dispose();
}
#Override
public void attachSprite(String filePath) {
super.attachSprite(filePath);
if (entitySprite != null) {
fixture.setUserData(entitySprite);
body.setUserData(entitySprite);
entitySprite.setPosition(position.x - dimensions.x, position.y - dimensions.y);
entitySprite.setSize(dimensions.x, dimensions.y);
}
}
#Override
public void update() {
super.update();
if (entitySprite != null)
entitySprite.setPosition(position.x - dimensions.x, position.y - dimensions.y);
if (Gdx.input.isKeyPressed(Keys.D))
body.applyForce(10, 0, position.x, position.y, true);
if (Gdx.input.isKeyPressed(Keys.A))
body.applyForce(-10, 0, position.x, position.y, true);
}
#Override
public void render(SpriteBatch batch) {
if (entitySprite != null)
entitySprite.draw(batch);
}
}
When I call this(I called it in the first set of code) it doesn't work. I can't figure out why only this entity won't render the circle. The collision still works with the body, so it is in the world but the circle itself I can't see. Any help with this is extremely appreciated.
To be clear, the Box2DBody isn't rendering, the Sprite is irrelevant.

libGDX orthographic camera view size (with Box2D)

I have next easy class with one ball and four walls around screen:
SpriteBatch batch;
Box2DDebugRenderer debugRenderer;
World world;
OrthographicCamera camera;
public float VIRTUAL_WIDTH = 720f;
public float VIRTUAL_HEIGHT = 1280f;
#Override
public void create () {
batch = new SpriteBatch();
camera = new OrthographicCamera(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
world = new World(new Vector2(0, -9), true);
debugRenderer = new Box2DDebugRenderer();
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
CircleShape circleShape = new CircleShape();
circleShape.setRadius(50);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circleShape;
fixtureDef.restitution = 1;
Body body = world.createBody(bodyDef);
body.createFixture(fixtureDef);
createWall(0f, -VIRTUAL_HEIGHT/2, VIRTUAL_WIDTH/2, 30f);
createWall(0f, VIRTUAL_HEIGHT/2, VIRTUAL_WIDTH/2, 30f);
createWall(-VIRTUAL_WIDTH/2, 0f, 30f, VIRTUAL_HEIGHT/2);
createWall(VIRTUAL_WIDTH/2, 0f, 30f, VIRTUAL_HEIGHT/2);
}
private void createWall(float x, float y, float hx, float hy){
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.StaticBody;
bodyDef.position.set(x, y);
PolygonShape polygonShape = new PolygonShape();
polygonShape.setAsBox(hx, hy);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = polygonShape;
world.createBody(bodyDef).createFixture(fixtureDef);
}
#Override
public void render () {
camera.update();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
world.step(1 / 60.0f, 8, 3);
batch.setProjectionMatrix(camera.combined);
batch.begin();
debugRenderer.render(world, camera.combined);
batch.end();
}
And it's do next screen on my device (maybe not perfect screenshot):
The screen is slightly shifted to the left, it's because rounding errors?
UPDATE:
Instead of this
public float VIRTUAL_WIDTH = 720f;
public float VIRTUAL_HEIGHT = 1280f;
Try this
public float VIRTUAL_WIDTH = Gdx.graphics.getWidth();
public float VIRTUAL_HEIGHT = Gdx.graphics.getHeight();

libGDX Box2DDebugRenderer Draw Box Too Big & Too Small

So, I just want to draw Box2DDebugRenderer same with sprite size.
I have use same SpriteBatch same Camera and same Viewport. It took me 8 hours, tried finding a solution around google and still not solve the problem.
Here what I got:
I just change this line:
PolygonShape shape = new PolygonShape();
shape.setAsBox(32, 32);
Some tutorial said to div by 2. I tried that I got result like 2nd picture.
Here my script
PlayScreen.java:
public class PlayScreen implements Screen {
private SpriteBatch batch;
private TiledMap tileMap;
private TiledMapRenderer tiledMapRenderer;
private OrthographicCamera cam;
private Player player;
private World world;
private Box2DDebugRenderer debugRenderer;
private FitViewport gamePort;
public PlayScreen(HookaHookaGame game) {
this.batch = game.getSpriteBatch();
this.world = new World(new Vector2(0, -20), true);
// Create cam
cam = new OrthographicCamera();
gamePort = new FitViewport(800, 600, cam);
gamePort.apply(true);
//initially set our gamcam to be centered correctly at the start of of map
// cam.position.set(400, 300, 0);
// cam.update();
// Load tilemap
tileMap = new TmxMapLoader().load("simulation01.tmx");
tiledMapRenderer = new OrthogonalTiledMapRenderer(tileMap, batch);
// Create box2d debug renderer
debugRenderer = new Box2DDebugRenderer();
// Create player sprite
player = new Player(this.world);
}
#Override
public void show() {
}
#Override
public void render(float delta) {
//cam.update();
// Update sprite
player.update(delta);
//Clear the game screen with Black
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Draw tilemap
tiledMapRenderer.setView(cam);
tiledMapRenderer.render();
// Set camera to spritebatch
batch.setProjectionMatrix(cam.combined);
// Draw sprite
batch.begin();
batch.draw(player.getKeyFrame(), 300, 300);
player.draw(batch);
batch.end();
// Draw box2d debug renderer
debugRenderer.render(world, cam.combined);
}
#Override
public void resize(int width, int height) {
gamePort.update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
}
Player.java, extended from Sprite Class:
public class Player extends Sprite {
private Animation anim;
private float stateTimer;
private World world;
private Body body;
// Debug
public Player(World world) {
stateTimer = 0;
this.world = world;
loadAnim();
}
private void loadAnim() {
Array<TextureRegion> temp = new Array<TextureRegion>(40);
Texture texture = new Texture("sprite.png");
temp.add(new TextureRegion(texture, 3*32, 3*32, 32, 32));
temp.add(new TextureRegion(texture, 2*32, 3*32, 32, 32));
temp.add(new TextureRegion(texture, 32, 3 * 32, 32, 32));
temp.add(new TextureRegion(texture, 0, 3*32, 32, 32));
anim = new Animation(0.1f, temp, Animation.PlayMode.LOOP);
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(100, 100);
bodyDef.type = BodyDef.BodyType.DynamicBody;
body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape();
shape.setAsBox(32, 32);
FixtureDef fixture = new FixtureDef();
fixture.shape = shape;
body.createFixture(fixture);
setBounds(0, 0, 32, 32);
setPosition(100, 100);
}
public void update(float delta) {
stateTimer += delta;
setRegion(getKeyFrame());
setSize(32, 32);
}
public TextureRegion getKeyFrame() {
return anim.getKeyFrame(stateTimer, true);
}
}
Could you explain what happen exactly?
Box2d and Textures have different origins.
The origin of the body is its center.
The origin of the Texture is the bottom left corner.
As you can see, the center of the box2d object is exactly at the bottom left corner of the texture, if you draw them both at the same position.
Pseudo code:
batch.draw(texture, body.x - texture.width / 2, body.y - texture.heigth / 2);
Otherwise you could set the origin of the box2d body to "the bottom left corner", but that might give you trouble if you follow other tutorials.
you can also put the origin of your sprite to center by calling the function setOriginCenter() of your Sprites before drawing them
so the spritebatch will draw them from center just like your box

Box2DDebugRenderer and SpriteBatch misplaced

I am playing with libgdx for making yet another physics game :) and I have found something weird. Namely I use SpriteBatch for rendering images at the same time with Box2DDebugRenderer for debuging.
But when the physics acts, they appear to be misplaced. I wrote:
public class Canon implements ApplicationListener {
private OrthographicCamera camera;
private Box2DDebugRenderer debugRenderer;
/...
public void create() {
camera = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
world = new World(new Vector2(0f, -9.8f), true);
camera.position.set(CAMERA_WIDTH/2, CAMERA_HEIGHT/2, 0f);
camera.update();
debugRenderer = new Box2DDebugRenderer();
spriteBatch = new SpriteBatch();
//Create a canon. A rectangle :)
bd = new BodyDef();
fd = new FixtureDef(); fd.density = 1;
PolygonShape ps = new PolygonShape();
// Cannon
bd.type = BodyDef.BodyType.StaticBody;
bd.position.set(new Vector2(8, 5));
ps.setAsBox(5f, 1f);
cannonBody = world.createBody(bd);
fd.shape = ps;
cannonBody.createFixture(fd);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
debugRenderer.render(world, camera.combined);
world.step(BOX_STEP, BOX_VELOCITY_ITERATIONS, BOX_POSITION_ITERATIONS);
spriteBatch.begin();
Sprite s = (Sprite)targetBody1.getUserData();
spriteBatch.draw(s.getTexture(),
(targetBody1.getPosition().x - bodyWidth/2)*ppuX, (targetBody1.getPosition().y - bodyheight/2)*ppuY,
0f, 0f, bodyWidth*ppuX, bodyheight*ppuY, 1f, 1f, radToGrad*targetBody1.getAngle(), 0, 0, s.getTexture().getWidth(), s.getTexture().getHeight(), false, false);
spriteBatch.end();
}
}
And here's how it looks thereafter
Any ideas?
Thanks!
I found it. This is due to the fact rotations in OpenGL are done around the bottom left corner, whereas rotations in Box2D are done around mass center's body.
Rotating the texture around mass center body gives right physics/texture behavior.

Categories