How to create sprite sheets & animations for libGDX

In this tutorial you will learn how to use sprite sheets with libGDX.
Why using sprite sheets?
- By loading all graphics at once instead of numerous single images, the loading time of your app can be significantly reduced.
- Using a sprite atlas also enhances the game's performance, because textures only need to be set on the graphics device once, resulting in improved frame rates.
Creating a sprite sheet
The easiest way to create a sprite sheet is using TexturePacker — available for Windows, macOS and Linux:
Creating a sprite sheet with TexturePacker is quite simple:
- Drag and drop all the files you want to add to the left pane of TexturePacker
- Select the LibGDX framework and set the file path for the generated Data file. The Texture file can be left empty; TexturePacker will automatically save the PNG containing the sprite sheet next to the data file.
- Click on Publish sprite sheet
We recommend storing the individual sprites in a root folder named raw-assets, while only placing the sprite sheet data and texture file in the assets folder. This makes it easier to determine which assets must be deployed later.
Using a sprite sheet
Let's start with a new, empty project as you can generate it with the libGDX Project Setup Tool. The default implementation of the main class in the core module loads a sprite called libgdx.png and displays it on a dark background.
In the previous section, we've already added the libgdx.png sprite to the sprite sheet. Now, we can load the sprite sheet and extract the sprite from it:
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.ScreenUtils;
/** {@link com.badlogic.gdx.ApplicationListener} implementation shared by all platforms. */
public class MyGdxGame extends ApplicationAdapter {
private SpriteBatch batch;
private TextureAtlas textureAtlas;
private Sprite sprite;
@Override
public void create() {
batch = new SpriteBatch();
textureAtlas = new TextureAtlas("spritesheet.atlas");
sprite = textureAtlas.createSprite("libgdx");
}
@Override
public void render() {
ScreenUtils.clear(0.57f, 0.77f, 0.85f, 1);
batch.begin();
sprite.setPosition(10, 10);
sprite.draw(batch);
batch.end();
}
@Override
public void dispose() {
batch.dispose();
textureAtlas.dispose();
}
}We've also changed the background to a bright blue color:
Creating an animation
Now, we want to replace the static sprite with an animation. TexturePacker can automatically detect animation sprites: If the sprite filenames only differ by a sequence number suffix, TexturePacker will remove this suffix and export all the sprites as animation frames in the data file.
This makes it very easy to create an animation from a sprite sheet:
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.ScreenUtils;
/** {@link com.badlogic.gdx.ApplicationListener} implementation shared by all platforms. */
public class MyGdxGame extends ApplicationAdapter {
private SpriteBatch batch;
private TextureAtlas textureAtlas;
private Animation<Sprite> animation;
private float stateTime = 0;
@Override
public void create() {
batch = new SpriteBatch();
textureAtlas = new TextureAtlas("spritesheet.atlas");
animation = new Animation<>(0.066f, textureAtlas.createSprites("capguy"), Animation.PlayMode.LOOP);
}
@Override
public void render() {
ScreenUtils.clear(0.57f, 0.77f, 0.85f, 1);
stateTime += Gdx.graphics.getDeltaTime();
Sprite sprite = animation.getKeyFrame(stateTime, true);
sprite.setX(stateTime * 250 % (Gdx.graphics.getWidth() + 400) - 200);
batch.begin();
sprite.draw(batch);
batch.end();
}
@Override
public void dispose() {
batch.dispose();
textureAtlas.dispose();
}
}The Animation object acts as a container for animation frames. You can retrieve the frame to display at
a specific point in time using the getKeyFrame method. Note that you need to track the elapsed time externally.
The complete example project is available on GitHub
Using texture compression
Texture compression reduces the amount of GPU memory a texture occupies and can speed up rendering. Unlike PNG or JPEG, which are decompressed into raw pixels before they reach the GPU, compressed textures stay compressed in video memory. This means lower memory usage and faster texture uploads.
libGDX supports KTX files with ASTC, ETC1/ETC2 and DXT (S3TC) compression, which can be generated with TexturePacker. For a full overview of all formats supported by TexturePacker, see Texture compression.
| Format | bpp | Platform |
|---|---|---|
| ASTC | 0.89 - 8 | iOS, modern Android |
| ETC1 | 4 | All OpenGL ES devices (no alpha) |
| ETC2 | 4 - 8 | OpenGL ES 3.0+ |
| DXT1 | 4 | Desktop (1-bit transparency) |
| DXT5 | 8 | Desktop (full alpha) |