Location: Assets/Plugins/Animancer/Examples/07 Layers/02 Dynamic Layers
Recommended After: Basic Layers
Learning Outcomes: in this example you will learn:
How to smoothly change which body parts an animation controls.
How to play one animation on two different layers at the same time.
This example expands upon the Basic Layers example to have the Action animation control the whole body if the character would be Idle but only control the upper body while they Walk.
Layers are a Pro-Only Feature. You can try them out in the Unity Editor with Animancer Lite, but they're not available in runtime builds unless you purchase Animancer Pro.
Summary
- You can play an animation on multiple layers by creating multiple states for it.
- Using the first state as the Key of the second makes it easy to look up that state when it's needed.
- That's also what
FadeMode.FromStart
does when it needs to create a new state to fade in while the old one fades out.
Overview
The following videos compares this example to Basic Layers:
Basic Layers | Dynamic Layers |
---|---|
Shooting while Idle puts the character's legs in the Idle pose. | Shooting while Idle puts the character's legs in the Shooting pose, which looks much better. |
Shooting while Walking lets the character's legs Walk. | Shooting while Walking lets the character's legs Walk. |
As the Inspector shows in the above videos, having the Action animation sometimes control the full body and sometimes only the upper body is achieved by creating a second AnimancerState
to play the same AnimationClip
. The general code structure has a few notable differences from the Basic Layers example. We want the same control logic (W to Walk and Left Click to perform the Action) but need some more complex logic for the way we play the different animations so it makes sense to split those concerns into two scripts:
DynamicLayeredCharacterAnimations
is similar toLayeredCharacterAnimations
, but instead of referencing anAnimancerComponent
it references aLayeredAnimationManager
.LayeredAnimationManager
takes the commands it's given and decides how to actually interact with theAnimancerComponent
. It contains the main new logic of this example.
Controls
DynamicLayeredCharacterAnimations
references a LayeredAnimationManager
instead of directly controlling an AnimancerComponent
:
using Animancer;
using UnityEngine;
public sealed class DynamicLayeredCharacterAnimations : MonoBehaviour
{
[SerializeField] private LayeredAnimationManager _AnimationManager;
[SerializeField] private ClipTransition _Idle;
[SerializeField] private ClipTransition _Move;
[SerializeField] private ClipTransition _Action;
It has no _ActionMask
or _ActionFadeOutDuration
(because they're in the LayeredAnimationManager
instead).
The rest of the script is all the same, except that it's controlling the LayeredAnimationManager
instead of the AnimancerComponent
:
private void Awake()
{
_Action.Events.OnEnd = _AnimationManager.FadeOutUpperBody;
}
private void Update()
{
UpdateMovement();
UpdateAction();
}
private void UpdateAction()
{
if (ExampleInput.LeftMouseUp)
{
_AnimationManager.PlayAction(_Action);
}
}
private void UpdateMovement()
{
float forward = ExampleInput.WASD.y;
if (forward > 0)
{
_AnimationManager.PlayBase(_Move, false);
}
else
{
_AnimationManager.PlayBase(_Idle, true);
}
}
}
Layer Setup
As with LayeredCharacterAnimations
, the first thing LayeredAnimationManager
does is initialize its layers:
[SerializeField] private AnimancerComponent _Animancer;
[SerializeField] private AvatarMask _ActionMask;
private AnimancerLayer _BaseLayer;
private AnimancerLayer _ActionLayer;
private void Awake()
{
_BaseLayer = _Animancer.Layers[0];
_ActionLayer = _Animancer.Layers[1];
_ActionLayer.SetMask(_ActionMask);
}
Play Base
The _ActionFadeDuration
field will be explained in the Play Action Full Body section:
[SerializeField, Seconds] private float _ActionFadeDuration = AnimancerPlayable.DefaultFadeDuration;
It has a bool
field to remember whether the current animation on the base layer should allow actions to control the whole body:
private bool _CanPlayActionFullBody;
public void PlayBase(ITransition transition, bool canPlayActionFullBody)
{
_CanPlayActionFullBody = canPlayActionFullBody;
That parameter is set as shown in the Controls section:
- Idle sets it to
true
. - Move sets it to
false
.
If the value was just set to true
and the _ActionLayer
isn't fading out or is already inactive, it ignores the transition
it was just told to play and plays the current Action on the whole body:
if (_CanPlayActionFullBody && _ActionLayer.TargetWeight > 0)
{
PlayActionFullBody(_ActionFadeDuration);
}
Otherwise, it plays the given transition
:
else
{
_BaseLayer.Play(transition);
}
}
For example:
- The character is playing Walk on the lower body and Action on the upper body.
- The player releases the movement key so
_AnimationManager.PlayBase(_Idle, true);
is called. - Instead of swapping to Idle and Action like in the Basic Layers example, it would start playing the Action on the whole body.
Play Action
Telling an Action to play will always play it on the _ActionLayer
:
public void PlayAction(ITransition transition)
{
_ActionLayer.Play(transition);
And if the current animation on the base layer allows it, it then tells the Action to play on the whole body:
if (_CanPlayActionFullBody)
PlayActionFullBody(transition.FadeDuration);
}
Play Action Full Body
Playing the Action on the whole body could be done by playing it only on the _BaseLayer
and fading out the _ActionLayer
, but changing an animation to a different layer can't be smoothly blended so that would make it much harder to respond appropriately if the character stops or starts Walking during the Action.
Instead, the desired result can be achieved much more easily by playing the Action either on the _ActionLayer
or on both layers at the same time:
- Get the current Action's
AnimancerState
from the_ActionLayer
:
private void PlayActionFullBody(float fadeDuration)
{
var upperBodyState = _ActionLayer.CurrentState;
- Call
GetOrCreateState
to get a state on the_BaseLayer
:
- The first parameter is the Key used to find an existing state if there is one. Rather than using the Action's
AnimationClip
which is already used as the Key for theupperBodyState
, it uses theupperBodyState
itself as the Key. - The second parameter is the
AnimationClip
for the state to use.
var fullBodyClone = _BaseLayer.GetOrCreateState(upperBodyState, upperBodyState.Clip);
That gives the following behaviour:
- The first time this method is called, there won't be any state registered with the
upperBodyState
as its Key soGetOrCreateState
will create a new state with that Key using the sameAnimationClip
. - Every time it gets called after that, it will find the same state that was created the first time.
- This example only has one Action animation, but if this method were called with a different one, it would be a completely different state and therefore create a copy of itself on its first time as well.
- Once it has that cloned state, it simply plays it on the
_BaseLayer
:
_BaseLayer.Play(fullBodyClone, fadeDuration);
This method is given a different fadeDuration
depending on where it's called from:
- Inside
PlayAction
where a new Action is played, it uses the sametransition.FadeDuration
so the states on both layers will fade in at the same time. - Inside
PlayBase
where the Action is already playing, there would be no point in using the same fade duration since it wasn't started at the same time so instead it uses the_ActionFadeDuration
field which is also used byFadeOutUpperBody
.
- It also sets the clone to the same
NormalizedTime
as the original so they're both playing the same parts of the animation:
fullBodyClone.NormalizedTime = upperBodyState.NormalizedTime;
}
Fade Out Upper Body
DynamicLayeredCharacterAnimations
also sets its _Action
to fade out the upper body in its End Event:
class DynamicLayeredCharacterAnimations
{
private void Awake()
{
_Action.Events.OnEnd = _AnimationManager.FadeOutUpperBody;
}
}
class LayeredAnimationManager
{
public void FadeOutUpperBody()
{
_ActionLayer.StartFade(0, _ActionFadeDuration);
}
}
Conclusion
Now we have a character who plays the Action animation normally if they were Idle or plays it on the upper body only if they were Walking: