// 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); } /************************************************************************************************************************/ } }