You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

412 lines
22 KiB

// Animancer // https://kybernetik.com.au/animancer // Copyright 2021 Kybernetik //
using System.Collections.Generic;
using UnityEngine;
namespace Animancer
{
/// <summary>[Pro-Only]
/// A <see cref="NamedAnimancerComponent"/> which plays a main <see cref="RuntimeAnimatorController"/> with the
/// ability to play other individual <see cref="AnimationClip"/>s separately.
/// </summary>
/// <remarks>
/// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/animator-controllers#hybrid">Hybrid</see>
/// </remarks>
/// https://kybernetik.com.au/animancer/api/Animancer/HybridAnimancerComponent
///
[AddComponentMenu(Strings.MenuPrefix + "Hybrid Animancer Component")]
[HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(HybridAnimancerComponent))]
public class HybridAnimancerComponent : NamedAnimancerComponent
{
/************************************************************************************************************************/
#region Fields and Properties
/************************************************************************************************************************/
[SerializeField, Tooltip("The main Animator Controller that this object will play")]
private ControllerTransition _Controller;
/// <summary>[<see cref="SerializeField"/>]
/// The transition containing the main <see cref="RuntimeAnimatorController"/> that this object plays.
/// </summary>
public ref ControllerTransition Controller => ref _Controller;
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Initialisation
/************************************************************************************************************************/
#if UNITY_EDITOR
/// <summary>[Editor-Only]
/// Sets <see cref="PlayAutomatically"/> = false by default so that <see cref="OnEnable"/> will play the
/// <see cref="Controller"/> instead of the first animation in the
/// <see cref="NamedAnimancerComponent.Animations"/> array.
/// </summary>
/// <remarks>
/// Called by the Unity Editor when this component is first added (in Edit Mode) and whenever the Reset command
/// is executed from its context menu.
/// </remarks>
protected override void Reset()
{
base.Reset();
if (Animator != null)
{
Controller = Animator.runtimeAnimatorController;
Animator.runtimeAnimatorController = null;
}
PlayAutomatically = false;
}
#endif
/************************************************************************************************************************/
/// <summary>
/// Plays the <see cref="Controller"/> if <see cref="PlayAutomatically"/> is false (otherwise it plays the
/// first animation in the <see cref="NamedAnimancerComponent.Animations"/> array).
/// </summary>
/// <remarks>Called by Unity when this component becomes enabled and active.</remarks>
protected override void OnEnable()
{
PlayController();
base.OnEnable();
#if UNITY_ASSERTIONS
if (Animator != null && Animator.runtimeAnimatorController != null)
OptionalWarning.NativeControllerHybrid.Log($"An Animator Controller is assigned to the" +
$" {nameof(Animator)} component while also using a {nameof(HybridAnimancerComponent)}." +
$" Most likely only one of them is being used so the other should be removed." +
$" See the documentation for more information: {Strings.DocsURLs.AnimatorControllers}", this);
#endif
}
/************************************************************************************************************************/
public override void GatherAnimationClips(ICollection<AnimationClip> clips)
{
base.GatherAnimationClips(clips);
clips.GatherFromSource(_Controller);
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Animator Controller Wrappers
/************************************************************************************************************************/
/// <summary>
/// Transitions to the <see cref="Controller"/> over its specified
/// <see cref="AnimancerTransition{TState}.FadeDuration"/>.
/// <para></para>
/// Returns the <see cref="AnimancerTransition{TState}.State"/>.
/// </summary>
public ControllerState PlayController()
{
if (!_Controller.IsValid())
return null;
// Don't just return the result of Transition because it is an AnimancerState which we would need to cast.
Play(_Controller);
return _Controller.State;
}
/************************************************************************************************************************/
#region Cross Fade
/************************************************************************************************************************/
/// <summary>Starts a transition from the current state to the specified state using normalized times.</summary>
/// <remarks>If `fadeDuration` is negative, it uses the <see cref="AnimancerPlayable.DefaultFadeDuration"/>.</remarks>
public void CrossFade(
int stateNameHash,
float fadeDuration = ControllerState.DefaultFadeDuration,
int layer = -1,
float normalizedTime = float.NegativeInfinity)
{
fadeDuration = ControllerState.GetFadeDuration(fadeDuration);
var controllerState = PlayController();
controllerState.Playable.CrossFade(stateNameHash, fadeDuration, layer, normalizedTime);
}
/************************************************************************************************************************/
/// <summary>Starts a transition from the current state to the specified state using normalized times.</summary>
/// <remarks>If `fadeDuration` is negative, it uses the <see cref="AnimancerPlayable.DefaultFadeDuration"/>.</remarks>
public AnimancerState CrossFade(
string stateName,
float fadeDuration = ControllerState.DefaultFadeDuration,
int layer = -1,
float normalizedTime = float.NegativeInfinity)
{
fadeDuration = ControllerState.GetFadeDuration(fadeDuration);
if (States.TryGet(name, out var state))
{
Play(state, fadeDuration);
if (layer >= 0)
state.LayerIndex = layer;
if (normalizedTime != float.NegativeInfinity)
state.NormalizedTime = normalizedTime;
return state;
}
else
{
var controllerState = PlayController();
controllerState.Playable.CrossFade(stateName, fadeDuration, layer, normalizedTime);
return controllerState;
}
}
/************************************************************************************************************************/
/// <summary>Starts a transition from the current state to the specified state using times in seconds.</summary>
/// <remarks>If `fadeDuration` is negative, it uses the <see cref="AnimancerPlayable.DefaultFadeDuration"/>.</remarks>
public void CrossFadeInFixedTime(
int stateNameHash,
float fadeDuration = ControllerState.DefaultFadeDuration,
int layer = -1,
float fixedTime = 0)
{
fadeDuration = ControllerState.GetFadeDuration(fadeDuration);
var controllerState = PlayController();
controllerState.Playable.CrossFadeInFixedTime(stateNameHash, fadeDuration, layer, fixedTime);
}
/************************************************************************************************************************/
/// <summary>Starts a transition from the current state to the specified state using times in seconds.</summary>
/// <remarks>If `fadeDuration` is negative, it uses the <see cref="AnimancerPlayable.DefaultFadeDuration"/>.</remarks>
public AnimancerState CrossFadeInFixedTime(
string stateName,
float fadeDuration = ControllerState.DefaultFadeDuration,
int layer = -1,
float fixedTime = 0)
{
fadeDuration = ControllerState.GetFadeDuration(fadeDuration);
if (States.TryGet(name, out var state))
{
Play(state, fadeDuration);
if (layer >= 0)
state.LayerIndex = layer;
state.Time = fixedTime;
return state;
}
else
{
var controllerState = PlayController();
controllerState.Playable.CrossFadeInFixedTime(stateName, fadeDuration, layer, fixedTime);
return controllerState;
}
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Play
/************************************************************************************************************************/
/// <summary>Plays the specified state immediately, starting from a particular normalized time.</summary>
public void Play(
int stateNameHash,
int layer = -1,
float normalizedTime = float.NegativeInfinity)
{
var controllerState = PlayController();
controllerState.Playable.Play(stateNameHash, layer, normalizedTime);
}
/************************************************************************************************************************/
/// <summary>Plays the specified state immediately, starting from a particular normalized time.</summary>
public AnimancerState Play(
string stateName,
int layer = -1,
float normalizedTime = float.NegativeInfinity)
{
if (States.TryGet(name, out var state))
{
Play(state);
if (layer >= 0)
state.LayerIndex = layer;
if (normalizedTime != float.NegativeInfinity)
state.NormalizedTime = normalizedTime;
return state;
}
else
{
var controllerState = PlayController();
controllerState.Playable.Play(stateName, layer, normalizedTime);
return controllerState;
}
}
/************************************************************************************************************************/
/// <summary>Plays the specified state immediately, starting from a particular time (in seconds).</summary>
public void PlayInFixedTime(
int stateNameHash,
int layer = -1,
float fixedTime = 0)
{
var controllerState = PlayController();
controllerState.Playable.PlayInFixedTime(stateNameHash, layer, fixedTime);
}
/************************************************************************************************************************/
/// <summary>Plays the specified state immediately, starting from a particular time (in seconds).</summary>
public AnimancerState PlayInFixedTime(
string stateName,
int layer = -1,
float fixedTime = 0)
{
if (States.TryGet(name, out var state))
{
Play(state);
if (layer >= 0)
state.LayerIndex = layer;
state.Time = fixedTime;
return state;
}
else
{
var controllerState = PlayController();
controllerState.Playable.PlayInFixedTime(stateName, layer, fixedTime);
return controllerState;
}
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Parameters
/************************************************************************************************************************/
/// <summary>Gets the value of the specified boolean parameter.</summary>
public bool GetBool(int id) => _Controller.State.Playable.GetBool(id);
/// <summary>Gets the value of the specified boolean parameter.</summary>
public bool GetBool(string name) => _Controller.State.Playable.GetBool(name);
/// <summary>Sets the value of the specified boolean parameter.</summary>
public void SetBool(int id, bool value) => _Controller.State.Playable.SetBool(id, value);
/// <summary>Sets the value of the specified boolean parameter.</summary>
public void SetBool(string name, bool value) => _Controller.State.Playable.SetBool(name, value);
/// <summary>Gets the value of the specified float parameter.</summary>
public float GetFloat(int id) => _Controller.State.Playable.GetFloat(id);
/// <summary>Gets the value of the specified float parameter.</summary>
public float GetFloat(string name) => _Controller.State.Playable.GetFloat(name);
/// <summary>Sets the value of the specified float parameter.</summary>
public void SetFloat(int id, float value) => _Controller.State.Playable.SetFloat(id, value);
/// <summary>Sets the value of the specified float parameter.</summary>
public void SetFloat(string name, float value) => _Controller.State.Playable.SetFloat(name, value);
/// <summary>Gets the value of the specified integer parameter.</summary>
public int GetInteger(int id) => _Controller.State.Playable.GetInteger(id);
/// <summary>Gets the value of the specified integer parameter.</summary>
public int GetInteger(string name) => _Controller.State.Playable.GetInteger(name);
/// <summary>Sets the value of the specified integer parameter.</summary>
public void SetInteger(int id, int value) => _Controller.State.Playable.SetInteger(id, value);
/// <summary>Sets the value of the specified integer parameter.</summary>
public void SetInteger(string name, int value) => _Controller.State.Playable.SetInteger(name, value);
/// <summary>Sets the specified trigger parameter to true.</summary>
public void SetTrigger(int id) => _Controller.State.Playable.SetTrigger(id);
/// <summary>Sets the specified trigger parameter to true.</summary>
public void SetTrigger(string name) => _Controller.State.Playable.SetTrigger(name);
/// <summary>Resets the specified trigger parameter to false.</summary>
public void ResetTrigger(int id) => _Controller.State.Playable.ResetTrigger(id);
/// <summary>Resets the specified trigger parameter to false.</summary>
public void ResetTrigger(string name) => _Controller.State.Playable.ResetTrigger(name);
/// <summary>Gets the details of one of the <see cref="Controller"/>'s parameters.</summary>
public AnimatorControllerParameter GetParameter(int index) => _Controller.State.Playable.GetParameter(index);
/// <summary>Gets the number of parameters in the <see cref="Controller"/>.</summary>
public int GetParameterCount() => _Controller.State.Playable.GetParameterCount();
/// <summary>Indicates whether the specified parameter is controlled by an <see cref="AnimationClip"/>.</summary>
public bool IsParameterControlledByCurve(int id) => _Controller.State.Playable.IsParameterControlledByCurve(id);
/// <summary>Indicates whether the specified parameter is controlled by an <see cref="AnimationClip"/>.</summary>
public bool IsParameterControlledByCurve(string name) => _Controller.State.Playable.IsParameterControlledByCurve(name);
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Misc
/************************************************************************************************************************/
// Layers.
/************************************************************************************************************************/
/// <summary>Gets the weight of the layer at the specified index.</summary>
public float GetLayerWeight(int layerIndex) => _Controller.State.Playable.GetLayerWeight(layerIndex);
/// <summary>Sets the weight of the layer at the specified index.</summary>
public void SetLayerWeight(int layerIndex, float weight) => _Controller.State.Playable.SetLayerWeight(layerIndex, weight);
/// <summary>Gets the number of layers in the <see cref="Controller"/>.</summary>
public int GetLayerCount() => _Controller.State.Playable.GetLayerCount();
/// <summary>Gets the index of the layer with the specified name.</summary>
public int GetLayerIndex(string layerName) => _Controller.State.Playable.GetLayerIndex(layerName);
/// <summary>Gets the name of the layer with the specified index.</summary>
public string GetLayerName(int layerIndex) => _Controller.State.Playable.GetLayerName(layerIndex);
/************************************************************************************************************************/
// States.
/************************************************************************************************************************/
/// <summary>Returns information about the current state.</summary>
public AnimatorStateInfo GetCurrentAnimatorStateInfo(int layerIndex = 0) => _Controller.State.Playable.GetCurrentAnimatorStateInfo(layerIndex);
/// <summary>Returns information about the next state being transitioned towards.</summary>
public AnimatorStateInfo GetNextAnimatorStateInfo(int layerIndex = 0) => _Controller.State.Playable.GetNextAnimatorStateInfo(layerIndex);
/// <summary>Indicates whether the specified layer contains the specified state.</summary>
public bool HasState(int layerIndex, int stateID) => _Controller.State.Playable.HasState(layerIndex, stateID);
/************************************************************************************************************************/
// Transitions.
/************************************************************************************************************************/
/// <summary>Indicates whether the specified layer is currently executing a transition.</summary>
public bool IsInTransition(int layerIndex = 0) => _Controller.State.Playable.IsInTransition(layerIndex);
/// <summary>Gets information about the current transition.</summary>
public AnimatorTransitionInfo GetAnimatorTransitionInfo(int layerIndex = 0) => _Controller.State.Playable.GetAnimatorTransitionInfo(layerIndex);
/************************************************************************************************************************/
// Clips.
/************************************************************************************************************************/
/// <summary>Gets information about the <see cref="AnimationClip"/>s currently being played.</summary>
public AnimatorClipInfo[] GetCurrentAnimatorClipInfo(int layerIndex = 0) => _Controller.State.Playable.GetCurrentAnimatorClipInfo(layerIndex);
/// <summary>Gets information about the <see cref="AnimationClip"/>s currently being played.</summary>
public void GetCurrentAnimatorClipInfo(int layerIndex, List<AnimatorClipInfo> clips) => _Controller.State.Playable.GetCurrentAnimatorClipInfo(layerIndex, clips);
/// <summary>Gets the number of <see cref="AnimationClip"/>s currently being played.</summary>
public int GetCurrentAnimatorClipInfoCount(int layerIndex = 0) => _Controller.State.Playable.GetCurrentAnimatorClipInfoCount(layerIndex);
/// <summary>Gets information about the <see cref="AnimationClip"/>s currently being transitioned towards.</summary>
public AnimatorClipInfo[] GetNextAnimatorClipInfo(int layerIndex = 0) => _Controller.State.Playable.GetNextAnimatorClipInfo(layerIndex);
/// <summary>Gets information about the <see cref="AnimationClip"/>s currently being transitioned towards.</summary>
public void GetNextAnimatorClipInfo(int layerIndex, List<AnimatorClipInfo> clips) => _Controller.State.Playable.GetNextAnimatorClipInfo(layerIndex, clips);
/// <summary>Gets the number of <see cref="AnimationClip"/>s currently being transitioned towards.</summary>
public int GetNextAnimatorClipInfoCount(int layerIndex = 0) => _Controller.State.Playable.GetNextAnimatorClipInfoCount(layerIndex);
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}