Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


There got to be a better way to manage animations.


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
7 replies to this topic

#1 Sunsharior   Members   -  Reputation: 471

Like
0Likes
Like

Posted 23 March 2014 - 09:29 PM

Somes weeks ago, i've started pouring more hours on the animation part of my game, which was pretty bare up to this point. After sucessfully creating a tool to easily create and modify my 2d animations from a sprite sheet (setting things like sprite rect, crop, hitboxes, weapon mounting point and angle, etc...), i now have to actually do the logic of switching all of the animation the actors have in the game.

 

The code below is my function to change the actor's animation based on his state.

While, i've not actually come to a dead end, i feel what my code is starting to feel tangled and harder to debug.

As you will notice a little bit further down, it'll only get more complicated, as the number of possible animation is very wide.

 

So my question is this: Is there a better way of doing this? Or should i just continue doing all thoses else-if?

I really do not want all the hours i'm doing to go to waste if i have to redo everything, especially all the time spent doing my own animation tool (which is very specific to my game).

//////////////////////////////////////////////////////////////////////////
//
void IActor::ManageAnimation()
{
    Animations id = ANIMATION_IDLE_OOC;
    //If out of combat
    if(!IsState(STATE_INCOMBAT) )
    {
        if(IsState(STATE_JUMPING))
            id = ANIMATION_JUMP_OOC;

        else if(IsState(STATE_FALLING))
            id = ANIMATION_FALL_OOC;

        else if(IsIdle(MAX_IDLE_TIME))
            id = ANIMATION_SLEEP_OOC;

        //Sliding show be shown over moving
        else if(IsState(STATE_SLIDING))
            id = ANIMATION_SLIDE_OOC;

        else if(IsState(STATE_MOVING))
            id = ANIMATION_MOVE_OOC;
    }
    else
    {
        //...
    }

    SetAnimation(id);
}
enum Animations
{
    //////////////////////////////////////////////////////////////////////////
    //Generic animation. Everyone should have theses. It's the job of this class to set them.
    //Out of combat
    ANIMATION_IDLE_OOC,            //Basic idle animation out of combat.
    ANIMATION_SLEEP_OOC,        //Animation for idling too long... Player yawn, wolf lay down and sleep, etc...
    ANIMATION_MOVE_OOC,            //Moving
    ANIMATION_LOOK_UP_OOC,        //Look up
    ANIMATION_LOOK_DOWN_OOC,    //Look down
    ANIMATION_TREATHENED_OOC,    //Transition between out of combat and in combat.
    ANIMATION_JUMP_OOC,            //Jump
    ANIMATION_FALL_OOC,            //Falling
    ANIMATION_SLIDE_OOC,        //When the actor is changing direction or sliding down a slope.

    //In combat
    ANIMATION_IDLE_IC,            //Basic idle animation in combat.
    ANIMATION_MOVE_IC,            //Moving
    ANIMATION_LOOK_UP_IC,        //Look up
    ANIMATION_LOOK_DOWN_IC,        //Look down
    ANIMATION_TREATHENED_IC,    //Transition between in combat and out of combat.
    ANIMATION_JUMP_IC,            //Jump
    ANIMATION_FALL_IC,            //Falling
    ANIMATION_SLIDE_IC,            //When the actor is changing direction or sliding down a slope.

    //generic
    ANIMATION_HURT,
    ANIMATION_CRITITAL_HURT,
    ANIMATION_DIE,
    ANIMATION_DEAD,
    //End of generic animation
    //////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////////
    //Specific animation. Not everyone have them. It's the job of the Actor's interface to set them. thoses animation can all be canceled by us.
    //Reserve for physical attack with a weapon or unarmed.
    ANIMATION_ATTACK_1,
    ANIMATION_ATTACK_2,
    ANIMATION_ATTACK_3,
    ANIMATION_ATTACK_4,
    ANIMATION_ATTACK_5,

    //Reserved for Spell animations
    ANIMATION_SPECIAL_1,
    ANIMATION_SPECIAL_2,
    ANIMATION_SPECIAL_3,
    ANIMATION_SPECIAL_4,
    ANIAMTION_SPECIAL_5,

    //Reserved for social animations. could be laugh, cry, angry, yell, etc...
    ANIMATION_SOCIAL_1,
    ANIMATION_SOCIAL_2,
    ANIMATION_SOCIAL_3,
    ANIMATION_SOCIAL_4,
    ANIMATION_SOCIAL_5,
    //End specific animation
    //////////////////////////////////////////////////////////////////////////
};

Edited by Sunsharior, 23 March 2014 - 09:31 PM.


Sponsor:

#2 Krohm   Crossbones+   -  Reputation: 3167

Like
4Likes
Like

Posted 24 March 2014 - 12:05 AM

Your whole problem arises because you're trying to enumerate the animations. This is a fundamentally wrong way to a approach the problem. Animations states are strings, and they are asset-specific. You don't map them to uints, you can do it, but it really isn't a sensible way of authoring assets.

Animation names are strings. So you can have very specific animations such as PlayModelAnimation("cutscene4_bridgeCollapsed_teammateAlive") where your avatar and the sidekick note how they were doing great so far, but also have to cut another few seconds next time.

All those specific needs will give you serious issues.

But... let's suppose you have a filter cooking those strings to uints. Then your solution is to have that same filter build you an std::map<const char*, uint>.

I'm having myself some problems however in understanding how this could work for you since your IsState function seems to be smarter than I'd suggest to - so something is falling... or jumping... in combat or not. So basically IsState does non-trivial checking of variables which are of your own design.

In my design, I don't set animation as part of the loop. I start them event-based so no state polling is required and I found this to be a rather good solution so far.



#3 LorenzoGatti   Crossbones+   -  Reputation: 2733

Like
4Likes
Like

Posted 24 March 2014 - 02:20 AM

Your animation management appears to be a finite state machine, which doesn't need to be hardcoded with constants and ifs and enums.

Instead, you should define a data structure to represent animation definitions, loaded from external files and processed by a generic animation manager. Your current ManageAnimation method is in class IActor, but it isn't generic: it assumes a number of states and events exist.


Produci, consuma, crepa

#4 DareDeveloper   GDNet+   -  Reputation: 963

Like
3Likes
Like

Posted 24 March 2014 - 02:25 AM

You can try another approach to see if you would run into problems:

 

Come up with feature ideas that you might want to add at some point and think about what API you want to work with for those features.

Things like animation blending, playing at different speeds, pausing, aborting etc.

 

Where would you store what the currently shown frame is? I guess that would be another member variable in the IActor class?

 

I think I'd put most of the logic into an Animation class and let the Actor manage the currentAnimation reference (set new Animation objects, forward the time that has passed to it etc.).


Given enough eyeballs, all mysteries are shallow.

ProcGames.com


#5 mikeman   Members   -  Reputation: 2248

Like
3Likes
Like

Posted 24 March 2014 - 05:07 AM

Well, another solution you could use that would make the code a bit cleaner without changing the logic too much would be to implement a module which "chooses" which animation should be played based on registered rules and conditions. This is basically what you do here, deciding the animation based on the state(s) the character is in, with the difference that now is hardcoded.

 

So you could have something like this:

 

 
struct AnimationRule
{
    std::set<int> states;
    std::set<int> excluded_states;
    int animation_id;
};
 

 

Each rules contains a set of the states that need to be active and a set of the states that need to be inactive in order for animation_id to be played.

 

Then you can basically feed the Animation Manager a list of an arbitrary number of rules, using some kind of priority system(could be an explicit number you assign to each Rule, or just based on the order you register them), and the manager would evaluate those and have the first rule that has its conditions satisfied play the corresponding animation_id. That way at least you get rid of all the hardcoding, and you can even encode the whole thing in data files.



#6 Hauck   Members   -  Reputation: 223

Like
1Likes
Like

Posted 24 March 2014 - 07:43 AM

I've run into a similar issue a while ago and I someone linked me this article:

 

http://lazyfoo.net/articles/article06/index.php

 

Not sure if it'll help you, but perhaps you may find an alternative solution for your problem.

I hope it helps :)



#7 Sunsharior   Members   -  Reputation: 471

Like
1Likes
Like

Posted 24 March 2014 - 09:17 AM

Thank you for your reply, Krohm (and everyone else). Quoting you, I will explain a bit further how i currently handle my animations.

 

 

Your whole problem arises because you're trying to enumerate the animations. This is a fundamentally wrong way to a approach the problem. Animations states are strings, and they are asset-specific. You don't map them to uints, you can do it, but it really isn't a sensible way of authoring assets.

Animation names are strings. So you can have very specific animations such as PlayModelAnimation("cutscene4_bridgeCollapsed_teammateAlive") where your avatar and the sidekick note how they were doing great so far, but also have to cut another few seconds next time.

 

I do have a "PlayAnimation" function that is called during cutscene. this function do not depend on the state of the actor, as it is only processing scripted animations. But this is somewhat irrelevant to my problem here.

What i'm describing in my post is during normal gameplay (Nothing scripted here). This depend heavily on the state of the actor, for example, if he is currently underwater, or jumping, or swiging his sword, etc... What im trying to do here is, at the end of my frame, i check what happened to the actor as a whole and set the animation correctly. Let say in a single frame, the actor moved right, but also got hurt, i need to prioritize showing the hurt animation for the next couple frames before resuming showing moving right.

 

 

I'm having myself some problems however in understanding how this could work for you since your IsState function seems to be smarter than I'd suggest to - so something is falling... or jumping... in combat or not. So basically IsState does non-trivial checking of variables which are of your own design.

 

IsState is simply an array of boolean that contain the state of a very specific actor. It can be things like "in combat", "falling" or "jumping". A complimentary function SetState does the work during user input (or AI) to set the appropriate states.

 

 

In my design, I don't set animation as part of the loop. I start them event-based so no state polling is required and I found this to be a rather good solution so far.

 

If i could do the same thing without the state polling i would be very happy. I'll try to explore Finite State Machine, which i am aleredy using for the user input / Ai function and see if i can use it too for the animations.

 

I think mikeman's answer i what i'll be looking into first, thought, as it is very close to what i'm currently trying to do.

 

Of course i will take time to really think this through. Heck, maybe putting the ManageAnimation code inside my Iactor class is innapropriate, as the actor truly care only to keep track of it's current animation and frame, as DareDeveloper aleredy said is true.


Edited by Sunsharior, 24 March 2014 - 10:09 AM.


#8 superman3275   Crossbones+   -  Reputation: 2061

Like
1Likes
Like

Posted 24 March 2014 - 11:10 AM

Your animation management appears to be a finite state machine, which doesn't need to be hardcoded with constants and ifs and enums.

Instead, you should define a data structure to represent animation definitions, loaded from external files and processed by a generic animation manager. Your current ManageAnimation method is in class IActor, but it isn't generic: it assumes a number of states and events exist.

This is spot on. Read this for a great introductory tutorial to creating FSM's.

 

Here's another one which uses Enumerations, but is still very general. It might be easier for you to use this one, because it somewhat follows your current train of thought.


I'm a game programmer and computer science ninja ph34r.png!

Here's my 2D RPG-Ish Platformer Programmed in Python + Pygame, with a Custom Level Editor and Rendering System!

 

Here's my Custom IDE / Debugger Programmed in Pure Python and Designed from the Ground Up for Programming Education!

Want to ask about Python, Flask, wxPython, Pygame, C++, HTML5, CSS3, Javascript, jQuery, C++, Vimscript, SFML 1.6 / 2.0, or anything else? Recruiting for a game development team and need a passionate programmer? Just want to talk about programming? Email me here:

hobohm.business@gmail.com

or Personal-Message me on here smile.png!





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS