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();
Related
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.
Im trying to learn about the Box2D physics engine.
Im using it in LibGX, and im facing a problem.
When i convert from Pixels to Meters, only large objects are drawn..
say this:
public class GameScreen implements Screen{
static int PPM = 100;
Box2DDebugRenderer debugRenderer;
World w = new World(new Vector2(0,-0.981f),true);
OrthographicCamera cam;
public static Body createDynamicBody(World world, int x, int y, int w,
int h, int density, int scale) {
BodyDef def = new BodyDef();
def.type = BodyDef.BodyType.DynamicBody;
def.position.set(x/scale,y/scale);
Body b = world.createBody(def);
FixtureDef fdef =new FixtureDef();
PolygonShape shape = new PolygonShape();
shape.setAsBox((w/2)/scale,(h/2)/scale);
fdef.shape = shape;
b.createFixture(fdef);
return b;
}
// initialized when Screen is loaded
#Override
public void show() {
cam = new OrthographicCamera();
font = new BitmapFont();
debugRenderer = new Box2DDebugRenderer();
cam.setToOrtho(false,800,480);
debugRenderer = new Box2DDebugRenderer();
// this body is not drawn
Body b = createDynamicBody(w,200,200,150,150,5, PPM);
// this body is drawn
Body b2 = createDynamicBody(w,200,200,200,200,5, PPM);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0.2f,0.1f,0.7f,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
debugRenderer.render(w,camera.combined.cpy().scale(PPM,PPM,0));
w.step(1/60f,6,2);
}
}
In your createDynamicBody you are using an int for scale. This is causing you to loose precision when you divide. Simple replace your int scale with float scale and it should work.
public static Body createDynamicBody(World world, int x, int y, int w,
int h, int density, float scale)
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.
I'm having a problem trying to extend org.jbox2d.dynamics.Body
I've set Rectangle to extend org.jbox2d.dynamics.Body and set it to be DYNAMIC, but the Rectangle will not move.
And if I create multiple Rectangle's, they're all positioned in the same position i.e (0.0f, 0.0f), why?
Rectangle is defined as:
public class Rectangle extends Shape {
private PolygonShape blockShape;
private Body body;
private float width;
private float height;
public Rectangle (
Vec2 centerPoint,
float angle,
float width, float height,
BodyType bt,
float density, float friction, float restitution,
World world,
BodyDef bd){
super(bd,world);
this.width = width;
this.height = height;
blockShape = new PolygonShape();
blockShape.setAsBox(this.width, this.height);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = blockShape;
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
this.bodydef.type = BodyType.DYNAMIC;
this.bodydef.position.set(centerPoint.x, centerPoint.y);
this.bodydef.angle = angle;
world.createBody(this.bodydef);
createFixture(fixtureDef);
setType(bt);
};
Shape has follow definition:
public abstract class Shape extends org.jbox2d.dynamics.Body{
protected World world;
protected BodyDef bodydef;
public Shape(BodyDef bd, World w) {
super(bd, w);
this.world = w;
this.bodydef = bd;
}
protected abstract void update();
protected abstract void draw(GLAutoDrawable gLDrawable);
}
In Level.java which extends org.jbox2d.dynamics.World
I create a Rectangle and call Rectangle.draw(gLDrawable)
new Rectangle(
new Vec2(1.0f,1.0f),//Vec2(x,y)
0.0f, //angle
2.0f, //width
2.0f, //height
BodyType.DYNAMIC, //bodyType
0.5f, //density
0.5f, //friction
0.5f, //restitution
this, //world
new BodyDef())); //BodyDef
Decided to use UserData of org.jbox2d.dynamics.Body
Example:
org.jbox2d.dynamics.Body body = level.createBody(bodydef);
body.setUserData(
new Shape(parameters)
);
I got an issues concerning the vertices of my shape.
The origin is at the top left corner (0,0) when I set the position of the bodyDef at 1, 1 bodyDef.position.set(position.x, position.y) the body do a translation diagonally by applying a vector (1,1) and I don't get it why ... can you help me ?
public class Player {
private Body body;
public Player(World world, Vec2 position) {
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(position.x, position.y);
bodyDef.type = BodyType.DYNAMIC;
PolygonShape shapeShip = new PolygonShape();
Vec2[] verticesShip = {
new Vec2(0.0f, -10.0f),
new Vec2(10.0f, 10.0f),
new Vec2(-10.0f, 10.0f)
};
shapeShip.set(verticesShip, verticesShip.length);
FixtureDef fixtureDefShip = new FixtureDef();
fixtureDefShip.shape = shapeShip;
fixtureDefShip.density = 0.5f;
fixtureDefShip.friction = 0.3f;
fixtureDefShip.restitution = 0.5f;
body = world.createBody(bodyDef);
body.createFixture(fixtureDefShip);
}
public void draw(Graphics2D graphics, int width, int height) {
PolygonShape polygonShape = (PolygonShape) body.getFixtureList().getShape();
Vec2[] vertices = polygonShape.getVertices();
for(int i = 0; i < polygonShape.getVertexCount(); i++) {
Vec2 vertice = vertices[i];
vertices[i] = body.getWorldPoint(vertice);
System.out.println(body.getWorldCenter());
}
DrawShape.drawPolygon(graphics, polygonShape.getVertices(), polygonShape.getVertexCount(), Color.WHITE);
}
public void forward() {
Vec2 force = new Vec2(0.0f, 10.0f);
Vec2 point = body.getPosition();
body.applyForce(force, point);
}
public void rotateLeft() {
float angle = (float) Math.toDegrees(body.getAngle()) % 360;
body.setTransform(body.getPosition(), (float) Math.toRadians(--angle));
}
public void rotateRight() {
float angle = (float) Math.toDegrees(body.getAngle()) % 360;
body.setTransform(body.getPosition(), (float) Math.toRadians(++angle));
}
public Body getBody() {
return body;
}
}
public void draw(Graphics2D graphics, int width, int height) {
PolygonShape polygonShape = (PolygonShape) body.getFixtureList().getShape();
Vec2[] vertices = polygonShape.getVertices();
Vec2[] verticesTransform = new Vec2[polygonShape.getVertexCount()];
for(int i = 0; i < polygonShape.getVertexCount(); i++) {
verticesTransform[i] = body.getWorldPoint(vertices[i]);
System.out.println(verticesTransform[i]);
}
DrawShape.drawPolygon(graphics, verticesTransform, verticesTransform.length, Color.WHITE);
}