views:

432

answers:

7

I have a question not necessarily specific to any platform or API but more specific to interactions in code between animations.

A game is a good example. Let's say the player dies, and there's a death animation that must finish before the object is removed. This is typical for many cases where some animation has to finish before continuing with what ever action that would normally follow. How would you go about doing this?

My question is about the control and logic of animation. How would you design a system which is capable of driving the animation but at the same time implement custom behavior?

The problem that typically arise, is that the game logic and animation data become codependent. That is, the animation has to callback into code or somehow contain meta data for duration of animation sequences. What's typically even more so a problem, is when an animation, which has to trigger some other code, say after 1.13s spawn a custom sprite, this tend to result in deep nesting of code and animation. A bomb with a timer would be en example of both logic and animation where both things interact, but I want to keep them as separate as possible.

But what would you do to keep animation and code two separate things?

Recently I've been trying out mgrammar, and I'm thinking, a DSL might be the way to go. That would allow the animation or animator, to express certain things in a presumably safe manner which would then go into the content pipeline...

A: 

I guess I don't really see the problem.

If you had some nest of callback's I could see why thing might be hard to follow, but if you only have one call back for all animation events and one update function which starts animations, it's pretty easy to follow the code.

So what do you gain by separating them?

BigSandwich
To me it feels wrong that the animation intertwines with code. I want to keep these things to completely seperate things making animation data resuable and modular. It might sound strange but once you get into the coding part it's really apparent that it's a problem.
John Leidegren
I write this kind of code quite often. You seem to be asking how you can decouple a data driven system from the data that drives it. I don't think you can. I think your problem is the nesting, but you can refactor your code to remove that. If you post code with your problem, I'll refactor it.
BigSandwich
There's no code to post, but I'm defentitly trying to seperate the data from a data driven system. As you say, it might not be possible but I'm optimisitic, I wan't to make it two things.
John Leidegren
A: 

I think you should separate the rendering from the game logic.

You have at least two different kind of objects:

  • An entity that holds the unit's data (hit points, position, speed, strength, etc.) and logic (how it should move, what happens if it runs out of hit points, ...).
  • Its representation, that is the sprite, colors, particles attached to it, sounds, whatever. The representation can access the entity data, so it knows the position, hit points, etc.
  • Maybe a controller if the entity can be directly controlled by a human or an AI (like a car in a car simulation).

Yes that sounds like the Model-View-Controller architecture. There are lots of resources about this, see this article from deWiTTERS or The Guerrilla Guide to Game Code by Jorrit Rouwé, for game-specific examples.

About your animation problems: for a dying unit, when the Model updates its entity and figures out it has no more hit points, it could set a flag to say it's dead and remove the entity from the game (and from memory). Then later, when the View updates, it reads the flag and starts the dying animation. But it can be difficult to decide where to store this flag since the entity object should disappear.

There's a better way in my humble IMHO. When your entity dies, you could send an event to all listeners that are registered to the UnitDiedEvent that belongs to this specific entity, then remove the entity from the game. The entity representation object is listening to that event, and its handler starts the dying animation. When the animation is over, the entity representation can finally be removed.

The observer design pattern can be useful, here.

Splo
That solves the death problem but not the generic. For ex. Play sword slash anim, when the anim reaches frame 25 turn on sword damage box. You want the animation to continue. A callback is really the only good way to do this.
BigSandwich
Also, who says you want to get rid of the guy when he dies? Maybe I want a bunch of ragdolls to kick around. Maybe he turns into a zombie and gets back up. Your making a lot of assumptions.
BigSandwich
I guess adding more events and event handlers would do the trick. Event handlers also can alter or create new objects (e.g. a new animation at frame 25).
Splo
Well the problem with event handling is that it isn't really inversion of control, I think that is what I'm getting at, at least one thing to all this. How will the event go from HasDied to Dispate if the logic never knows the length of animation?
John Leidegren
A: 

For a couple games I made to solve this problem I created two animation classes

asyncAnimation - For fire and forget type animations

syncAnimation - If I wanted to wait for the animation to resolve before returning control

As games usually have a main loop it looked something like this C# style psuedo code

while(isRunning) 
{

   renderStaticItems();
   renderAsyncAnimations();

   if (list_SyncAnimations.Count() > 0)
   {
       syncAnimation = list_SyncAnimations.First();
       syncAnimation.render();

       if (syncAnimation.hasFinished()) 
       {
           list_SyncAnimations.removeAt(0); 
           // May want some logic here saying if
           // the sync animation was 'player dying' update some variable
           // so that we know the animation has ended and the
           // game can prompt for the 'try again' screen
       }

   }
   else
   {
       renderInput();
       handleOtherLogic(); // Like is player dead, add sync animation if required.
   }
}

So what the code does is maintain a list of sync animations that need to be resolved before continuing with the game - If you need to wait for several animations just stack them up.

Also, it might be a good idea to look into the command pattern or provide a callback for when the sync animation has finished to handle your logic - its really up how you want to do it.

As for your "Spawn at sec 1.13" perhaps the SyncAnimation class should have an overridable .OnUpdate() method, which can do some custom logic (or call a script)

It depends what your requirements may be.

JSmyth
So instead of a callback when you get to frame 25, you have to poll the animation with an update function? How is that better? Also, where will you store the 1.13 seconds? If it's in the animation, all you've done is move things around.
BigSandwich
Games have a polling function anyway - and yeah, it is moving things around. The logic has to go somewhere, put it where it makes most sense to you.
JSmyth
+3  A: 

The solution depends on the gameplay you're going for. If the gameplay is 100% code-driven, the anim is driven by the entity's state(state-driven animation). If it's graphics/animation-driven, the anim length determines how long the entity's in that state(anim-driven state).

The latter is typically more flexible in a commercial production environment as the designer can just say "we need that death anim to be shorter" and it gets negotiated. But when you have very precise rules in mind or a simulated system like physics, state-driven animation may be preferable. There is no 100% orthogonal and clean solution for the general case.

One thing that helps to keep it from getting too messy is to consider game AI patterns:

Game AI is typically implemented as some form of finite state machine, possibly multiple state machines or layered in some way(the most common division being a high level scripting format with low level actions/transitions).

At the low level you can say things like "in the hitreact state, playback my hitreact anim until it finishes, then find out from the high-level logic what state to continue from." At the high level there are a multitude of ways to go about defining the logic, but simple repeating loops like "approach/attack/retreat" are a good way to start.

This helps to keep the two kinds of behaviors - the planned activities, and the reactions to new events - from being too intermingled. Again, it doesn't always work out that way in practice, for the same reasons that sometimes you want code to drive data or the other way around. But that's AI for you. No general solutions here!

A: 

The animation can call a callback function that you supply, or send a generic event back to the code. It doesn't need anything more than that, which keeps all logic in the code. Just inject the callback or connect the event when the animation is created.

Kylotan
+1  A: 

Your enemy needs to have multiple states. Alive and dead are not enough. Alive, dying and dead might be. Your enemy processing loop should check its state and perform different operations:

if(state == alive and hp == 0)
{
   state = dying
   currentanimation = animation(enemy_death)
   currentanimation.play()
}
elseif(state == dying and currentanimation.isPlaying == true)
{
    // do nothing until the animation is finished.
    // more efficiently implemented as a callback.
}
elseif(state == dying and currentanimation.isPlaying == false)
{
    state = dead
    // at this point either the game engine cleans up the object, or the object deletes itself.
}
Martin
A: 

I try as much as possible to keep callbacks out of child animations. Animations should indicate that they are complete, the actions taken on an animations completion should be called from the controller level of the application.

In Actionscript this is the beauty of event dispatching/listening - The controller object can create the aimation and then assign a handler for an event which the animation dispatches when it is complete.

I've used the pattern for several things in Flash projects and it helps keep code independent far better than callbacks.

Especially if you write custom event objects which extend Event to carry the kind of information you need. such as teh MouseEvent that carries localX, localY, and stageX and stageY. I use a custom I've named NumberEvent to broadcast any kind of numerical information around my applications.

in actionscript controler object:

var animObj:AwsomeAnim = AwsomeAnim();
animObj.start();
animObj.addEventListener(AwsomeAnim.COPLETE,_onAnimFinish);

function _onAnimFinish():void
{
    // actions to take when animation is complete here
}

In javascript where custom events do not exist. I just have a boolean variable in the animation object, and check it on a timer from the controller.

in javascript controller object:

var animObj = new animObj();// among other things must set this.isComplete = false
animObj.start();

function checkAnimComplete()
{
    if(animObj.isComplete == true)
    {
        animCompleteActions();

    }else{
        setTimeout(checkAnimComplete,300);
    }
}
checkAnimComplete();


function animCompleteActions()
{
    // anim complete actions chere
}
Fire Crow