Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am trying to use a sprite sheet for animation but I'm having some trouble with my implementation. The three key things that I require my code to do are:

  • Play through a set of images once, for example a jump animation
  • Loop through a set of images continuously, for example a walking animation
  • Change the speed of the animation

I'm currently managing sprite sheets using the following code:

Sprite_Sheet

public class Sprite_Sheet {
    static Image background_sheet_1;
    //more images

    URL url;
    static Main m1;

    public Sprite_Sheet(Main m2) {
        try {
            url = m2.getDocumentBase();
        }
        catch(Exception e){ }
        background_sheet_1 = m2.getImage(url, "image/background/background.png");

        //more images       
        this.m1 = m2;
    }
}

Player

public Player(int ix, int iy) {
        x = ix;
        y = iy;
        player_image = Sprite_Sheet.player_sheet;
    }   

    /*** animate player ***/
    public void animation_player() {    
        int tester = (int)(frame + frame_speed); 
        if(tester < pictures) {
            frame += frame_speed;
        }
        else {
            frame = 0;
        }
    }

Sprites are drawn in the paint method like so:

    g.drawImage(player_image, x, y, x + width, y + height, 50 * (int )frame, 
                266, 50 * (int) frame + 44, 316, Sprite_Sheet.m1);

Am I on the right track with this code? Or is there a better way to load sprite sheets for animation? The code works fine as is, but I don't have much control over the animation, which is a problem.

share|improve this question

closed as not constructive by Josh Petrie, bummzack, msell, Anko, Trevor Powell Apr 12 '13 at 13:43

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.If this question can be reworded to fit the rules in the help center, please edit the question.

    
Really nice edit (Props, Alex!). It's still difficult to see the code's intention though, making this question feel like a request for a complete sprite animation system. I don't think that would make a useful question. –  Anko Sep 10 '13 at 15:47

1 Answer 1

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.

enter image description here

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 :)

share|improve this answer
    
thank you so much!!!! –  guester Apr 13 '13 at 1:22
    
You're welcome :) enjoy –  Savlon Apr 13 '13 at 1:27

Not the answer you're looking for? Browse other questions tagged or ask your own question.