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.
274 lines
10 KiB
274 lines
10 KiB
// Animancer // https://kybernetik.com.au/animancer // Copyright 2021 Kybernetik // |
|
|
|
using Animancer.Units; |
|
using System; |
|
using System.Collections.Generic; |
|
using UnityEngine; |
|
using Object = UnityEngine.Object; |
|
|
|
namespace Animancer |
|
{ |
|
/// <inheritdoc/> |
|
/// https://kybernetik.com.au/animancer/api/Animancer/ManualMixerTransitionAsset |
|
[CreateAssetMenu(menuName = Strings.MenuPrefix + "Mixer Transition/Manual", order = Strings.AssetMenuOrder + 2)] |
|
[HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(ManualMixerTransitionAsset))] |
|
public class ManualMixerTransitionAsset : AnimancerTransitionAsset<ManualMixerTransition> |
|
{ |
|
/// <inheritdoc/> |
|
[Serializable] |
|
public class UnShared : |
|
AnimancerTransitionAsset.UnShared<ManualMixerTransitionAsset, ManualMixerTransition, ManualMixerState>, |
|
ManualMixerState.ITransition |
|
{ } |
|
} |
|
|
|
/// <inheritdoc/> |
|
/// https://kybernetik.com.au/animancer/api/Animancer/ManualMixerTransition_1 |
|
[Serializable] |
|
public abstract class ManualMixerTransition<TMixer> : AnimancerTransition<TMixer>, IMotion, IAnimationClipCollection |
|
where TMixer : ManualMixerState |
|
{ |
|
/************************************************************************************************************************/ |
|
|
|
[SerializeField] |
|
[Tooltip(Strings.Tooltips.OptionalSpeed)] |
|
[AnimationSpeed] |
|
[DefaultValue(1f, -1f)] |
|
private float _Speed = 1; |
|
|
|
/// <summary>[<see cref="SerializeField"/>] |
|
/// Determines how fast the mixer plays (1x = normal speed, 2x = double speed). |
|
/// </summary> |
|
public override float Speed |
|
{ |
|
get => _Speed; |
|
set => _Speed = value; |
|
} |
|
|
|
/************************************************************************************************************************/ |
|
|
|
[SerializeField] |
|
[UnityEngine.Serialization.FormerlySerializedAs("_Clips")] |
|
[UnityEngine.Serialization.FormerlySerializedAs("_States")] |
|
private Object[] _Animations; |
|
|
|
/// <summary>[<see cref="SerializeField"/>] Objects that define how to create each state in the mixer.</summary> |
|
/// <remarks>See <see cref="ManualMixerState.Initialize(Object[])"/> for more information.</remarks> |
|
public ref Object[] Animations => ref _Animations; |
|
|
|
/// <summary>The name of the serialized backing field of <see cref="Animations"/>.</summary> |
|
public const string AnimationsField = nameof(_Animations); |
|
|
|
/************************************************************************************************************************/ |
|
|
|
[SerializeField] |
|
[AnimationSpeed] |
|
[DefaultValue(1f, -1f)] |
|
private float[] _Speeds; |
|
|
|
/// <summary>[<see cref="SerializeField"/>] |
|
/// The <see cref="AnimancerNode.Speed"/> to use for each state in the mixer. |
|
/// </summary> |
|
/// <remarks>If the size of this array doesn't match the <see cref="Animations"/>, it will be ignored.</remarks> |
|
public ref float[] Speeds => ref _Speeds; |
|
|
|
/// <summary>The name of the serialized backing field of <see cref="Speeds"/>.</summary> |
|
public const string SpeedsField = nameof(_Speeds); |
|
|
|
/// <summary>Are there at least enough <see cref="Speeds"/> for each of the<see cref="Animations"/>?</summary> |
|
public bool HasSpeeds => _Speeds != null && _Speeds.Length >= _Animations.Length; |
|
|
|
/************************************************************************************************************************/ |
|
|
|
[SerializeField] |
|
private bool[] _SynchronizeChildren; |
|
|
|
/// <summary>[<see cref="SerializeField"/>] |
|
/// The flags to be used in <see cref="MixerState.InitializeSynchronizedChildren"/>. |
|
/// </summary> |
|
/// <remarks>The array can be null or empty. Any elements not in the array will be treated as true.</remarks> |
|
public ref bool[] SynchronizeChildren => ref _SynchronizeChildren; |
|
|
|
/// <summary>The name of the serialized backing field of <see cref="SynchronizeChildren"/>.</summary> |
|
public const string SynchronizeChildrenField = nameof(_SynchronizeChildren); |
|
|
|
/************************************************************************************************************************/ |
|
|
|
/// <summary>[<see cref="ITransitionDetailed"/>] Are any of the <see cref="Animations"/> looping?</summary> |
|
public override bool IsLooping |
|
{ |
|
get |
|
{ |
|
for (int i = _Animations.Length - 1; i >= 0; i--) |
|
{ |
|
if (AnimancerUtilities.TryGetIsLooping(_Animations[i], out var isLooping) && |
|
isLooping) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
} |
|
|
|
/// <inheritdoc/> |
|
public override float MaximumDuration |
|
{ |
|
get |
|
{ |
|
if (_Animations == null) |
|
return 0; |
|
|
|
var duration = 0f; |
|
var hasSpeeds = HasSpeeds; |
|
|
|
for (int i = _Animations.Length - 1; i >= 0; i--) |
|
{ |
|
if (!AnimancerUtilities.TryGetLength(_Animations[i], out var length)) |
|
continue; |
|
|
|
if (hasSpeeds) |
|
length *= _Speeds[i]; |
|
|
|
if (duration < length) |
|
duration = length; |
|
} |
|
|
|
return duration; |
|
} |
|
} |
|
|
|
/// <inheritdoc/> |
|
public virtual float AverageAngularSpeed |
|
{ |
|
get |
|
{ |
|
if (_Animations == null) |
|
return default; |
|
|
|
var average = 0f; |
|
var hasSpeeds = HasSpeeds; |
|
|
|
var count = 0; |
|
for (int i = _Animations.Length - 1; i >= 0; i--) |
|
{ |
|
if (AnimancerUtilities.TryGetAverageAngularSpeed(_Animations[i], out var speed)) |
|
{ |
|
if (hasSpeeds) |
|
speed *= _Speeds[i]; |
|
|
|
average += speed; |
|
count++; |
|
} |
|
} |
|
|
|
return average / count; |
|
} |
|
} |
|
|
|
/// <inheritdoc/> |
|
public virtual Vector3 AverageVelocity |
|
{ |
|
get |
|
{ |
|
if (_Animations == null) |
|
return default; |
|
|
|
var average = new Vector3(); |
|
var hasSpeeds = HasSpeeds; |
|
|
|
var count = 0; |
|
for (int i = _Animations.Length - 1; i >= 0; i--) |
|
{ |
|
if (AnimancerUtilities.TryGetAverageVelocity(_Animations[i], out var velocity)) |
|
{ |
|
if (hasSpeeds) |
|
velocity *= _Speeds[i]; |
|
|
|
average += velocity; |
|
count++; |
|
} |
|
} |
|
|
|
return average / count; |
|
} |
|
} |
|
|
|
/************************************************************************************************************************/ |
|
|
|
/// <summary>Are all <see cref="Animations"/> assigned?</summary> |
|
public override bool IsValid |
|
{ |
|
get |
|
{ |
|
if (_Animations == null || |
|
_Animations.Length == 0) |
|
return false; |
|
|
|
for (int i = _Animations.Length - 1; i >= 0; i--) |
|
if (_Animations[i] == null) |
|
return false; |
|
|
|
return true; |
|
} |
|
} |
|
|
|
/************************************************************************************************************************/ |
|
|
|
/// <summary>Initializes the <see cref="AnimancerTransition{TState}.State"/> immediately after it is created.</summary> |
|
public virtual void InitializeState() |
|
{ |
|
var mixer = State; |
|
|
|
var auto = MixerState.AutoSynchronizeChildren; |
|
try |
|
{ |
|
MixerState.AutoSynchronizeChildren = false; |
|
mixer.Initialize(_Animations); |
|
} |
|
finally |
|
{ |
|
MixerState.AutoSynchronizeChildren = auto; |
|
} |
|
|
|
mixer.InitializeSynchronizedChildren(_SynchronizeChildren); |
|
|
|
if (_Speeds != null) |
|
{ |
|
#if UNITY_ASSERTIONS |
|
if (_Speeds.Length != 0 && _Speeds.Length != _Animations.Length) |
|
Debug.LogError( |
|
$"The number of serialized {nameof(Speeds)} ({_Speeds.Length})" + |
|
$" does not match the number of {nameof(Animations)} ({_Animations.Length}).", |
|
mixer.Root?.Component as Object); |
|
#endif |
|
|
|
var children = mixer.ChildStates; |
|
var count = Math.Min(children.Count, _Speeds.Length); |
|
while (--count >= 0) |
|
children[count].Speed = _Speeds[count]; |
|
} |
|
} |
|
|
|
/************************************************************************************************************************/ |
|
|
|
/// <inheritdoc/> |
|
public override void Apply(AnimancerState state) |
|
{ |
|
base.Apply(state); |
|
|
|
if (!float.IsNaN(_Speed)) |
|
state.Speed = _Speed; |
|
|
|
for (int i = 0; i < _Animations.Length; i++) |
|
if (_Animations[i] is ITransition transition) |
|
transition.Apply(state.GetChild(i)); |
|
} |
|
|
|
/************************************************************************************************************************/ |
|
|
|
/// <summary>Adds the <see cref="Animations"/> to the collection.</summary> |
|
void IAnimationClipCollection.GatherAnimationClips(ICollection<AnimationClip> clips) => clips.GatherFromSource(_Animations); |
|
|
|
/************************************************************************************************************************/ |
|
} |
|
}
|
|
|