LibGDX Beginner Tutorial: Sprite Sheets & Physics with Box2d

This tutorial is for libGDX beginners, explaining how to create sprite sheets, use sprites in your game
and add game physics.

Creating a new project

Using Gradle to import & manage dependencies

Creating sprite sheets

Using sprites

Creating physics collision shapes

Editing physics parameters

Using physics in your game

Create a new LibGDX application

Start by downloading the
setup app
that LibGDX provides, as well as the
Android SDK.
I won't go into details about how to set up the SDK since Android already
provides in depth guides for that.

Run the tool and make sure that you have the Desktop and Android
sub-projects, as well the the Box2d extension selected. Use whatever you'd
like for the project's name, package, and game class. See the screenshot below
for an example.

It doesn't hurt to have more sub-projects or extensions selected. Those included
in the example below are just the minimal requirements for this tutorial.

Configuring the project in Android Studio

After you generate the application with the setup tool, open Android Studio, or
whichever editor you prefer. Just keep in mind that this guide is written with
Android Studio in mind so you may have to make some adjustments to get it to
work with another editor.

Import the project using Gradle.

Open the project tab, and select the "Android" view.

Expand the Gradle Scripts tree node, and open the project's build.gradle
file. This is the build.gradle file that is found in your project's root path.
Notice that it has Project to the right of it while the others say Module.

Scroll down until you find the project(":core") section, and add this
line into the dependencies:

When you save the build file, Android Studio will tell you that the Gradle files
have changed, and ask if you want to sync the IDE. Click Sync Now in the
notification that appears at the top of the editor window. This will download
the gdx-pe-loader dependency and install it in your project for you.

Setting up run configurations

Next we need to tell Android Studio how to start the desktop version of our app
so we don't have to test it out with an Android device every time we make a
small change to our code.

From the Run menu, select Edit Configurations. Click the green + at
the top-left corner of the window, and select Application to add a desktop
application profile.

Change the following values for the configuration:

Change the name to Desktop

Click the ellipsis to the right of the main class setting and select the
DesktopLauncher class.

Add /android/assets to the end of the working directory.

Select desktop for the classpath.

You can now run the desktop application to check that everything is working. To
do that just click the play button to the right of the configuration in Android
Studio's menu bar.

After the project builds, you should see this window:

Some notes on LibGDX

There are three modules in the application:

android

core

desktop

The android and desktop modules contain code to launch the application on those two
platforms. If you checked the iOS or Html options in the LibGDX set up app, then
you will have a module for each of those as well.

All of the code we'll be writing is going into the core module. However, the
assets for our project, such as images, sprite sheets, and the PhysicsEditor XML
data, will be placed under android/assets.

If you decide that you do not want to make an Android app, you'll have to do
some additional research and work into setting up LibGDX to load assets outside
of the default path, which is android/assets. Alternatively, you can just
include the Android project even if you're not going to release your game on
that platform. It doesn't hurt to have it there. This is what I personally
recommend doing for the sake of simplicity.

If you open up the PhysicsGame class found in the core module, it should look
something like this:

This is the sample code provided by LibGDX. It provides a SpriteBatch, which
is used to draw sprites, and a sample texture that it draws on the screen.

One thing that's very important to realize when working with LibGDX, is that
you cannot instantiate most LibGDX classes outside of the create() method.
As a Java programmer, you might instinctively want to do something like this:

Go ahead and try that now to see what I happens. You should get an
UnsatisfiedLinkError. This is because LibGDX uses a native library, which
needs to be loaded into memory before we can start working with it. So the only
only safe place we have to instantiate LibGDX objects is in the create method.

One thing that isn't provided in this sample code (that really should be), is
an additional method called dispose. LibGDX has its own way of cleaning up
after itself. This is because when writing a game in Java, keeping tabs on when
the garbage collector runs is very important. Letting the garbage collector
decide when to clean things up in a game is a bad idea. It could cause lag and
memory leaks that could severely impact the performance of your game.

The sample code should really include a dispose method that looks like this:

@Override
public void dispose() {
batch.dispose();
img.dispose();
}

You don't have to add that now, I just wanted you to be aware that you will have
to keep tabs on the objects you create, and make sure to properly dispose of
them. You can read more about how that works on the LibGDX wiki in their article
on memory management

Adding our game's assets

We are going to be using TexturePacker
for managing our games assets. This is a commercial application, but it has a
free trial that will work for the sake of this tutorial. Go ahead and download a
copy of that now if you don't already have one.

Outside of Android Studio, in your operating system's native file manager,
create a directory in the root folder of your project named raw-assets. It's a
good idea to keep your unmodified assets separate like this. It makes it a lot
easier to update our sprites down the road if we have the raw assets that they
were created from, rather than having to edit a sprite sheet directly.

Now download raw-assets.zip
and extract it into that
folder. It has all the raw assets we'll be using in this tutorial.

The first thing we should do with these assets is create a sprite sheet. We'll
be using TexturePacker for this. Open the app now, and drag the raw-assets
into the left panel. You should end up
with a new sprite sheet that looks like this:

Dragging the folder as the following advantage: Making changes to the sprite folder — by adding,
removing or changing images — automatically updates the sprite sheet in TexturePacker.

Save the project as sprites.tps next to the raw-assets folder.

Make sure that LibGDX is selected as data format, and point the data file to
your project's android/assets/sprites.txt folder, and the texture file to
android/assets/sprites.png — the result should look like the image below. You
can leave everything else at their default settings.

Click the Publish sprite sheet button in TexturePacker's tool bar. After that
you can close out of TexturePacker, and go back to Android Studio. If you expand
the android module, it should now look like this:

You'll notice that we have two new files. Don't worry about their colors in the
above screenshot. The files are red because the project I'm working on is a git
repository, and that is just to let me know that I have not yet committed them
to git.

The .txt file contains information about the sprite sheet. It tells the
position, rotation, size, and offsets for each sprite. The format it uses is
native to LibGDX. You can read more about that on the LibGDX wiki in the article
on the
TextureAtlas
object.

Loading sprite sheets in LibGDX

Loading sprite sheets in LibGDX is surprisingly easy. All we have to do is
create a new TextureAtlas instance, and give it the name of our atlas file,
which in this case is sprites.txt. Add this code to our PhysicsGame class:

I only included the relevant code in this example. I don't want you to delete
everything else in the class, just add what you need to and leave the rest
alone.

Now to create a sprite, all we have to do is this:

Sprite sprite = textureAtlas.createSprite("banana");

Let's go ahead and try that now. Let's remove the sample image and replace it
with one of our sprites. To do that, delete the Texture img; line, and any
reference to the img variable. Then create a new variable for our sprite, and
initialize it in the create method.

Note that drawing sprites is done differently than drawing textures. With a
texture you have to use batch.draw, but with sprites you use sprite.draw,
and give it the batch to draw to. I will go into detail about how this works
later on in the tutorial.
The full code for our PhysicsGame class should now look like this:

And if we run the application, we'll get a window that looks like this:

The red is a little hard on the eyes, though. Let's change that to a nice sky
blue color. To do that, let's change the first line in our render function to
look like this:

Gdx.gl.glClearColor(0.57f, 0.77f, 0.85f, 1);

Note that the RGB values are from 0 to 1, and not the traditional 0 to 255. This
is because we're making a direct call to the OpenGL function glClearColor. You
can read more about how that function works in the official
OpenGL SDK
documentation.

Now if we run our application, it should look like this:

Scaling our game

If you run the game again, and resize the window, you will notice that our
sprite does not exactly scale well. Instead it just shrinks or grows in the
direction we resize the window, like this:

To get things to scale properly, we will be using a
Viewport. If you read up on
viewports on the wiki, you'll notice there are several different variations. We
will be using an
ExtendViewport
for our game.

We'll also need to include an
OrthographicCamera,
which is required for attaching a 2D environment to a viewport.

The sizes, 800, 600, are the resolution at which our sprites are scaled to
100%. If the window gets bigger than that, the sprites will grow, if it gets
smaller, the sprites will shrink, both while maintaining their original aspect
ratios.

But we still haven't told the sprite batch to use our camera, or the viewport
when the screen is resized. Here's how to do that:

Now if we run our game and resize the window, the banana will properly scale to
fit our viewport:

Note that we are updating the batch's projection matrix every time the screen is
resized. This is what actually makes the scaling possible. We will need to
update its projection matrix any time we make a change to the camera. If we were
to change its position, for example.

We're also telling the viewport to center the camera every time the screen is
resized. That's what the true parameter in viewport.update is for. If you
are writing something like a platform game, you more than likely will want to
update the camera's position manually based on where the player is in the game
world. In that case we'd want to move the call to batch.setProjectionMatrix
out of the resize method and into something more appropriate.

Drawing multiple sprites

You may think that to draw more than one sprite we'd need to create two Sprite
instances. However, once you draw a sprite to the screen, you can reuse the same
instance to draw it again. Let's update our render code to look like this:

Before we continue, let's take a closer look at the drawSprite helper method I
just wrote. Do you notice anything about it that might impact the performance of
our game? I do. Each time we call it, we're creating a new Sprite instance,
which will very quickly start consuming a lot of memory. Let's fix that by
caching the sprites using a HashMap.

Start by creating a HashMap that we can store our sprites inside of. Since
this is not a LibGDX object, we can go ahead and make this a final variable, and
initialize it outside of our create method:

The new Vector2(0, -10) is the direction that gravity pulls in our world. In
this case, it doesn't have any affect on the X axis, and pulls on the Y axis
with a force of -10.

You may have already noticed when we draw our sprites to the screen that 0,0 is
the bottom-left corner of the screen. This means that -10 will pull our physics
objects down toward the bottom of the screen, and of course positive 10 would
move them upward, which is not what we want.

Next we need to step our physics simulation. I could write a full article on
stepping a Box2D world, but for now I'm just going to give you the code to add
to the game to make that happen:

You can read more on how stepping the simulation works on the LibGDX wiki in
this article.

Creating the physics objects

To add objects to our world, we need to provide them with what are known as
bodies in Box2D terms. Each body has one or more fixtures, which are just basic
shapes, like polygons or circles. The fixtures are what the physics objects
works with, and are what actually collide with one another. The bodies are
pretty much just bags of fixtures, although they do provide a little more
information, like whether or not they can move.

We're going to be using an application called
PhysicsEditor
to create polygons and shapes for our sprites, and then we'll be loading those
in using the PhysicsShapeCache class provided by the gdx-pe-loader dependency we installed when we set up our LibGDX
application.

To get started, download and launch PhysicsEditor now. Once you have that open,
click the Add sprites button in the toolbar, or drag and drop the sprites from
our project's raw-assets folder into the sprites section of the window. This
is what PhysicsEditor will look like after you do that:

Then select the "LibGDX" option as your exporter:

Save the .pes file into the project's folder. Name it sprites.pes
to continue with the same naming convention that we've been using so far. Then
click the Publish button and save it into android/assets/physics.xml under
the project's root path.

Now we can load the physics.xml file we just created in our application by
adding this code:

Of course, we still need to actually draw our collision polygons in
PhysicsEditor, or we don't exactly have anything to load. Let's start with the
banana.

Back in PhysicsEditor, select the banana sprite, and click on the wand tool. You
can find that in the toolbar just above the image of the banana. It looks like
this:

After clicking on that icon, this window will open:

This tool detects the edges of the sprite and creates a polygon for it. You can
play around with the settings to see the effects it has on the collision
polygon.

Click the OK button. The polygon will be added to your sprite. You can then
adjust its vertices by dragging them. Double-click on a vertex to delete it.
Double-click outside of the shape to add a vertex to the closest edge.

Here's a video showing how I created the various collision polygons I used for
this tutorial:

Hint: Double-click instead of right click to add or remove vertices
as it is done in the video.

Notice how I zoomed in to about 350% so I could more clearly see the images I
was working with.

If you watch the part of the video where I added to the shapes to the cherries,
you'll notice that I opted to use circles instead polygons. The reason I did
this is because of how much faster detecting collisions between circles is than
between polygons.

All you have to do to detect collision between two circles is measure the
distance between them, and check if it is less than the sum of their radii. You
can see the difference in these two functions:

It is obvious just by comparing the two which one is going to impact the
performance of the game more. Clearly the fewer vertices you add to your sprites
the better the performance you will have, circles only contain one vertex each.
So using circles where possible will theoretically increase the performance of
your game.

Rendering Box2D objects.

One of the most frustrating things to work with regarding Box2D is its speed
limit. Physics bodies cannot move faster than about 120 units per second - a
unit in Box2D is typically referred to as meters. This means that at a screen
resolution of 800 pixels wide, it would take approximately 6.7 seconds for an
object to move from one side of the screen to the other at full speed.

To overcome this, some people will translate Box2D coordinates to screen
coordinates when rendering their world. The PhysicsEditor application is set up
to accommodate this by providing a PTM (Pixels To Meters) ratio.

However, instead of doing that, I prefer to scale our screen down to a size that
fits within our physics world, which is a lot simpler. In fact all you really
have to do is change the size of the viewport, and scaling down the sprites to
an appropriate site as they are loaded into the game.

Let's start with the size of the viewport. Instead of initializing it with
800,600, let's use 50,50:

viewport = new ExtendViewport(50, 50, camera);

This does two things for us. One, it gives us a constant size to work with. We
now know the exact size of our game world. It's 50 meters wide, by 50 meters
tall. Two, it allows our sprites to move very fast. It will now only take 0.42
seconds for a sprite to move the full width of the screen. Much better than the
6.7 seconds we get with a world 800 meters (formally known as pixels) wide.

If we run our game now, the sprites will be way too big to be drawn on the
screen, as seen in the screenshot below.

To fix this, we have to scale the image to something more appropriate to our
game world. To do this, I'm going to update the method we use to cache our
sprites in the HashMap:

The banana is smaller than it was before, which is good. I would like to be able
to draw more sprites on the screen, and 100% scale was just too big. It would
take a little math to figure out exactly how much it was scaled in actual
pixels, but that's not really important. We're working with meters now, not
pixels. That's something you'll have to get used to.

Notice, too, that the cherries are gone. I didn't delete the code that drew the
cherries. They're no longer being rendered, because they are at x=100, y=100,
and since our game world is only 50x50 meters, they're way off the screen right
now. We can bring them back into view easily enough. This will put them at
roughly the same coordinates as before:

drawSprite("cherries", 5, 5);

Creating Box2D bodies

It is not the intention of this tutorial to detail exactly how to create a Box2D
body. I suggest doing a little research of your own to get familiar with exactly
how that works. The LibGDX wiki has an in depth article on that
here.

Box2D itself is a C++ library. The implementation that LibGDX provides is a thin
Java wrapper around it.

Here
is a very in depth Box2D
guide you might want to bookmark as well. Although it's focused on C++
programmers, it has a lot more detail than what LibGDX provides on their wiki,
and it answers a lot of questions I had when I first started using it. It's just
a matter of converting the C++ syntax into Java to use it with LibGDX.

That said, let's get started by removing our render code and replacing it with a
debug renderer that will draw our collision polygons onto the screen for us:

Make sure to initialize any Box2D objects after the call to Box2D.init() is
made to make sure the library is properly loaded first. You'll also want to put
your call to debugRenderer.render at the end of the render method so that the
debug shapes will always be rendered on top of everything else. I'll show the
full game code soon.

PhysicsShapeCache makes it really easy to create our physics bodies. Here's an example of
how to create the banana:

If you add that code to the create method, you should see the body appear on
screen, and slowly fall downward. Because it's drawing only the polygons, it
will be very difficult to see, so I changed the background to black for this
screenshot so you can see it clearer - the polygon in the image is a little
broken since I took a screenshot of something in motion:

If you run the game now, you'll see a banana fall from the top of the screen
to the bottom, with its polygons drawn in wireframe over top of it.

Creating a ground

Right now the banana just falls off the screen and keeps going. Let's give it
something to fall onto. For this I'm going to just create a single meter tall
rectangle that spans the width of the screen, and place that at 0,0.

You can see how much work the PhysicsShapeCache does for us. The createBody code
contains the minimal amount of work involved in creating just a single Box2D
fixture. I won't go into detail about how it works in this tutorial, but here's
the gist of it.

Every time the screen is resized, the ground will have to be resized as well.
To make things easy, I just destroy the old ground and create a new one when
that happens.

Next, I create a BodyDef, which tells Box2D that the ground is static, and can
only be moved manually. Then I create a FixtureDef, which contains a rectangle
that I adjust to the width of the viewport, and one meter tall. The rest of the
function creates the body, sets its position and rotation, and then disposes of
the shape that was used to create it.

If you run the game now, you'll notice something peculiar happens when the
banana hits the ground. The Box2D debug renderer shows the banana rotate, but
our sprite does not.

It's easy enough to rotate a sprite in LibGDX. Let's update the drawSprite
method to accomodate for that:

Then of course, we'll have to update the call to drawSprite in our render
method. LibGDX wants degrees, but Box2D gives us radians. So we'll need to make
sure to translate our radians to degrees before passing it off to the
drawSprite method:

With this updated code, a bunch of fruit will fall from above, hit the ground,
then slide around. It will look something like this:

This is a good start, but let's make things more interesting. You'll notice how
nothing really bounces. It just kind of falls, then slides around. The ground
doesn't appear to have any friction to it, either.

Let's start by adding friction to the ground. Edit the createGround method,
and add friction to the ground's FixtureDef.

FixtureDef fixtureDef = new FixtureDef();
fixtureDef.friction = 1;

Of course, our physics bodies need friction as well for this to work. Using
PhysicsEditor we can make some simple adjustments to give each of our fixtures
a little more of a tactile feel.

Open PhysicsEditor, and edit the properties of each polygon and circle to look
like this:

Then click "Save" and "Publish." Now run the game again. It will look something
like this:

You can play around with the different settings. Here's what they mean:

Restitution is how bouncy the objects are

Density is how heavy they are

Friction is how much they resist

All the values are from 0 to 1.

In case you are wondering why heavier objects don't drop faster than lighter objects: They just don't —
as long as air resistance is not considered (which box2d does not simulate).
Galileo Galilei showed this in 1589.

You might also want to adjust the level of gravity to make things more
responsive. Right now it is set to -10, if you can recall. Try adjusting it to
something much higher, like -120: