Box2D and libGDX

28
Box2D and libGDX Jussi Pohjolainen Tampere University of Applied Sciences

Transcript of Box2D and libGDX

Page 1: Box2D and libGDX

Box2D  and  libGDX  

Jussi  Pohjolainen  Tampere  University  of  Applied  Sciences  

Page 2: Box2D and libGDX

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  ...  

Page 3: Box2D and libGDX

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.  

Page 4: Box2D and libGDX

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

Page 5: Box2D and libGDX

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!  

Page 6: Box2D and libGDX

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

Page 7: Box2D and libGDX

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

 

Page 8: Box2D and libGDX

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"    

Page 9: Box2D and libGDX

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!  

Page 10: Box2D and libGDX

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

Page 11: Box2D and libGDX

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

Page 12: Box2D and libGDX

// *** 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);

}

Page 13: Box2D and libGDX

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  

Page 14: Box2D and libGDX

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

}

}

Page 15: Box2D and libGDX

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); ... }

Page 16: Box2D and libGDX

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  

Page 17: Box2D and libGDX

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  

 

Page 18: Box2D and libGDX

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;

}

}

Page 19: Box2D and libGDX

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

}

Page 20: Box2D and libGDX

Draw  texture  to  body  pos!  public void render() {

...

batch.draw(texture,

playerBody.getPosition().x,

playerBody.getPosition.y);

}

Page 21: Box2D and libGDX

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

Page 22: Box2D and libGDX

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

}

Page 23: Box2D and libGDX

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(); }

Page 24: Box2D and libGDX

FORCES  AND  IMPULSES  

Page 25: Box2D and libGDX

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  

Page 26: Box2D and libGDX

Examples  // gradually accelerate right

body.applyForceToCenter(50f, 0, true);

// Jump up

body.applyLinearImpulse(new Vector2(0f, 20f),

body.getWorldCenter(),

true);

Page 27: Box2D and libGDX

COLLISIONS  

Page 28: Box2D and libGDX

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) { } });