Box2D and libGDX
-
Upload
jussi-pohjolainen -
Category
Technology
-
view
483 -
download
4
Transcript of Box2D and libGDX
Box2D and libGDX
Jussi Pohjolainen Tampere University of Applied Sciences
Box2D
• Free open source 2D physics simulator engine wriEen in C++
• Has been used for example in Angry Birds, Tiny Wings
• Has been ported to Java, Flash, C#, JavaScript • Programs that use Box2D – libGDX, Unity, iOS SpriteKit, GameMaker Studio ...
Concepts • World
– CollecNon of bodies, fixtures, joints that interact • Body
– Game Objects that contains fixtures – Dynamic (spaceship), StaAc (ground) and KinemaAc – Fixture
• Body has Fixture! • Density: Mass per square meter: bowling ball dense, balloon not • FricAon: When sliding along something, amount of opposite force • ResAtuAon: How bouncy object is (0 = does not bounce, 1 = very bouncy) • Shape
– Fixture has Shape – Polygon or Circle, can be mixed
• Joint – Holds bodies together. Several joints available.
World Body Fixture Shape
// Create array full of bodies Array<Body> bodies = new Array<Body>(); // Take all bodies from world and put them into array world.getBodies(bodies); // Get shape of first body, it's first fixture and it's shape Shape s = bodies.get(0).getFixtureList().get(0).getShape();
Units
• Box2D uses floaAng points • Tuned for meter – kilogram – second (KMS) • OpNmized so that shapes can move between 0.1 – 10 meters
• It's tempNng to use pixel as units -‐> leads to poor simulaNon and weird behavior – Keep moving objects between 0.1 – 10 meters!
• DO NOT USE PIXELS! DO NOT!
Units in libGDX
• Window/real resoluNon <-‐> World Units <-‐> Box2D units (meters)
• How to handle this? Just use meters in World Units, so you don't have to make any conversions – float width = 640.0f / 100.0f – float height = 400.0f / 100.0f – camera = new OrthographicCamera(); – camera.setToOrtho(false, width, height);
CreaNng a World
// Create world and set it's gravity!
// true => allow body sleeping
World world = new World(new Vector2(0, -9.8f), true);
// Next steps: create a body to the world
Body Types
• Dynamic Body – Player and other actors on screen
• StaAc Body – Walls, floors and so on
• KinemaAc Body – Moving pladorm in pladorm game – It's a staNc body that can also move and rotate. When dynamic body collides, kinemaNc body "wins"
Bodies
• Bodies are objects in physics space – Does not hold texture! So it's not visible!
• Hold properNes like – mass, velocity, locaNon, angle
• Define size and shape of the body using a fixture
• Shape can be circle or polygon or a mixture of these!
CreaNng a Body
• To create body, use – // Create BodyDef.. – Body playerBody = world.createBody(BodyDef);
• BodyDef? – Type of body? StaNc, kinemaNc, dynamic – PosiNon
• Ager creaNon, add a Fixture to the body – Density, ResNtuNon (bouncy?), fricNon (slippery?), shape (circle or polygon)
– playerBody.createFixture(playerFixtureDef);
CreaNng a Body public void createPlayerBody() { // *** 1) DEFINITION OF A BODY *** /
// Body Definition BodyDef myBodyDef = new BodyDef();
// It's a body that moves
myBodyDef.type = BodyDef.BodyType.DynamicBody;
// Initial position is centered up (width = 12.8, height = 7.2)
// This position is the CENTER of the shape! myBodyDef.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2);
// *** 2) CREATE THE BODY *** /
Body playerBody = world.createBody(myBodyDef);
// *** 3) FIXTURE FOR THE BODY *** /
// Create fixture and add the shape to it
FixtureDef playerFixtureDef = new FixtureDef();
// Mass per square meter (kg^m2)
playerFixtureDef.density = 1;
// How bouncy object? Very bouncy [0,1]
playerFixtureDef.restitution = 1.0f;
// How slipper object? [0,1]
playerFixtureDef.friction = 0.5f;
// Create circle shape.
CircleShape circleshape = new CircleShape();
circleshape.setRadius(0.1f);
// Add the shape to the fixture
playerFixtureDef.shape = circleshape;
// Add fixture to the body
playerBody.createFixture(playerFixtureDef);
}
About Rendering
• Box2D holds the physics, it's not about rendering – For every render call, update (step) the world
• To render, map some texture to the posiNon of the playerBody!
• For debugging, use debugRenderer
Example About Rendering public class Box2DExample extends ApplicationAdapter {
private SpriteBatch batch;
public static final boolean DEBUG_PHYSICS = true;
public static final float WORLD_WIDTH = 6.4f;
public static final float WORLD_HEIGHT = 4.0f;
private OrthographicCamera camera;
private World world;
private Box2DDebugRenderer debugRenderer;
@Override
public void create () {
camera = new OrthographicCamera();
camera.setToOrtho(false, WORLD_WIDTH, WORLD_HEIGHT);
batch = new SpriteBatch();
world = new World(new Vector2(0f, -9.81f), true);
debugRenderer = new Box2DDebugRenderer();
createPlayerBody();
}
public void createPlayerBody() { ... }
@Override
public void render () {
batch.setProjectionMatrix(camera.combined);
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(DEBUG_PHYSICS) {
debugRenderer.render(world, camera.combined);
}
batch.begin();
// Do some drawing
batch.end();
world.step(1/60f, 6, 2);
}
}
Rendering (Debug) // Create the renderer (in create...) Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer(true, // draw bodies false, // joints false, // drawAABBs false, // drawInactiveBodies false, // drawVelocities false); // drawContacts public void render() { ... debugRenderer.render(world, camera.combined); world.step(1 / 60f, 6, 2); ... }
Update World: Stepping • To update world, we need to tell the world to step
// Advance simulation 1/60 of a sec. This should // correspond to framerate in your game float timeStep = 1/60.0f; // How strongly to correct velocity when colliding // More higher, more correct simulation but cost of performance int velocityIterations = 6; // How strongly to correct position when colliding // More higher, more correct simulation but cost of performance int positionIterations = 2; world.step(timeStep, velocityIterations, positionIterations);
• Do this at the end of the render() call
Variable Nmestep
• If you have following – world.step(1/60f, 6, 2);
• SimulaNon can be ruined if machine is not able to run at 60fps
• You could calculate the game speed and add it to stepping – world.step(framerate, 6, 2);
• Problem now is that your game can run very very fast or very very slow
public void render() {
...
doPhysicsStep();
}
private double accumulator = 0; private double currentTime = TimeUtils.millis() / 1000.0;
private void doPhysicsStep() {
// We are not using Gdx.graphics.getDeltaTime(), it may not
// be accurate enough.
// Current time
double newTime = TimeUtils.millis() / 1000.0;
// How much time since last call?
double deltaTime = newTime - currentTime;
currentTime = newTime;
float TIME_STEP = 1/60f;
// If it took ages (> 0.25 => 4 fps)
// FIXED Upper limit to prevent going really slow
if(deltaTime > 0.25) {
deltaTime = 0.25;
}
accumulator += deltaTime;
while (accumulator >= TIME_STEP) {
world.step(TIME_STEP, 6, 2);
accumulator -= TIME_STEP;
}
}
"Ground" public void createGround() {
// *** 1) DEFINITION OF A BODY *** /
// Body Definition
BodyDef myBodyDef = new BodyDef();
// This body won't move
myBodyDef.type = BodyDef.BodyType.StaticBody;
// Initial position is centered up
// This position is the CENTER of the shape!
myBodyDef.position.set(WORLD_WIDTH / 2, 0.25f);
// *** 2) CREATE THE BODY *** /
Body groundBody = world.createBody(myBodyDef);
// *** 3) FIXTURE FOR THE BODY *** /
// Create shape
PolygonShape groundBox = new PolygonShape();
// Real width and height is 2 X this!
groundBox.setAsBox( WORLD_WIDTH/2 , 0.25f);
// Add shape to fixture, 0.0f is density.
// Using method createFixture(Shape, density) no need
// to create FixtureDef object as on createPlayer!
groundBody.createFixture(groundBox, 0.0f);
}
Draw texture to body pos! public void render() {
...
batch.draw(texture,
playerBody.getPosition().x,
playerBody.getPosition.y);
}
Draw texture to body pos! batch.draw(playerTexture,
playerBody.getPosition().x - playerRadius,
playerBody.getPosition().y - playerRadius,
playerRadius, // originX
playerRadius, // originY
playerRadius * 2, // width
playerRadius * 2, // height
1.0f, // scaleX
1.0f, // scaleY
playerBody.getTransform().getRotation() * MathUtils.radiansToDegrees,
0, // Start drawing from x = 0
0, // Start drawing from y = 0
playerTexture.getWidth(), // End drawing x
playerTexture.getHeight(), // End drawing y
false, // flipX
false); // flipY
Create Body and Add User Data public void createJussi(float x, float y) {
BodyDef bodyDef = new BodyDef(); bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(x,y); Body body = world.createBody(bodyDef); PolygonShape dynamicCircle = new PolygonShape(); dynamicCircle.setAsBox(0.5f, 0.5f); FixtureDef fixtureDef = new FixtureDef(); fixtureDef.shape = dynamicCircle; fixtureDef.density = 1.0f; fixtureDef.friction = 0.1f; fixtureDef.restitution = 0.8f; body.createFixture(fixtureDef);
// user data can be anything! any object! body.setUserData(jussiTexture);
}
Create Body and Add User Data public void render() { ... debugRenderer.render(world, camera.combined); // populate the array with bodies world.getBodies(bodies); // iterate the bodies for (Body body : bodies) { if(body.getUserData() != null && body.getUserData() == jussiTexture) { batch.draw((Texture) body.getUserData(), body.getPosition().x - 0.5f, body.getPosition().y - 0.5f, ... body.getTransform().getRotation() * MathUtils.radiansToDegrees, ... false ); } } doPhysicsStep(); }
FORCES AND IMPULSES
Forces and Impulses
• To move things around, you'll need to apply forces or impulses to a body
• Force – Gradually over Nme change velocity of a body – Also angular force available, torque (twisNng strength)
• Impulse – Change velocity immediately
• Of course you can just change the posiNon of a body
Examples // gradually accelerate right
body.applyForceToCenter(50f, 0, true);
// Jump up
body.applyLinearImpulse(new Vector2(0f, 20f),
body.getWorldCenter(),
true);
COLLISIONS
Collision world.setContactListener(new ContactListener() { @Override public void beginContact(Contact contact) { Body bodyA = contact.getFixtureA().getBody(); Body bodyB = contact.getFixtureB().getBody(); } @Override public void endContact(Contact contact) { } @Override public void preSolve(Contact contact, Manifold oldManifold) { } @Override public void postSolve(Contact contact, ContactImpulse impulse) { } });