Elevate Developer Efficiency & build GenAI Application with Amazon Q
Box2D and libGDX
1. Box2D
and
libGDX
Jussi
Pohjolainen
Tampere
University
of
Applied
Sciences
2. 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
...
3. 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.
4. 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();
5. 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!
6. 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);
7. 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
8. 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"
9. 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!
10. 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);
11. 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);
12. // *** 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);
}
13. 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
14. 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);
}
}
16. 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
17. 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
18. 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;
}
}
19. "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);
}
20. Draw
texture
to
body
pos!
public void render() {
...
batch.draw(texture,
playerBody.getPosition().x,
playerBody.getPosition.y);
}
21. 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
22. 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);
}
23. 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();
}
25. 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
26. Examples
// gradually accelerate right
body.applyForceToCenter(50f, 0, true);
// Jump up
body.applyLinearImpulse(new Vector2(0f, 20f),
body.getWorldCenter(),
true);