How to avoid Spagettie AnimGraph?

I got this question from twitter. The state machine is cool but how do we avoid this?



UE4 animation system is very good to use when you have small sets of animations but it can be cumbersome if you have many many many variation of the animations. If you want to see all the animations you want to look, this is how it can look.

As much as state machine and the transition look cool to visualize, it doesn't help if they are too many of them because at this point, you won't be able to see what's going on.

So what do you do?

First thing I mention when I see this is our ability system.

Disclaimer - Note that I'm not a gameplay engineer at Epic, so my knowledge on the current ability system is very outdated. (I did work on gameplay during Gears of War times) But to relay this idea of what to do to avoid the spaghetti states it is required to mention the ability system. Second, I'm not on Fortnite, so I don't know how their real implementation is. This is just example of how it can be, so do not take this as "this is how we did implement".

So what is ability system? That may be too broad concept, but it can be a thought as a single state that happens in-game. It doesn't provide nice graph with transition, but it is a meaningful state of the game. So say for Paragon, we have many abilities, but one of them can be as simple as each action in the hero's ability. For Fortnite, it can be DBNO (down but not out) state. Let's consider DBNO as an example.

You can add DBNO to AnimBP if you want. If you do though this is what can looks like,

1. From game, player damage goes below threshold, now that triggers variable "bDBNO" in AnimBP to turn true.
2. In AnimBP, that variable change causes the "DBNO" state
3. When the character is revived, game turns off  AnimBP.bDBNO
4. Now the character is out of DBNO state from AnimBP

This sounds easy enough, but say now you want to do extra *stuff* during DBNO state for game, for example, the damage application is only 20% than usual. Now what to do?

So now you could also create your bDBNO variable in Character BP, and use that to do more stuff in Character BP. And AnimBP just pulls that data from Character, so it will always just sync.

1. From game, player damage goes below threshold, now that triggers variable "bDBNO" in Character BP to turn true.
2. In AnimBP's tick, it will copy bDBNO = MyCharacter.bDBNO, causing DBNO state gets activated
3. When the character is revived, game turns off  'bDBNO" in Charcter BP
4. In the AnimBP's tick, it will copy bDBNO = MyCharacter.bDBNO, causing it to get out of DBNO state

How does it look? Maybe a bit better. 

How do you change your damage application?

You might do along with this if statement

if (bDBNO)
{
   Damage *= 0.2;
}

What about now you have many many of these conditions? If it's code it will get many if statements. If it's in BP, it will get many of branch nodes. 

And now whenever you need, you have to have this MyCharacter.bDBNO everywhere. 

So this is where gameplay ability comes in. What it does is to create a meaning state in game, say now we want DBNO ability. Now things would look different. 

1. From game, player damage goes below threshold, now activate DBNO ability on the character
2. When revived, deactivate DBNO ability.

So now how to handle all damages?

Character can have ability component that manage these abilities activation, and deactivation, and flow.

For example instead of

if (bDBNO)
{
   Damage *= 0.2;
}

it can do this, 

Damage *= ActiveAbility.DamageApplicationRate;

And inside of DBNO ability, you can set DamageApplicationRate to be 0.2

This looks better, doesn't it?

Now ability system can contain many of these options to apply movement speed rate or how to play animation to it. 

If we want to not change anything in AnimBP, when DBNO ability gets activated, you could set AnimBP.bDBNO = true and when it gets deactivated, you could set AnimBP.bDBNO = false

But now question is that "does AnimBP have to know about it?" 

If you need a lot more controls of animation transitions/blending, that's when you want to put that in states - i.e. locomotion. If you do this from ability system it can get very complicated. Also locomotion is not something you only limit to a ability. Locomotion is used by many different abilities. You could put DBNO to state if you want to support all different directions, in that case it is about blending different animations. 

But if you just have one in/out of animation, I don't think you want to create state for all of them. 
Instead, you could add that to your ability system. Your ability system can have specific in/out condition that can be controlled, and each ability system can push animation via AnimMontage. You get notification when montage starts blending out or gets deactivated. 

Other good example can be hit reaction. If you want hit reaction to happen in many different abilities, and want directional, it is totally fine to put it in states because it serves many different gameplay abilities. 

To sum up, there is no single bullet that fixes this problem but you have to think about how you'd like to organize your game states. The fact AnimBP provides states doesn't mean you have to put all states in that graph. It is up to you how you design your system. And gameplay abilities can be the example of that. 

There are many ways of doing one thing, but choosing wisely how to solve one thing with a bit of mindset of future reading can save many things in your development time. 

Comments

Popular posts from this blog

Master pose vs copy pose vs mesh merge in UE4

What do I do when this happens?