Bouncing ball - Leakedbits/Codelabs GitHub Wiki
This tutorial tries to explain our BouncingBallSample, a ball falling and bouncing off a ramp and walls.
It's time to create an environment to play with. First, we will need a point of view, something that supplies the HOW we will be seeing things, because if you have no eyes you are unable to see anything. For that matter in this virtual environment our eyes will be an OrtographicCamera
object. Of couse, we also need the WHAT we are going to see, that is, a World
object. It will be in charge of hold all the bodies and simulate interactions between them.
private OrthographicCamera camera;
private World world;
Also, we will need a global Box2DDebugRenderer
object, a model renderer for debugging purposes.
private Box2DDebugRenderer debugRenderer;
The next step would be to render the environment, preparing everything we need.
@Override
public void render(float delta) {
/* Clear screen with a black background */
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
/* Render all graphics before do physics step */
debugRenderer.render(world, camera.combined);
/* Step the simulation with a fixed time step of 1/60 of a second */
world.step(1 / 60f, 6, 2);
}
Easy enough so far, right? What we have to do now is to override the show()
method, used for, well, show what we have done. First calls its superclass for setting the sample to receive all touch and key input events. Also prevents the app from be closed whenever the user presses the back button and instead returns to the main menu. After that, we create the World
with the same gravity value as the real world, and a boolean value for saving CPU usage. Also, we create the Box2DDebugRenderer
object.
Then we need to create our OrthographicCamera
with a width and a height. Remember that Box2D uses meters as unit of length (hello, SI!). Let's assume that we want the camera to be 20 meters wide, so we need to adjust the height multiplying its width by its aspect ratio. With all this in mind, we finish up with this method:
@Override
public void show() {
super.show();
world = new World(new Vector2(0, -9.81f), true);
debugRenderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(20,
20 * (Gdx.graphics.getHeight() / (float) Gdx.graphics
.getWidth()));
}
Now we need to create the Body
objects that will be shown. Let's start with the walls, ceiling and floor. As you are going to see below, we need to create a BodyDef
object and set its type as StaticBody
and his position as 0, 0
, the screen center, as starting point.
private Body createWalls() {
/* Walls body definition */
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.StaticBody;
bodyDef.position.set(0f, 0f);
Now we draw the lines which will form the walls, keeping in mind that we need to create a ChainShape
with a series of Vector2
objects defined by four points. Think of it as a real life drawing canvas: if you want to draw a square you go from one corner to another until you get back to the first one. But here our corners are cartographic points, being 0, 0
the center of the screen, and the Vector2
are just the strokes that link them.
/* Shape definition */
ChainShape wallsShape = new ChainShape();
wallsShape.createChain(new Vector2[] { new Vector2(-9, -5),
new Vector2(9, -5), new Vector2(9, 5), new Vector2(-9, 5),
new Vector2(-9, -5) });
Here we define the fixture, giving it the wallsShape
we already have and setting a value for its friction (used to make objects slide along each other) and restitution (used to make objects bounce).
/* Fixture definition */
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = wallsShape;
fixtureDef.friction = 0.5f;
fixtureDef.restitution = 0f;
Now we add the Body
object, defined by its bodyDef
, to the world
. We bestow our fixtureDef
on that body
.
/* Create body and fixture */
Body body = world.createBody(bodyDef);
body.createFixture(fixtureDef);
We don't need anymore the shape we used, since we already have it in the body
's fixture, so we dispose it.
/* Dispose shape once the body is added to the world */
wallsShape.dispose();
Finally, we return the body
we have just created
return body;
}
Something similar is done for drawing the ramp to which the ball will bounce once spawned. We only need to take into account that its position is going to be now 6.5f, 0f
and that for drawing we only need to link two points with one stroke:
rampShape.createChain(new Vector2[] { new Vector2(-2.5f, -1),
new Vector2(2.5f, 1) });
Also, we are going to change its friction to 0.3f
.
For creating the ball we follow a similar process, but using what Box2D has to offer:
private Body createBall() {
/*
* Ball body definition. Represents a single point in the world. This
* body will be dynamic because the ball must interact with the
* environment and will be set 6 meters right and 5 meters up from
* viewport center.
*/
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(6, 5);
/* Shape definition (the actual shape of the body) */
CircleShape ballShape = new CircleShape();
ballShape.setRadius(0.15f);
/*
* Fixture definition. Lets us define properties of a body like the
* shape, the density of the body, its friction or its restitution (how
* 'bouncy' a fixture is) in a physics scene.
*/
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = ballShape;
fixtureDef.density = 2.5f;
fixtureDef.friction = 0.25f;
fixtureDef.restitution = 0.75f;
/* Create body and fixture */
Body body = world.createBody(bodyDef);
body.createFixture(fixtureDef);
/* Dispose shape once the body is added to the world */
ballShape.dispose();
return body;
}
Now that we have all we want, we need to make our class to show all of it. How do we do that? Of course, in the show()
method.
@Override
public void show() {
// ...
createWalls();
createRamp();
createBall();
}
Remember! You have all this working code in our BouncingBallSample class.