first of all thanks a lot for your time :)
I'm currently trying to understand how jbox2d works and I'm having some issues. The code I wrote makes sense to me, but there should be something I didn't understand at all. Basically what I want to do at the moment is making the main character (controlled by the player) collide with the walls.
Without going too much into details I have a dynamic entity class called Player and a static entity class called Wall. I also have a class called Map which handles the level.
The coordinates of the entities are represented by the pixels in the screen.
now this is the part regarding jbox2d
in the class Map I have:
// ... other fields
private static final float TIME_STEP = 1.0f / 60.f;
private static final int VELOCITY_ITERATIONS = 6;
private static final int POSITION_ITERATIONS = 3;
private World world;
// constructor
public Map(int startPosX, int startPosY)
{
// ...other stuffs
Vec2 gravity = new Vec2(0, -10f);
world = new World(gravity);
// ...other stuffs
}
// update method that is called every 30 ms
public void update(int delta)
{
// ...other stuffs
world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
}
now this is how the static entity looks like:
private Map map;
private Body body;
private Fixture fixture;
private PolygonShape shape;
public Wall(int x, int y, Map map)
{
super(x, y);
this.map = map;
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(x, y);
bodyDef.type = BodyType.STATIC;
shape = new PolygonShape();
shape.setAsBox(CELL_HEIGHT, CELL_WIDTH);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
body = map.getWorld().createBody(bodyDef);
fixture = body.createFixture(fixtureDef);
}
and finally the player:
private Map map;
private PolygonShape shape;
private Body body;
private Fixture fixture;
public MovingEntity(float x, float y, Map map)
{
super.setX(x);
super.setY(y);
animation = Animation.IDLE;
layer = graphics().createImmediateLayer(new EntityRenderer(this));
layer.setVisible(false);
graphics().rootLayer().add(layer);
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(x, y);
bodyDef.type = BodyType.DYNAMIC;
shape = new PolygonShape();
shape.setAsBox(getFrameSize().height(), getFrameSize().width());
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
body = map.getWorld().createBody(bodyDef);
fixture = body.createFixture(shape, 2.0f);
}
do you guys now what am I doing wrong? The entities don't collide at all. Also if I try to print the current position of the player's body in my update method I'm getting coordinates changing even if I don't move (I guess like it is going down because of gravity, which I don't need in my game).
Thanks again a lot!
I think your entities do not collide because you are using empty polygon shapes.
shape = new PolygonShape();
You have to define the points of your Polygone shape so that jbox can test collision of the shapes. Something like this:
Vec2[] vertices = {
new Vec2(0.0f, - 10.0f),
new Vec2(+ 10.0f, + 10.0f),
new Vec2(- 10.0f, + 10.0f)
};
PolygonShape shape = new PolygonShape();
shape.set(vertices, vertices.length);
Further if you don't need gravity then just set the gravity vector to 0,0
Vec2 gravity = new Vec2(0f, 0f);
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 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();
UPDATE
I have a hint. That is because the center of the screen in JBox2D is (0, 0). This is so weird because x and y are not from -1 to 1, it depends of the screen size.
This means the Util class I copied from the internet (everybody shared this code) is not up to date. I still need help because I can't find any 'algorithm' to have aspect ratio + screen size dependency.
BASE QUESTION
Since I have added the "slick2D to jbox2D position" and vice versa (it's like position in screen = position in screen / 100 for JBox), I don't get a good result. Screenshots below
I'm explaining with code:
public class Util {
private static final float SCALE = 0.01f; // 1/100 pixels
public static float toPosX(float posX) { return (posX * SCALE); }
public static float toPosY(float posY) { return (-posY * SCALE); }
public static float toScreenX(float posX) { return (posX / SCALE); }
public static float toScreenY(float posY) { return (-posY / SCALE); }
}
Square.java :
public class Square {
private Body body;
private Rectangle rectangle;
Square(World world, BodyType type, Vec2 pos, Vec2 size) {
float x = Util.toPosX(pos.x);
float y = Util.toPosY(pos.y);
float sx = Util.toPosX(size.x);
float sy = Util.toPosY(size.y);
//body definition
BodyDef bd = new BodyDef();
bd.position.set(x, y);
bd.type = type;
//define shape of the body.
PolygonShape cs = new PolygonShape();
cs.setAsBox(sx, sy);
//define fixture of the body.
FixtureDef fd = new FixtureDef();
fd.shape = cs;
fd.density = 1f;
fd.friction = 0.3f;
fd.restitution = 1.0f;
//create the body and add fixture to it
body = world.createBody(bd);
body.createFixture(fd);
rectangle = new Rectangle(0, 0, size.x, size.y);
rectangle.setLocation(pos.x, pos.y);
}
public Body getBody() { return this.body; }
public Rectangle getRectangle() { return this.rectangle; }
}
And finally my display class:
public class DisplayManager extends BasicGame {
private GameManager gameManager;
private Body b1, b2;
private Rectangle r1, r2;
private World world;
public DisplayManager(GameManager game) {
super("Title");
this.gameManager = game;
}
#Override
public void init(GameContainer container) throws SlickException {
this.gameManager.initPlayersSprites();
world = new World(new Vec2(0, -10));
Square s1, s2;
s1 = new Square(world, BodyType.STATIC, new Vec2(10, 800), new Vec2(1900, 30));
b1 = s1.getBody();
r1 = s1.getRectangle();
s2 = new Square(world, BodyType.DYNAMIC, new Vec2(945, 200), new Vec2(30, 30));
b2 = s2.getBody();
r2 = s2.getRectangle();
}
public void render(GameContainer container, Graphics g) throws SlickException {
float step = 1.0f / 600.0f;
world.step(step, 6, 2);
r1.setLocation(Util.toScreenX(b1.getPosition().x), Util.toScreenY(b1.getPosition().y));
r2.setLocation(Util.toScreenX(b2.getPosition().x), Util.toScreenY(b2.getPosition().y));
g.draw(r1);
g.draw(r2);
}
}
Here is the result :
first frame :
http://i.stack.imgur.com/WgQPK.png
some frames later :
http://i.stack.imgur.com/a1XyL.png
(the ground didn't move, it's just another screenshot took from my hand)
In other words, the cube falls down like for ever. I debugged the position of the cube and it doesn't stop to go down.
With Slick2D DebugDraw it doesn't help because the cube goes through the ground anyway.
Please note that in JBox2D, it worked with pixels measurements (that was not accurate at all but collisions worked well)
That was because of the negative size. (toPoxY reverse the y pos)
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)
);