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