The best way to manage sprite sheet animations?

Started by
21 comments, last by MagnusJ 8 years, 8 months ago

Hello again,

Thank you for the code fragment renman29, it was very enlightning. I didn't realize it was taken to this kind of detail.

I believe state machines can be useful here, and I started to write a reply, but it somewhat exploded in length, and I wasn't even close to the end.

So instead I wrote it all down in a html file. It's not entirely finished, but please take a look, and let me know what you think.

[attachment=28604:state_machines_animation.zip]

Advertisement

I'd just like to say thanks again for all the swift and very helpful replies. I certainly have a lot to work with here!

In regards to everything that's been said, I've already implemented a few of these concepts. I like to keep my sections

separate depending on what they do. For example, I handle all the input, whether that's from a controller, mouse, keyboard, or

AI command, then convert that into some kind of intent.

The intent can then be worked with once, so everything down the line doesn't have to keep checking for different types of input!

This intent can then drive the states of my entities, so, for example, say it has an enum for its different states (walking, still, jumping, attacking1,

attacking2, etc), the methods called after these checks don't have to check against 10-20 different variables, and instead just

check if the entity's state is a certain way.

For example, if I have 10 separate lines of code that need to know if I'm standing, it's better to have:

if (this, that, something else, another thing, some variables doing stuff, this is on, etc)

{

currentState == PhysicalStates.Standing;

}

//Then further down the code

if (currentState == PhysicalStates.Standing)

{

//Do something hilarious

}

Since every check to see if I'm standing only has to check the PhysicalStates enum. So in that respect, my code is data-driven

to some extent. I also separate the animation completely into its own method, so that the movement, collisions, etc, don't have bits

of animation code wedged in. I originally did that, and it was a nightmare to maintain the animation, especially when adding new

animations in that I hadn't previously accounted for. I'd have to change the code massively to account for a single line.

Using this approach, I then have a ChangeAnimation() method in each entity (abstractly inherited) which separately checks if

the animation should change after all the logic nitty gritty code has been executed.

I hope this helps anyone who's having trouble with animation themselves.

I have to say that Alberth's state machines write-up is immensely helpful! I think that implementing two state machines (one for logical states

and another for animation states) is the best way for me to go. As I previously said, my code worked, but it was a messy jumble of if nests.

It's a relief to know that there's no "simple" solution to this problem. But if I implement the state machine concept, I'm pretty sure there's no way

the animations could go wrong.

I'm also going to change my animation class to simply have preset animations. At the moment, it's more like:

if (currentState == PhysicalStates.Dying)

{

animation.startPosX = 2;

animation.startPosY = 3;

animation.playSpeed = 5;

animation.replay = false;

}

After looking into this, it seems a much more maintainable approach would be to link these variable changes to their own

state, so a newer version could simply be:

if (currentState == PhysicalStates.Dying)

{

animation.Play("death");

}

The "death" animation would then essentially play an animation which was already defined as:

animation.startPosX = 2;

animation.startPosY = 3;

animation.playSpeed = 5;

animation.replay = false;

That's about my take on it anyway! Does this seem like the practical way to go about it? I want to make sure

I fully understand where people are going with this :)

Yep, state machines are a serious tool in the box. But they also bring own problems. One big thing is that they tend to become unmanageable quickly due to state count explosion. That has led to mixed forms (e.g. the state is partly external to state nodes) and variants like the hierarchical state machines.


The "death" animation would then essentially play an animation which was already defined as:

animation.startPosX = 2;
animation.startPosY = 3;
animation.playSpeed = 5;
animation.replay = false;

That's about my take on it anyway! Does this seem like the practical way to go about it? I want to make sure
I fully understand where people are going with this

I'm not totally sure about the meaning of the shown lines of code. A neat thing about data driven design is that code need not necessarily be touched when changing things. Part of it is to load control data with the resources. E.g. if a sprite sheet is loaded then not only its texture but also its frame description table is loaded. If the sprite sheet is used as a flipbook animation, then also the sequence of frames and their basic timing should be loaded with the animation clip.

However, if you have an explicit initialization phase where the control data are written once because of the lack of a full resource loading system, then IMHO its fine so far, too.

You already have a state explosion, it just doesn't look like a state machine, but takes the form of nested if/else branches and assignments, where you handle input, game character state, and animation state all at the same time.

The trick to avoid that, is to have more than one state machine (one for the game character, and one for the animations). That way you can think and define each aspect separately. The computer will then merge the states in runtime, where you don't care about the number of possible combinations, as there is always only exactly one combination active.

Alternatively, you merge the state machines off-line, but you typically need a code generator for that, and indeed you get the combinatorial explosion back, but it's likely not so big here, as game character states and animation states are tightly coupled. Also, if you generate it, it doesn't matter much as long as it fits in memory and the compiler can process it.

Edit: White space fix


The "death" animation would then essentially play an animation which was already defined as:

animation.startPosX = 2;
animation.startPosY = 3;
animation.playSpeed = 5;
animation.replay = false;

In more conceptual terms, these values should not be defined in the source code. You need to use external data to define your animations, something like json (which has many parsers and is easy to work with and not overly-verbose like XML). Then, you'd define your animations like this:

{"animations":[{"name":"death", "spritesheet":"death.png", "startPosX":2, "startPosY":3, "playSpeed":5, "replay":false}, 
                       {"name":"walking", "spritesheet":"walking.png", "startPosX":1, ...} ...]
}

Obviously, it can be more than that, but doing it that way you don't have to re-compile every time. Then, in your animations constructor (or Init, whatever), you can load all the animations and define them using the animation json file(s).

(Oh, I see Haegarr said this, but I wanted to give an example).

Good luck!

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

How exactly would you guys recommend going about attaching an animation to a key word? My animation

class contains all the information it needs for different animation strips. But say I made a method that takes a string:

//In animation class

public void SetAnimation(string inString)

{

//Attach this string to a particular animation strip when initializing

//Would only be called once

}

public void Play(string inString)

{

//Play pre-defined animation strip linked to the string

}

What would you guys recommend to link the string to all the variables required for a whole animation: eg. startPosX, startPosY, frames, playSpeed, etc

Don't use strings, use animation objects. It looks like you're programming in Java, maybe an enumeration is best:


public class AnimationData {
    boolean loaded = false; // Set to true if loaded.
    
    public int  startPosX;
    public int startPosY;
    public int playSpeed;
    public boolean replay;
    
    public static enum Animations {
        FOO,
        BAR;
        
        private Animations() {
        }
        
        public final AnimationData animData = new AnimationData();
    }
}

You can reference the animation by the enum value FOO or BAR

The enum value has a 'animData' field contain all stuff you have for the animation.


public void play(Animation a) { play(a.animData); }
public void play(AnimationData a) {
  // use the data
}

play(FOO);

Edit: Added a second play method

I'm going to try this new approach! Should hopefully have quite a bit of free time this weekend, so I can put it to use.

I've started redoing a load of my code to be more data-driven and I've also started working on a state-managing system.

Will keep you updated with the latest. Thanks again for the help :D

If all the animations are quite simple, I would make such arrays like SpritesheetX[animation][frame] and SpritesheetY[animation][frame], then init all them and go through each frame.

(So you can make them loop, non-loop, or like any short enough algorithm (This makes sense when the same sheet is used differently, that is quite common situation I guess))

This doesn't work well for slow animations though just like that.

For a case of slow animations I would make some counter that calls PerformSlowAnimation every, let's say, 10 ticks.

If the game is simple enough (in terms of animations), it might not matter if all animations are synchronized or not (they will be if you do so), and you can divide all animations onto fast, slow, very slow, etc.

If animation has very different frame rate during its life, you can always do a special method for this case. If there aren't much objects with "unstable" animations, you can design its behavior as just an additional functionality, so it doesn't have to work as fast as main method (so you can make if statements for that, switches, virtual calls or calls by pointers, whatever).

(Just want to add) There are occasions when a string matching is (initially) useful - such as external animation editor which may have some unknown # of named animations and behavior definitions which the code knows how to use. You can pre-cache (store ahead of time) a match to a logic(if then conditions) determined animation. If you want to keep the string format, you can use an unordered map or dictionary (hash tables - very fast) to match the name/description with the desired animation. For animations that will switch frequently though you may want to have it store the matches ahead of time (micro-optimization) to the appropriate enums.

Good luck on your mission. :)

This topic is closed to new replies.

Advertisement