Jump to content
  • Advertisement
Sign in to follow this  
Dirk Gregorius

Animation State Machine Transitions Implementation

This topic is 561 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Say we have a simple animation state machine. The state machine is described by some simple nodes with transitions between them. Transitions have conditions that need to be satsified in order to take a transition. Say we have a simple IDLE and WALK state with a transition between them. The transition has a simple condition SPEED > 0. Now the game sets SPEED = 1. What happens here is that the state machine instantaneously changes to WALK. For the underlying animation system the change is not so instantaneous. First we need to blend from idle to walk and then continue the walk animation. 

 

How is this effectively implemented? In practice the WALK state is not necessarily a simple animation, but can be a complex blend tree or an ASM itself.

Share this post


Link to post
Share on other sites
Advertisement

Some time ago (woops, more than a year, how time flies), I wrote

http://www.gamedev.net/page/resources/_/technical/game-programming/from-user-input-to-animations-using-state-machines-r4155

 

this handles user input, and how you switch animations from that input at the right moment.

 

The basic idea is that you see the animation as another state machine, and synchronize on taken edges, when a) the input machine wants to switch, and b) the animation state machine allows to switch.

 

Hope this helps,

Share this post


Link to post
Share on other sites

Yup. I saw this post. I am interested in the details of the TakeEdge() function. E.g. in the article you say: 

 

There is also a function TakeEdge(<state-machine>, <current-state>, <edge-name>) that takes the edge with the given name in the state machine, performs the assignments, and returns the new current state.

 

How can you return the new state? At least from the perspective of the animation this takes time. I see that I could add two assignments though:

 

1) Blend to target state

2) Run target state

 

Still I am interested in implementation details on this problem. Conceptually I see something like this:

void State::Update()
{
   // Find edges that can transition and choose one (e.g. based on priority)
   if ( !mTransition )
   {
      mTransition = CheckEdges();
   }
  
   // Are we transitioning to a new state
   if ( mTransition )
   {
      // Run the transition to the new state. This is not instantaneous for the animation 
      if ( mTransition->Update() == FINISHED )
      {
      mASM->SetState( mTransition->GetTarget() );
      }

   return;
   }

   // Nothing is happening so simply update current animation...
}
Edited by Dirk Gregorius

Share this post


Link to post
Share on other sites

One way would be to create transition states to represent the edges, but if I'm understanding you correctly then you could just have a cur_state and goal_state, then to transition you do something like:

 

cur_state = goal_state

goal_state = new_goal

state_trans_start_time = now

state_trans_end_time = state_trans_start_time + whatever

 

update  'remaining' as you go and then get the ratio:

 

trans_ratio = (state_trans_end_time - now) / (state_trans_end_time - state_trans_start_time)

 

and interpolate based on that value to blend the animations. (Make sure to check the finished condition.)

 

Unless I'm misunderstanding the question?

Share this post


Link to post
Share on other sites

Which module would do this? In the state machine itself or one layer above? And then also, when does the SM current state change. Immediately or after the transition is finished?

Share this post


Link to post
Share on other sites

This is (the shallow end of) fuzzy state. There doesn't necessarily need to be a definite single state that you are "in", just a membership value for states that are relevant. The fine details depend on what you're animating, what transitions are allowed and when, and what information you need from the SM.

 

In other words, you have to decide that yourself, based on your needs. You can limit the transition rate with game mechanics or you can blend more than two states, or have unique rules for each transition, etc.

Edited by Khatharr

Share this post


Link to post
Share on other sites

I would say that your transition is also a mini state machine, with an "in a state" condition, a transition for starting a transition, a state indicating it is in the middle of a transition, and a transition to be done with the condition.

 

Games use state machines for lots of thing, and this type of thing is not uncommon for items that need to be in transition over time.  

Share this post


Link to post
Share on other sites

Yup. I saw this post. I am interested in the details of the TakeEdge() function.

 

There is also a function TakeEdge(, , ) that takes the edge with the given name in the state machine, performs the assignments, and returns the new current state.

How can you return the new state? At least from the perspective of the animation this takes time.

Edges are instantaneous and atomic. You spend time in a state, not in an edge.

 

If you find you want to spend time in an edge, you can always add a new intermediate "do thing" state, and have a "start thing" edge to the intermediate state, and "end thing" edge from the intermediate state to the original destination of the edge.

 

I think you don't actually need such an intermediate state though.

 

 

I am not exactly sure what semantics you have attached to SPEED values. "SPEED=1" suggests to me you start changing x and y coordinates. However, in my view, you don't control that data, the animation knows how and when to change coordinates.

So instead, your "SPEED" should be seen as "desired_speed", ie expressing the wish to move. Setting "desired_speed" to 1 doesn't mean you start moving immediately. It's only a message to the animation state machine that it should work towards performing movement.

 

The latter could look something like

[attachment=34351:walk_states.png]

 

Each state represents "displaying frames of an animation". The edges are listed below.

Init: (initialization of the variables)
    int frame = 0; // Current displayed frame
    SetIdleAnimation();
    SetAnimationFrame(frame);

RepIdle: (repeat idle animation)
    condition:
        (frame == LastIdleFrame && desired_speed == 0)

    update:
        frame = 0;
        SetAnimationFrame(frame);

Tick: (advance to next frame)
    condition:
        true

    update:
        frame = frame + 1
        SetAnimationFrame(frame);
        // Update (x,y) position if required

Ssw: (start start-walking)
    condition:
        (frame == LastIdleFrame && desired_speed > 0)

    update:
        frame = 0;
        SetStartWalkAnimation();
        SetAnimationFrame(frame);

Sw: (start walking)
    condition:
        (frame == LastStartWalkFrame && desired_speed > 0)

    update:
        frame = 0;
        SetStartWalkAnimation();
        SetAnimationFrame(frame);

Aw: (abort walk)
    condition:
        (frame == LastStartWalkFrame && desired_speed == 0)

    update:
        frame = 0;
        SetStopWalkAnimation();
        SetAnimationFrame(frame);

RepWalk: (repeat walking, ie do next step of the character)
    condition:
        (frame == LastWalkFrame && desired_speed > 0)

    update:
        frame = 0;
        SetAnimationFrame(frame);

Stw: (start stop walking)
    condition:
        (frame == LastWalkFrame && desired_speed == 0)

    update:
        frame = 0;
        SetStopWalkAnimation();
        SetAnimationFrame(frame);

Si: (start idle)
    condition:
        (frame == LastStopWalkFrame && desired_speed == 0)

    update:
        frame = 0;
        SetIdleAnimation();
        SetAnimationFrame(frame);

Rw: (restart walking)
    condition:
        (frame == LastStopWalkFrame && desired_speed > 0)

    update:
        frame = 0;
        SetStartWalkAnimation();
        SetAnimationFrame(frame);

You stay in a state, displaying an animation, frame after frame (Tick advances to the next frame). At the end, of the animation, you jump to the next animation to display, depending on the value of the desired_speed.

 

Here I used the "desired_speed" variable to express what the animation system should work towards. If you want to have a separate state machine for expressing this, that's also possible. You have to synchronize with event-names then. In the above edges, take out all the "desired_speed" conditions, and add the event-name "TowardWalk" on all edges with the condition "desired_speed > 0". Add the event-name "TowardIdle" on all edges with the condition "desired_speed == 0".

Then add the following state machine (synchronizing on "TowardWalk" and "TowardIdle"):

[attachment=34352:toward.png]

 

The animation state machine switches to the next animation at the end of each animation. It does so by using an edge labeled "TowardIdle" or "TowardWalk". The above state machine limits the choice towards a single direction, since it can do only one of the events, and it synchronizes with the animation state machine.

This is what KhatHarr proposed too, I think, but more formalized. This kind of separation starts to pay off when you have more goal states you may want to work towrds, and the conditions of switching goal states are more complicated.

 

 

Edit: Sorry for the small images, I saw that too late.

Edited by Alberth

Share this post


Link to post
Share on other sites

Thanks everybody! Alberth, what you write makes a lot of sense! I think the only problem I have is to use two state machines. How about we have the state machine only know about state. E.g. idle and walk. And changes are instantaneous. Now I move the joystick forward and speed goes to one. The state machine signals switching state from idle to walk. I now describe the entire animation in a blend tree. I capture the event and add a blend node which blends from idle to walk and when it is done it continues with walk (or whatever has a 1.0 weighting). 

 

It is probably a matter of choice and both work fine. 

Edited by Dirk Gregorius

Share this post


Link to post
Share on other sites

In my approaches transitions are generally modeled as states, and changing states happens instantaneously.

 

The example in the OP may then be modeled with

a) a stable idle state;

b) a transitional state that replays a canned animation to bring the skeleton into a defined motion phase with a defined speed;

c) a transitional state that interpolates over time from the current speed to the goal speed, setting appropriate animation clips for blending;

d) a stable state that continues the walk cycle with the reached speed.

 

Alternatively to c) and d) there could be:

e) a stable state that regulates the speed in dependence on the difference between current speed and goal speed, hence running a continuous blending.

 

However, such a state machine is not only controlling animation since it also controls locomotion. Its granularity w.r.t. states is derived from animation needs, and hence does not reflect the difference between e.g. "still" and "moving" very well. When understood as component of a locomotion system, then states like b) can be seen as synchronization points where control is granted for a short time to the animation system. When control is returned, the state machine immediately enters state e).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!