Files
2025-09-16 18:07:13 +07:00

123 lines
5.5 KiB
C#

// Animancer // https://kybernetik.com.au/animancer // Copyright 2021 Kybernetik //
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
using Animancer.Units;
using UnityEngine;
namespace Animancer.Examples.Layers
{
/// <summary>Demonstrates how to use layers to play multiple animations at once on different body parts.</summary>
/// <example><see href="https://kybernetik.com.au/animancer/docs/examples/layers">Layers</see></example>
/// https://kybernetik.com.au/animancer/api/Animancer.Examples.Layers/LayerExample
///
[AddComponentMenu(Strings.ExamplesMenuPrefix + "Layers - Layer Example")]
[HelpURL(Strings.DocsURLs.ExampleAPIDocumentation + nameof(Layers) + "/" + nameof(LayerExample))]
public sealed class LayerExample : MonoBehaviour
{
/************************************************************************************************************************/
[SerializeField] private AnimancerComponent _BasicAnimancer;
[SerializeField] private AnimancerComponent _LayeredAnimancer;
// Not using Transitions because the End Event is completely different between the two characters.
[SerializeField] private AnimationClip _Idle;
[SerializeField] private AnimationClip _Run;
[SerializeField] private AnimationClip _Action;
[SerializeField, Seconds] private float _FadeDuration = AnimancerPlayable.DefaultFadeDuration;
[SerializeField] private AvatarMask _ActionMask;
/************************************************************************************************************************/
private const int BaseLayer = 0;
private const int ActionLayer = 1;
/************************************************************************************************************************/
private void OnEnable()
{
// Idle on default layer 0.
_BasicAnimancer.Play(_Idle);
_LayeredAnimancer.Play(_Idle);
// Set the mask for layer 1 (this automatically creates the layer).
_LayeredAnimancer.Layers[ActionLayer].SetMask(_ActionMask);
// Since we set a mask it will use the name of the mask in the Inspector by default. But we can also
// replace it with a custom name. Either way, layer names are only used in the Inspector and any calls to
// this method will be compiled out of runtime builds.
_LayeredAnimancer.Layers[ActionLayer].SetDebugName("Action Layer");
}
/************************************************************************************************************************/
private bool _IsRunning;
public void ToggleRunning()
{
// Swap between true and false.
_IsRunning = !_IsRunning;
// Determine which animation to play.
var animation = _IsRunning ? _Run : _Idle;
// Play it on the basic character.
_BasicAnimancer.Play(animation, _FadeDuration);
// If the Action is currently playing on the layered character, move it to the appropriate layer.
var previousLayer = _IsRunning ? BaseLayer : ActionLayer;
var state = _LayeredAnimancer.Layers[previousLayer].CurrentState;
if (state != null &&
state.Weight > 0 &&
state.Clip == _Action)
{
// Playing the animation on a different layer will need to create a copy of its state to let the
// original fade out properly on the previous layer. So we need to give the new state the correct time.
var time = state.Time;
state = PerformActionLayered();
state.Time = time;
// If the Action is now on the base layer, let it continue instead of playing the Idle or Run.
if (state.LayerIndex == BaseLayer)
return;
}
// Otherwise just play the Idle or Run animation normally.
_LayeredAnimancer.Play(animation, _FadeDuration);
}
/************************************************************************************************************************/
public void PerformAction()
{
// Basic.
var state = _BasicAnimancer.Play(_Action, _FadeDuration);
state.Events.OnEnd = () => _BasicAnimancer.Play(_IsRunning ? _Run : _Idle, _FadeDuration);
// Layered.
PerformActionLayered();
}
public AnimancerState PerformActionLayered()
{
// When running, perform the action on the ActionLayer (1) then fade that layer back out.
if (_IsRunning)
{
var state = _LayeredAnimancer.Layers[ActionLayer].Play(_Action, _FadeDuration, FadeMode.FromStart);
state.Events.OnEnd = () => state.Layer.StartFade(0, _FadeDuration);
return state;
}
else// Otherwise perform the action on the BaseLayer (0) then return to idle.
{
var state = _LayeredAnimancer.Layers[BaseLayer].Play(_Action, _FadeDuration, FadeMode.FromStart);
state.Events.OnEnd = () => _LayeredAnimancer.Play(_Idle, _FadeDuration);
return state;
}
}
/************************************************************************************************************************/
}
}