How to set up transitions between different animations(architecture).

Started by
10 comments, last by Heelp 7 years, 4 months ago

What's up bros.

I need to figure out a way to switch between different animations in a simple way. I've got working code now, but it's really ugly, that's why I was wondering if there was another, better solution.

The deal is that every function I create needs to simulate one frame of behaviour at a time, I can't just stay in the function forever, because I need to render too. And this is really bothering me, I can't wrap my head around this, because it's not the normal programming I'm used to. For example, in order to switch from IDLE animation to ATTACK, I need to first check if IDLE animation is over, and exactly then change to ATTACK, and then wait for ATTACK animation to be over, and only then switch back to IDLE. I'm using timers, of course, but still, my code is messed up.

Can you propose something that will do the trick, and also will be simple to implement?

Thanks for reading. :rolleyes:

EDIT: My code for now is this: ( it works, but I don't see how someone else will understand that code )


void Character::updateAnimation() //Executed once every frame
{
    animationOver = false; // animation is not over yet

    // If I have requested a new animation from some other function and it is different that the current one.
    if( currentAnimState != requestedAnim )
    {
        // If the current animation is aaalmost over, just a little bit before over, only then you can change to another animation.
        if( animTimer.GetRunningTime() >= getCurrentModel()->getMaxAnimationTime() - animRecoil || currentAnimState == int( Anim::IDLE ) )
        {
            animationOver = true; //The previous animation is over
            currentAnimState = requestedAnim; //Change the current animation state
            setCurrentModel( models[ int( currentAnimState ) ] ); // set the new animation
            animTimer.startNow(); //Start the new timer
        }
    }

    if( currentAnimState != int( Anim::IDLE ) ) //If animation is different from Idle, switch back to Idle, when it's over.
    {
        requestedAnim = int( Anim::IDLE );
    }
}
Advertisement

State machines are the usual tool for this. Unity and unreal both use them for plotting out a graph of animations and edges(transitions) if you want to look up their documentation and tutorials it might give you a better idea of how they are used. Though there might be other methods.

https://en.wikipedia.org/wiki/Finite_state_machine

http://www.gamedev.net/page/resources/_/technical/game-programming/state-machines-in-games-r2982

http://gameprogrammingpatterns.com/state.html

I wrote an article about this, a while ago
http://www.gamedev.net/page/resources/_/technical/game-programming/from-user-input-to-animations-using-state-machines-r4155

Pro tip: the entirety of game programming is managed with state machines. Everything needs its own data that remembers what state it is in, because you need to keep looping and calling Update() 30 or 60 times a second.

Your existing example has an explicit animation state (currentAnimState), implicit state transitions ("I have requested a new animation from some other function"), and each state has an implicit continuous substate (animTimer.GetRunningTime() - note that 'animationOver' isn't actually needed in your code above).

Personally I'm not convinced you need an explicit state machine of your own for this particular issue since much of it is handled inside the animation system. The only problem with your existing code is that it's a bit messy. The pseudocode could be significantly improved just by factoring out functions like IsEndOfCurrentAnimation and StartNewAnimation.

Satharis, Alberth, thanks for the links, I'll read the articles. I saw the Unity articles before posting here, but I saw the term "State Machine" and that scared me off.

Pro tip: the entirety of game programming is managed with state machines. Everything needs its own data that remembers what state it is in, because you need to keep looping and calling Update() 30 or 60 times a second.

So you say that this:


class Dot
{
   int x;
   int y;

   void moveDot()
   {
      x += 5;
   }
}

So you say that this is what people call a "state machine"? Because if this is it, then I can make state machines too :lol:

Perhaps I was overreaching with the terminology - especially if we talk about "finite state machines" where the number of states is normally small and fixed. But the general situation where you need to store state somewhere to record what something is doing, rather than just stay within a function while that action completes (e.g. movement to a location, or playing a whole animation), is standard to gamedev and quite uncommon in other types of software development.

Perhaps I was overreaching with the terminology - especially if we talk about "finite state machines" where the number of states is normally small and fixed. But the general situation where you need to store state somewhere to record what something is doing, rather than just stay within a function while that action completes (e.g. movement to a location, or playing a whole animation), is standard to gamedev and quite uncommon in other types of software development.

I think it would be better described as the fact things are in different states and states can represent behaviors carried out over lengths of time.

State machines are just a design idea people came up with to model the concept of a graph of states something can be in and how they can move between them. They sound more scary than they are. At their core you have to used them for any game. Think about any door in a game that you can open, it has to have a state, it gets drawn every frame and it has the ability to swing open and closed. Unless you track the state it is in then everytime the game goes to render it, it'd just be closed or open. Change requires data.

But yeah that's one thing you have to pick up pretty fast in game dev is unlike pretty much every other software discipline you have to think about: A. Things running in real time. B. Actions being taken over multiple function calls, like a screen fading between transitions. Usually when you're planning you have to take into consideration how long a thing(a player, a camera, a system) will be doing something for.

They sound more scary than they are

True. I did some research and the idea seems to be the following: I have some specified number of states that I can switch to, and every state has a couple of states that it can transition to.

And then if I want to return to the previous state, I store some number of previous states on some stack/storage, and when the action is over, I pop it. And if the game gets fat, maybe I'll need to make a class for every state and switch between objects, and if I see that I copy/paste too many if-statements, maybe inherit some stuff, and I need to be careful not to inherit too much because I gonna cry later.

Am I getting the basic idea?

Personally I'm not convinced you need an explicit state machine of your own for this particular issue since much of it is handled inside the animation system.

Where did you hear that explicit/implicit/continuous stuff, Kylotan? (I can't find it)

Alberth, you have 2 state machines, I want only one for now, I think, because the states seem fully synchronized with the animations.(for now)

I will fix some code and post it. Not that you are dying to see it, but still...

Kylotan, now it looks a lot better, thanks.


void Character::updateAnimation()
{
    //If requested animation is different than the current one.
    if( currentAnimState != requestedAnim ) 
    {
        if( isEndOfCurrentAnimation() || currentAnimState == int( Anim::IDLE ) )
        {
            startNewAnimation( requestedAnim );
        }
    }

    //Return back to idle when current anim is over. Does the job for now.
    if( currentAnimState != int( Anim::IDLE ) ) { requestedAnim = int( Anim::IDLE ); }
}

void Character::changeAnimationTo( int anim )
{
    requestedAnim = anim;
}

void Character::startNewAnimation( int requestedAnim ) //Change model and reset timer.
{
    currentAnimState = requestedAnim;
    setCurrentModel( models[ int( currentAnimState ) ] );
    animTimer.startNow();
}

bool Character::isEndOfCurrentAnimation()
{
    //If animation is aaalmost over, return true.
    if( animTimer.GetRunningTime() >= getCurrentModel()->getMaxAnimationTime() - animRecoil )
    {
        return true;
    }
    return false;
}

True. I did some research and the idea seems to be the following: I have some specified number of states that I can switch to, and every state has a couple of states that it can transition to.

The links explain more than I could here really but yes, think of it like a web of states connected by roads. The roads are the edges, and the edges might have requirements for you to travel along them. So in a way you can encapsulate the idea of a state as "check if I can travel along any of these roads, if I cannot then I should do the activity in the state I am in."

And then if I want to return to the previous state, I store some number of previous states on some stack/storage, and when the action is over, I pop it. And if the game gets fat, maybe I'll need to make a class for every state and switch between objects, and if I see that I copy/paste too many if-statements, maybe inherit some stuff, and I need to be careful not to inherit too much because I gonna cry later.

Whether or not you use a stack is kinda up to you and what kind of states you are making. You don't HAVE to use a state machine, it's just a concept to help create generic states and generic transitions between those states. You could write an entire game using code similar to what you have and it isn't -wrong- it just will get bulky rather fast. You DO have to track state in games, that's a requirement.

Where did you hear that explicit/implicit/continuous stuff, Kylotan? (I can't find it)

What I believe he means is that you are already working with the concept of "states." You change some variable to control the flow of your code. The alternative is creating a dedicated object(a state machine) that handles which state is active, the transitions between them, etc.

but I saw the term "State Machine" and that scared me off
There is the choice between using the name of something, or write a 15 lines description that hopefully adequately covers its name. The latter doesn't really fit in a title, and Wikipedia does a better job at explaining, most of the time.

And then if I want to return to the previous state, I store some number of previous states on some stack/storage, and when the action is over, I pop it.
The more basic version is that you don't have a stack or storage, you just have an edge back to the previous state.

Where did you hear that explicit/implicit/continuous stuff, Kylotan? (I can't find it)
Kylotan is speaking of state in a computer program, and comparing that with state in a state machine, in the way you program that in a computer with a class. It likely exists at the Internet, but it's not quite findable, as it's at the boundary of two areas, in context of practical programming.

What it means is that having an explicit state machine is kind of expensive. In the extreme form, you make a class for each state, and instantiate it. You have a 'state machine' class, containing the state objects, and managing jumping around in the state.

Now suppose you do that for the door example of Satharis. You'll have an Open class, an open_object, a Closed class, a closed_object, a Statemachine class, and a state_machine object. An alternative is a boolean "is_open". In both cases, you can express the state of the door (wrt to open/closed), and you can draw a door in the correct position. The second solution however is a bit fewer lines of code, around 90% less.

What this means, is that state machines do have expenses, at times, it's simpler/cheaper to use simple variables to express your state.

That doesn't mean state machines are useless. They provide a framework to think about your system while you design it. Even if, in the end, you don't actually encode the state machine in the program as classes or objects but eg with variables and if statements, it does help structuring your thoughts.

(Food for further thought, a program is also a state machine.)

Alberth, you have 2 state machines, I want only one for now, I think, because the states seem fully synchronized with the animations.(for now)
You can skip synchronization then, it should be a lot simpler :)

This topic is closed to new replies.

Advertisement