The way I do 2D animations is as follows:
Sprite Retrieval Class
This class can either load an image file or it can load a sprite from a sprite sheet. It's very self explanatory.
import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; public class Sprite { private static BufferedImage spriteSheet; private static final int TILE_SIZE = 32; public static BufferedImage loadSprite(String file) { BufferedImage sprite = null; try { sprite = ImageIO.read(new File("res/" + file + ".png")); } catch (IOException e) { e.printStackTrace(); } return sprite; } public static BufferedImage getSprite(int xGrid, int yGrid) { if (spriteSheet == null) { spriteSheet = loadSprite("AnimationSpriteSheet"); } return spriteSheet.getSubimage(xGrid * TILE_SIZE, yGrid * TILE_SIZE, TILE_SIZE, TILE_SIZE); } }
Animation Class
import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; public class Animation { private int frameCount; // Counts ticks for change private int frameDelay; // frame delay 1-12 (You will have to play around with this) private int currentFrame; // animations current frame private int animationDirection; // animation direction (i.e counting forward or backward) private int totalFrames; // total amount of frames for your animation private boolean stopped; // has animations stopped private List<Frame> frames = new ArrayList<Frame>(); // Arraylist of frames public Animation(BufferedImage[] frames, int frameDelay) { this.frameDelay = frameDelay; this.stopped = true; for (int i = 0; i < frames.length; i++) { addFrame(frames[i], frameDelay); } this.frameCount = 0; this.frameDelay = frameDelay; this.currentFrame = 0; this.animationDirection = 1; this.totalFrames = this.frames.size(); } public void start() { if (!stopped) { return; } if (frames.size() == 0) { return; } stopped = false; } public void stop() { if (frames.size() == 0) { return; } stopped = true; } public void restart() { if (frames.size() == 0) { return; } stopped = false; currentFrame = 0; } public void reset() { this.stopped = true; this.frameCount = 0; this.currentFrame = 0; } private void addFrame(BufferedImage frame, int duration) { if (duration <= 0) { System.err.println("Invalid duration: " + duration); throw new RuntimeException("Invalid duration: " + duration); } frames.add(new Frame(frame, duration)); currentFrame = 0; } public BufferedImage getSprite() { return frames.get(currentFrame).getFrame(); } public void update() { if (!stopped) { frameCount++; if (frameCount > frameDelay) { frameCount = 0; currentFrame += animationDirection; if (currentFrame > totalFrames - 1) { currentFrame = 0; } else if (currentFrame < 0) { currentFrame = totalFrames - 1; } } } } }
The animation class has start, stop, restart and reset to control the animation. You could easily add a boolean to test whether it should loop or not. I will not add this for you.
Frame Class
import java.awt.image.BufferedImage; public class Frame { private BufferedImage frame; private int duration; public Frame(BufferedImage frame, int duration) { this.frame = frame; this.duration = duration; } public BufferedImage getFrame() { return frame; } public void setFrame(BufferedImage frame) { this.frame = frame; } public int getDuration() { return duration; } public void setDuration(int duration) { this.duration = duration; } }
The Frame class holds an image and a duration associated with that image.
Implementation
The way I implement the animation is as follows:
Player.java
// Images for each animation private BufferedImage[] walkingLeft = {Sprite.getSprite(0, 1), Sprite.getSprite(2, 1}; // Gets the upper left images of my sprite sheet private BufferedImage[] walkingRight = {Sprite.getSprite(0, 2), Sprite.getSprite(2, 2}; private BufferedImage[] standing = {Sprite.getSprite(1, 0)}; // These are animation states private Animation walkLeft = new Animation(walkingLeft, 10); private Animation walkRight = new Animation(walkingRight, 10); private Animation standing = new Animation(standing, 10); // This is the actual animation private Animation animation = standing;
In your update or tick method you will have:
animation.update();
In your render or draw method you will have:
g.drawImage(animation.getSprite(), x, y, null);
Lets say you have implemented MouseListener
public void mousePressed(MouseEvent e) { animation = walkLeft; animation.start(); } public void mouseReleased(MouseEvent e) { animation.stop(); animation.reset(); animation = standing; }
Closing Notes
Here is a screen shot of the layout of a sprite sheet.

Each tile is 32 x 32 pixels.
Normally I wouldn't write this much code. However, I remember when I was struggling to figure this out and it annoyed the shit out of me... So here you go!
Good luck and I hope you can refactor it to suit your needs :)