Hey there. Try out my AnimationSet class.
package mm;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Iterator;
/**
* An AnimationSet is a single collection of Animations that one Entity might be
* using. It also handles all logic for when one Animation should end or when
* one should replace another. In terms of structure, an AnimationSet is simply
* a HashMap that contains all passed Animations. There will always be a default
* Animation, however, so that it can appear if no other Animations have been
* specified to play.
*
* @author Eli Delventhal
*/
public class AnimationSet
{
/**
* The map that contains all the animations, accessed by a String name.
*/
private HashMap<String,Animation> animations;
/**
* The name (key) of the current animation.
*/
private String currentAnimation;
/**
* The name (key) of the default animation.
*/
private String defaultName;
/**
* Creates an AnimationSet with only a default Animation.
* Subsequent Animations must be added using the addAnimation() method.
* @param defaultNom The name (key) of the default Animation.
* @param defaultAnimation The default Animation. Note that its priority should be very low.
*/
public AnimationSet(String defaultNom, Animation defaultAnimation)
{
animations = new HashMap<String,Animation>();
defaultName = defaultNom;
currentAnimation = defaultName;
animations.put(defaultName, defaultAnimation);
}
/**
* Adds an Animation to the map.
* @param name The name (key) of the Animation.
* @param animation The Animation to add.
*/
public void addAnimation(String name, Animation animation)
{
animations.put(name,animation);
}
/**
* Changes current the Animation to the specified one. This will
* only be done, however, if this is allowed priority-wise.
* @param name The name (key) of this Animation.
*/
public void setAnimation(String name)
{
//If the passed Animation doesn't exist, return with an error message.
Animation newAnimation = animations.get(name);
if (newAnimation == null)
{
System.err.println("Animation \"" + name + "\" not found!");
return;
}
//If the current Animation doesn't exist or is done, don't bother to
//check priority - instead start up the new animation immediately.
Animation current = animations.get(currentAnimation);
if (current == null || current.isDone())
{
startAnimation(name);
return;
}
//If the new Animation has a higher priority than the current one, then
//it's allowed to interrupt it and start.
if (newAnimation.getPriority() > current.getPriority())
{
startAnimation(name);
return;
}
}
/**
* Starts a specified Animation by setting it as the current one and by
* resetting it back to the start. Warning: this doesn't check to see if
* the map contains the passed name; NullPointerException can result.
* @param name The name of the Animation to start.
*/
private void startAnimation(String name)
{
currentAnimation = name;
animations.get(name).restart();
}
/**
* Tells this AnimationSet to "tick." By doing so, it will advance the current
* Animation, and if it's done, it will set the default Animation immediately.
*/
public void tick()
{
Animation current = animations.get(currentAnimation);
current.advance();
if (current.isDone())
startAnimation(defaultName);
}
/**
* Preloads all the Animations in the map.
*/
public void preloadAll()
{
for (Iterator<Animation> i = animations.values().iterator(); i.hasNext(); i.next().preloadAll());
}
/**
* Returns the current image in the current Animation.
*/
public BufferedImage getCurrentImage()
{
return animations.get(currentAnimation).getCurrentImage();
}
/**
* Finishes the current animation by turning on the default, regardless of priority.
*/
public void finishAnimation()
{
currentAnimation = defaultName;
animations.get(currentAnimation).restart();
}
/**
* An Animation holds information for drawing all the different progressions
* of images - the image prefixes, the number of images, and the delays.
* In addition, each Animation has a priority; higher priority animations will
* interrupt lower priority animations.
* Currently, Animation only supports PNG (because that's all I need), but it
* would be trivial to allow other file types.
*
* @author Eli Delventhal
*/
public static class Animation
{
private String prefix;
private String soundPrefix;
private int nFrames;
private int delay;
private int endDelay;
private int priority;
private int currentFrame;
private int lastAdvance;
/**
* Constructs an Animation.
* @param name The name of the folder the images are contained in (with /), or the prefix of the image.
* @param sound The name of the folder the sounds are contained in (with /), or the prefix of the sound.
* @param num The number of images in this animation.
* @param pause The pause between the changing of each image (in turns).
* @param endPause The pause after the animation finishes (in turns).
* @param prioritee The priority this animation has.
*/
public Animation(String name, String sound, int num, int pause, int endPause, int prioritee)
{
prefix = name;
soundPrefix = sound;
nFrames = num;
delay = pause;
endDelay = endPause;
priority = prioritee;
currentFrame = 0;
}
public BufferedImage getCurrentImage()
{
return ImageManager.getImage(prefix + currentFrame + ".png");
}
/**
* Advances the animation. The frame will only increment if it has been
* a long enough period of time since the last increment.
*/
public void advance()
{
int now = Globals.getCurrentTurn();
//Only advance if there has been a long enough pause and
//we're not at the last frame yet.
if (now - lastAdvance >= delay && currentFrame < nFrames-1)
{
currentFrame++;
lastAdvance = now;
if (soundPrefix.length() > 0)
SoundManager.playSound(soundPrefix + currentFrame + ".wav");
}
}
/**
* Returns whether or not this Animation has finished, length and end delay
* both considered.
* @return True / false if this Animation is finished.
*/
public boolean isDone()
{
int now = Globals.getCurrentTurn();
//If we're at the last frame and the pause has been long enough,
//return true to show that the animation is finished.
if (now - lastAdvance >= Math.max(delay,endDelay) && currentFrame >= nFrames-1)
return true;
return false;
}
/**
* Gives the priority of the Animation
* @return The priority.
*/
public int getPriority()
{
return priority;
}
/**
* Restarts the Animation.
*/
public void restart()
{
currentFrame = 0;
lastAdvance = Globals.getCurrentTurn();
if (soundPrefix.length() > 0)
SoundManager.playSound(soundPrefix + currentFrame + ".wav");
}
/**
* Preloads all the frames in this Animation.
*/
public void preloadAll()
{
for (int i = 0; i < nFrames; i++)
{
ImageManager.preloadImage(prefix + i + ".png");
if (soundPrefix.length() > 0)
SoundManager.preloadSound(soundPrefix + i + ".wav");
}
}
}
}
You give each object its own AnimationSet, and create it upon construction. Then you simply call tick() every time you draw. The beauty of using this is that no animations can supersede the others, and you can give them separate priorities.
Oh yeah, I should also note that this enables you to put in images and sounds, and obviously I have ImageManager and SoundManager classes. The AnimationSet allows you to preload all of its images and sounds before use.
The way the images and sounds work is you specify a prefix (like “Images/Dog/walk”) and then every subsequent image has a number value representing the current frame, and is a PNG (although this can be easily changed). So, you would have: Images/Dog/walk0.png Images/Dog/walk1.png Images/Dog/walk2.png Images/Dog/walk3.png etc.