Blend trees are good for hiding complexity. A blend tree doesn’t have state; it doesn’t call back out into code. It simply blends between the different clips based on the parameters you define.
The significance of this is that you can iterate on blend trees without worrying about breaking the rest of your game. You can hide a complex web of states, and prevent bugs down the road, because you can’t tie behaviour to most of the animations in a blend tree.
It looks complex, but this locomotion blend tree actually helped to keep things straight-forward.
It helps to think of layers as a rough analogy to a class in a script. You want everything in a layer to serve both the same logical and behavioural function. This is because every layer controls what other layers it overrides, whether or not it affects certain bones, whether that layer is additive, and so on.
All of the states for Henry’s left hand. Each layer has a purpose and a clear hierarchy. It makes it easy to control and easy to find state
Reusable, logical patterns in state and sub-state machines speed up development, allow multiple people to make similar content, make debugging easier and can in fact reduce bugs overall.
A few patterns that are good for structuring your layers:
- The hub and spoke: this pattern makes it easy to debug, because you can see clearly the transitions go out and come back into the empty state. Every spoke of the hub should reset any state it touches. Spokes are good candidates for making into sub-state machines using the patterns below.
A hub and spoke pattern used to animate Henry’s right arm.
- The shared entering/shared exiting pattern: by grouping states into a pattern of ‘intro’ - ‘execution / loop’ - ‘outro’ you can cleanly attach any animation events or state machine behaviors to the ‘intro’ and ‘outro’ states. Much like a blend tree, you can then iterate and change the inner ‘execution / loop’ states without worrying about breaking your game.
Entry to Exit: All of the states for Henry’s right hand to hold a book; more substates hiding details underneath.
- The critical-section and settle pattern: for interruptible animations, especially player input driven animations, break your clip into two parts. First, a critical-section that contains all state changes, effects, damage, etc., that must always play to completion. Second, a settle animation that looks good getting you back to idle and that can be interrupted by new input.
Once your animations are working and looking like you want them to, you need to feed back the state of your animators into the state of your game. Here are a couple of good points to keep in mind when doing that.
State machine behaviors are bits of code that you can attach to any animation state. You use them to tie behavior directly to the state of the animator itself.
Avoid writing complex gameplay code inside of them because it can get difficult to track down where your changes in the state are coming from. If you are using state machine behavior to drive gameplay code, use a messaging system; talk to a manager class, or trigger your code off of parameters at a higher level.
Finally, my favorite state machine behavior: Debug.Break();. It’s the most useful state machine behavior; you can attach anywhere in your animation setup, and you’ll have a breakpoint, similar to that of a visual scripting system.
Don’t be afraid to use C# code where appropriate. Rather than have hundreds or thousands of transitions, or hooking up AnyState transitions all over the place, use Animator.Play or Animator.CrossFade to dynamically cause direct transitions from code. If that still isn’t enough control, you can look into our PlayablesAPI for even more ways to animate using code. Examples can be found here.
Animation events tie a specific moment of your animation clip to a specific change of state in your game; they can be used to set off things like visual and sound effects. However, if you transition out of a clip before it has fired, then it will never fire. A way to solve this is to add a state machine behavior that ensures the event always fires when a specific point in time is reached, no matter what else happens, or doesn’t happen, in the game.