// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik //
using Animancer.Units;
using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer
{
///
/// https://kybernetik.com.au/animancer/api/Animancer/ManualMixerTransitionAsset
#if !UNITY_EDITOR
[System.Obsolete(Validate.ProOnlyMessage)]
#endif
[CreateAssetMenu(menuName = Strings.MenuPrefix + "Mixer Transition/Manual", order = Strings.AssetMenuOrder + 2)]
[HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(ManualMixerTransitionAsset))]
public class ManualMixerTransitionAsset : AnimancerTransitionAsset
{
///
[Serializable]
public new class UnShared :
UnShared,
ManualMixerState.ITransition
{ }
}
///
/// https://kybernetik.com.au/animancer/api/Animancer/ManualMixerTransition_1
#if !UNITY_EDITOR
[System.Obsolete(Validate.ProOnlyMessage)]
#endif
[Serializable]
public abstract class ManualMixerTransition : AnimancerTransition,
IMotion, IAnimationClipCollection, ICopyable>
where TMixer : ManualMixerState
{
/************************************************************************************************************************/
[SerializeField]
[Tooltip(Strings.Tooltips.OptionalSpeed)]
[AnimationSpeed]
[DefaultValue(1f, -1f)]
private float _Speed = 1;
/// []
/// Determines how fast the mixer plays (1x = normal speed, 2x = double speed).
///
public override float Speed
{
get => _Speed;
set => _Speed = value;
}
/************************************************************************************************************************/
[SerializeField]
[UnityEngine.Serialization.FormerlySerializedAs("_Clips")]
[UnityEngine.Serialization.FormerlySerializedAs("_States")]
private Object[] _Animations;
/// [] Objects that define how to create each state in the mixer.
/// See for more information.
public ref Object[] Animations => ref _Animations;
/// The name of the serialized backing field of .
public const string AnimationsField = nameof(_Animations);
/************************************************************************************************************************/
[SerializeField]
[AnimationSpeed]
[DefaultValue(1f, -1f)]
private float[] _Speeds;
/// []
/// The to use for each state in the mixer.
///
/// If the size of this array doesn't match the , it will be ignored.
public ref float[] Speeds => ref _Speeds;
/// The name of the serialized backing field of .
public const string SpeedsField = nameof(_Speeds);
/// Are there at least enough for each of the?
public bool HasSpeeds => _Speeds != null && _Speeds.Length >= _Animations.Length;
/************************************************************************************************************************/
[SerializeField]
private bool[] _SynchronizeChildren;
/// []
/// The flags to be used in .
///
/// The array can be null or empty. Any elements not in the array will be treated as true.
public ref bool[] SynchronizeChildren => ref _SynchronizeChildren;
/// The name of the serialized backing field of .
public const string SynchronizeChildrenField = nameof(_SynchronizeChildren);
/************************************************************************************************************************/
/// [] Are any of the looping?
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;
}
}
///
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;
}
}
///
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;
}
}
///
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;
}
}
/************************************************************************************************************************/
/// Are all assigned?
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;
#if UNITY_EDITOR
return true;
#else
return false;
#endif
}
}
/************************************************************************************************************************/
/// Initializes the immediately after it is created.
public virtual void InitializeState()
{
var mixer = State;
var childCount = mixer.ChildCount;
var auto = ManualMixerState.SynchronizeNewChildren;
try
{
ManualMixerState.SynchronizeNewChildren = false;
mixer.AddRange(_Animations);
}
finally
{
ManualMixerState.SynchronizeNewChildren = 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 count = Math.Min(_Animations.Length, _Speeds.Length);
for (int i = count - 1; i >= 0; i--)
mixer.GetChild(childCount + i).Speed = _Speeds[i];
}
}
/************************************************************************************************************************/
///
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));
}
/************************************************************************************************************************/
/// Adds the to the collection.
void IAnimationClipCollection.GatherAnimationClips(ICollection clips)
=> clips.GatherFromSource(_Animations);
/************************************************************************************************************************/
///
public virtual void CopyFrom(ManualMixerTransition copyFrom)
{
CopyFrom((AnimancerTransition)copyFrom);
if (copyFrom == null)
{
_Speed = 1;
_Animations = default;
_Speeds = default;
_SynchronizeChildren = default;
return;
}
_Speed = copyFrom._Speed;
AnimancerUtilities.CopyExactArray(copyFrom._Animations, ref _Animations);
AnimancerUtilities.CopyExactArray(copyFrom._Speeds, ref _Speeds);
AnimancerUtilities.CopyExactArray(copyFrom._SynchronizeChildren, ref _SynchronizeChildren);
}
/************************************************************************************************************************/
}
}