The Coder's Handbook
Sprites and Animation
SPRITE SHEETS
For more detailed information, look at the Image and Sprite Sheet classes in the Slick 2D Javadoc
What Is A Sprite Sheet?
Sometimes in your program you are going to have a lot of images. The most common scenarios for this are (1) Animations and (2) Terrain Tiles. You certainly can get by just loading individual images. But, this involves using a lot of files and can be rather cumbersome.
Instead, you can use a Sprite Sheet which is a single image that has all of your sprites organized in a line or a grid. Consider two examples below.
Robot Animations Sprite Sheet
Each row represents a single type of animation. From top to bottom: walk, idle, attack, death 1, death 2.
Each column represents a single frame in that animation. Following left to right you can see a progression in each animation.
This means that if I reference a robot[2][3], I know that is going to be:
The third type of animation (attack)
The fourth frame in that animation sequence.
Tileset Sprite Sheet
Each row represents a single type of terrain. From top to bottom: stone bricks, sand, grass, dirt, sandstone bricks.
Each column represents a different type of tile. For instance, we have night and day versions of different patterns of tiles.
This means that if I reference tile[2][3], I know that it is going to be:
The third type of tile (dirt).
The fourth version of that tile (night type).
How do I make one?
First, you load in an Image, then you'll create a Sprite Sheet from that Image.
When initializing the sheet, you'll specify each individual sprite's size in pixels. They must be uniform.
You can then use the getSprite() method to access an individual sprite from the sheet.
Note that we don't use pixels here, just the index position.
Example Code:
import org.newdawn.slick.Image;
import org.newdawn.slick.SpriteSheet;
// Declare the Image and SpriteSheet
private Image tileset;
private SpriteSheet tiles;
public void init(GameContainer gc) throws SlickException
{
// Initialize the image and sprite sheet
tileset = new Image("res/terrain.png");
tiles = new SpriteSheet(tileset, 32, 32);
}
public void render(GameContainer gc, Graphics g) throws SlickException
{
// Draw the image at the 3rd position horizontally and 2nd position vertically
// Appears at (100, 100)
tiles.getSprite(2, 1).draw(100, 100);
}
The SpriteSheet has other handy methods. Two notable ones are getVerticalCount() and getHorizontalCount() to tell you how many elements are in each sheet, so you can avoid hardcoding.
This isn't helpful at all.
ORGANIZATION
Load Images in A Different Class
Usually, you'll want to animate an object in its own class. Yet that poses a problem. When should you load the image? In the object's constructor? But what if you make a hundred robots? Do we load the image a hundred times? This would be very inefficient.
Instead, consider making a class like Images, shown in the example below. You can make one - or multiple - methods to load sets of images all in one big batch. This can be done at the start of the program, or between levels when they're about to become relevant.
Note that Images is a singleton, and we simply use static method and data. If that part is confusing, review static in the handbook on Object Oriented Programming.
ANIMATION
Making Animations With Sprite Sheets
Keep track of your current step as you move through the animation.
Every few frames that pass, increase your step by one.
If your step exceeds the number of frames, reset it to zero to loop the animation.
My example aims to be clear + simple, but isn't the most compact solution. If your method is shorter, that's great!
Example - The Walking Knight
Game Class - Your main gameplay state
public class Game extends BasicGameState
{
Knight knight;
init() method
{
Images.loadImages();
knight = new Knight();
}
render() method
{
knight.render();
}
update() method
{
knight.update();
}
}
Images Class - Loads images for your program
public class Images
{
public static Image knightImage;
public static SpriteSheet knight;
public static void loadImages() throws SlickException
{
knightImage= new Image("res/knightSideWalk.png");
knight = new SpriteSheet(knightImage, 32, 64);
}
}
Knight Class - An example of an object you want to animate
public class Knight
{
private SpriteSheet mySheet;
private Image currentFrame;
private int step = 0;
private int frames = 0;
private int framesPerStep = 6;
public Knight()
{
mySheet = Images.knight;
currentFrame = mySheet.getSprite(0, 0);
}
public void update()
{
frames++;
// Every few frames, update your step
if(frames % framesPerStep == 0)
{
step++;
}
// Repeat if you reach the end
if(step >= mySheet.getHorizontalCount())
{
step = 0;
}
// Set the current frame based on your step
currentFrame = mySheet.getSprite(step, 0);
}
public void render()
{
// Draw the current frame at a fixed position
currentFrame.draw(500, 500);
}
}