123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- using System;
- using Unity.Collections;
- using UnityEngine;
- using UnityEngine.Animations;
- using UnityEngine.Playables;
- using Object = UnityEngine.Object;
- namespace Animancer
- {
-
-
-
- public static partial class AnimancerUtilities
- {
-
- #region Misc
-
-
- public const bool IsAnimancerPro = true;
-
-
-
- public static float Wrap01(float value)
- {
- var valueAsDouble = (double)value;
- value = (float)(valueAsDouble - Math.Floor(valueAsDouble));
- return value < 1 ? value : 0;
- }
-
-
- public static float Wrap(float value, float length)
- {
- var valueAsDouble = (double)value;
- var lengthAsDouble = (double)length;
- value = (float)(valueAsDouble - Math.Floor(valueAsDouble / lengthAsDouble) * lengthAsDouble);
- return value < length ? value : 0;
- }
-
-
-
-
- public static float Round(float value)
- => (float)Math.Round(value, MidpointRounding.AwayFromZero);
-
-
-
- public static float Round(float value, float multiple)
- => Round(value / multiple) * multiple;
-
-
-
- public static bool IsFinite(this float value) => !float.IsNaN(value) && !float.IsInfinity(value);
-
- public static bool IsFinite(this Vector2 value) => value.x.IsFinite() && value.y.IsFinite();
-
-
-
-
-
-
- public static string ToStringOrNull(object obj)
- {
- if (obj is null)
- return "Null";
- if (obj is Object unityObject && unityObject == null)
- return $"Null ({obj.GetType()})";
- return obj.ToString();
- }
-
-
- public static void CopyExactArray<T>(T[] copyFrom, ref T[] copyTo)
- {
- if (copyFrom == null)
- {
- copyTo = null;
- return;
- }
- var length = copyFrom.Length;
- SetLength(ref copyTo, length);
- Array.Copy(copyFrom, copyTo, length);
- }
-
-
- public static void Swap<T>(this T[] array, int a, int b)
- {
- var temp = array[a];
- array[a] = array[b];
- array[b] = temp;
- }
-
-
-
-
- public static bool IsNullOrEmpty<T>(this T[] array) => array == null || array.Length == 0;
-
-
-
-
-
-
-
-
-
- public static bool SetLength<T>(ref T[] array, int length)
- {
- if (array == null || array.Length != length)
- {
- array = new T[length];
- return true;
- }
- else return false;
- }
-
-
- public static bool IsValid(this AnimancerNode node) => node != null && node.IsValid;
-
- public static bool IsValid(this ITransitionDetailed transition) => transition != null && transition.IsValid;
-
-
- public static AnimancerState CreateStateAndApply(this ITransition transition, AnimancerPlayable root = null)
- {
- var state = transition.CreateState();
- state.SetRoot(root);
- transition.Apply(state);
- return state;
- }
-
-
- public static void RemovePlayable(Playable playable, bool destroy = true)
- {
- if (!playable.IsValid())
- return;
- Assert(playable.GetInputCount() == 1,
- $"{nameof(RemovePlayable)} can only be used on playables with 1 input.");
- Assert(playable.GetOutputCount() == 1,
- $"{nameof(RemovePlayable)} can only be used on playables with 1 output.");
- var input = playable.GetInput(0);
- if (!input.IsValid())
- {
- if (destroy)
- playable.Destroy();
- return;
- }
- var graph = playable.GetGraph();
- var output = playable.GetOutput(0);
- if (output.IsValid())
- {
- if (destroy)
- {
- playable.Destroy();
- }
- else
- {
- Assert(output.GetInputCount() == 1,
- $"{nameof(RemovePlayable)} can only be used on playables connected to a playable with 1 input.");
- graph.Disconnect(output, 0);
- graph.Disconnect(playable, 0);
- }
- graph.Connect(input, 0, output, 0);
- }
- else
- {
- Assert(graph.GetOutput(0).GetSourcePlayable().Equals(playable),
- $"{nameof(RemovePlayable)} can only be used on playables connected to another playable or to the graph output.");
- if (destroy)
- playable.Destroy();
- else
- graph.Disconnect(playable, 0);
- graph.GetOutput(0).SetSourcePlayable(input);
- }
- }
-
-
-
-
-
- public static bool HasEvent(IAnimationClipCollection source, string functionName)
- {
- var clips = ObjectPool.AcquireSet<AnimationClip>();
- source.GatherAnimationClips(clips);
- foreach (var clip in clips)
- {
- if (HasEvent(clip, functionName))
- {
- ObjectPool.Release(clips);
- return true;
- }
- }
- ObjectPool.Release(clips);
- return false;
- }
-
- public static bool HasEvent(AnimationClip clip, string functionName)
- {
- var events = clip.events;
- for (int i = events.Length - 1; i >= 0; i--)
- {
- if (events[i].functionName == functionName)
- return true;
- }
- return false;
- }
-
-
-
-
-
-
-
-
- public static void CalculateThresholdsFromAverageVelocityXZ(this MixerState<Vector2> mixer)
- {
- mixer.ValidateThresholdCount();
- for (int i = mixer.ChildCount - 1; i >= 0; i--)
- {
- var state = mixer.GetChild(i);
- if (state == null)
- continue;
- var averageVelocity = state.AverageVelocity;
- mixer.SetThreshold(i, new Vector2(averageVelocity.x, averageVelocity.z));
- }
- }
-
-
- public static object GetParameterValue(Animator animator, AnimatorControllerParameter parameter)
- {
- switch (parameter.type)
- {
- case AnimatorControllerParameterType.Float:
- return animator.GetFloat(parameter.nameHash);
- case AnimatorControllerParameterType.Int:
- return animator.GetInteger(parameter.nameHash);
- case AnimatorControllerParameterType.Bool:
- case AnimatorControllerParameterType.Trigger:
- return animator.GetBool(parameter.nameHash);
- default:
- throw CreateUnsupportedArgumentException(parameter.type);
- }
- }
-
- public static object GetParameterValue(AnimatorControllerPlayable playable, AnimatorControllerParameter parameter)
- {
- switch (parameter.type)
- {
- case AnimatorControllerParameterType.Float:
- return playable.GetFloat(parameter.nameHash);
- case AnimatorControllerParameterType.Int:
- return playable.GetInteger(parameter.nameHash);
- case AnimatorControllerParameterType.Bool:
- case AnimatorControllerParameterType.Trigger:
- return playable.GetBool(parameter.nameHash);
- default:
- throw CreateUnsupportedArgumentException(parameter.type);
- }
- }
-
-
- public static void SetParameterValue(Animator animator, AnimatorControllerParameter parameter, object value)
- {
- switch (parameter.type)
- {
- case AnimatorControllerParameterType.Float:
- animator.SetFloat(parameter.nameHash, (float)value);
- break;
- case AnimatorControllerParameterType.Int:
- animator.SetInteger(parameter.nameHash, (int)value);
- break;
- case AnimatorControllerParameterType.Bool:
- animator.SetBool(parameter.nameHash, (bool)value);
- break;
- case AnimatorControllerParameterType.Trigger:
- if ((bool)value)
- animator.SetTrigger(parameter.nameHash);
- else
- animator.ResetTrigger(parameter.nameHash);
- break;
- default:
- throw CreateUnsupportedArgumentException(parameter.type);
- }
- }
-
- public static void SetParameterValue(AnimatorControllerPlayable playable, AnimatorControllerParameter parameter, object value)
- {
- switch (parameter.type)
- {
- case AnimatorControllerParameterType.Float:
- playable.SetFloat(parameter.nameHash, (float)value);
- break;
- case AnimatorControllerParameterType.Int:
- playable.SetInteger(parameter.nameHash, (int)value);
- break;
- case AnimatorControllerParameterType.Bool:
- playable.SetBool(parameter.nameHash, (bool)value);
- break;
- case AnimatorControllerParameterType.Trigger:
- if ((bool)value)
- playable.SetTrigger(parameter.nameHash);
- else
- playable.ResetTrigger(parameter.nameHash);
- break;
- default:
- throw CreateUnsupportedArgumentException(parameter.type);
- }
- }
-
-
-
-
-
-
-
- public static NativeArray<T> CreateNativeReference<T>() where T : struct
- {
- return new NativeArray<T>(1, Allocator.Persistent, NativeArrayOptions.ClearMemory);
- }
-
-
- public static string GetUnsupportedMessage<T>(T value)
- => $"Unsupported {typeof(T).FullName}: {value}";
-
- public static ArgumentException CreateUnsupportedArgumentException<T>(T value)
- => new ArgumentException(GetUnsupportedMessage(value));
-
- #endregion
-
- #region Components
-
-
-
-
- public static T AddAnimancerComponent<T>(this Animator animator) where T : Component, IAnimancerComponent
- {
- var animancer = animator.gameObject.AddComponent<T>();
- animancer.Animator = animator;
- return animancer;
- }
-
-
-
-
-
- public static T GetOrAddAnimancerComponent<T>(this Animator animator) where T : Component, IAnimancerComponent
- {
- if (animator.TryGetComponent<T>(out var component))
- return component;
- else
- return animator.AddAnimancerComponent<T>();
- }
-
-
-
-
-
- public static T GetComponentInParentOrChildren<T>(this GameObject gameObject) where T : class
- {
- var component = gameObject.GetComponentInParent<T>();
- if (component != null)
- return component;
- return gameObject.GetComponentInChildren<T>();
- }
-
-
-
-
- public static bool GetComponentInParentOrChildren<T>(this GameObject gameObject, ref T component) where T : class
- {
- if (component != null &&
- (!(component is Object obj) || obj != null))
- return false;
- component = gameObject.GetComponentInParentOrChildren<T>();
- return !(component is null);
- }
-
- #endregion
-
- #region Editor
-
-
-
-
-
-
-
-
- [System.Diagnostics.Conditional(Strings.Assertions)]
- public static void Assert(bool condition, object message)
- {
- #if UNITY_ASSERTIONS
- if (!condition)
- throw new UnityEngine.Assertions.AssertionException(message != null ? message.ToString() : "Assertion failed.", null);
- #endif
- }
-
-
- [System.Diagnostics.Conditional(Strings.UnityEditor)]
- public static void SetDirty(Object target)
- {
- #if UNITY_EDITOR
- UnityEditor.EditorUtility.SetDirty(target);
- #endif
- }
-
-
-
-
-
-
-
-
-
-
-
-
- [System.Diagnostics.Conditional(Strings.UnityEditor)]
- public static void EditModeSampleAnimation(this AnimationClip clip, Component component, float time = 0)
- {
- #if UNITY_EDITOR
- if (!ShouldEditModeSample(clip, component))
- return;
- var gameObject = component.gameObject;
- component = gameObject.GetComponentInParentOrChildren<Animator>();
- if (component == null)
- {
- component = gameObject.GetComponentInParentOrChildren<Animation>();
- if (component == null)
- return;
- }
- UnityEditor.EditorApplication.delayCall += () =>
- {
- if (!ShouldEditModeSample(clip, component))
- return;
- clip.SampleAnimation(component.gameObject, time);
- };
- }
- private static bool ShouldEditModeSample(AnimationClip clip, Component component)
- {
- return
- !UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode &&
- clip != null &&
- component != null &&
- !UnityEditor.EditorUtility.IsPersistent(component);
- #endif
- }
-
-
-
-
-
-
-
-
-
- [System.Diagnostics.Conditional(Strings.UnityEditor)]
- public static void EditModePlay(this AnimationClip clip, Component component)
- {
- #if UNITY_EDITOR
- if (!ShouldEditModeSample(clip, component))
- return;
- var animancer = component as IAnimancerComponent;
- if (animancer == null)
- animancer = component.gameObject.GetComponentInParentOrChildren<IAnimancerComponent>();
- if (!ShouldEditModePlay(animancer, clip))
- return;
-
- if (animancer.IsPlayableInitialized)
- {
- animancer.Playable.Play(clip);
- return;
- }
-
- UnityEditor.EditorApplication.delayCall += () =>
- {
- if (ShouldEditModePlay(animancer, clip))
- animancer.Playable.Play(clip);
- };
- }
- private static bool ShouldEditModePlay(IAnimancerComponent animancer, AnimationClip clip)
- {
- return
- ShouldEditModeSample(clip, animancer?.Animator) &&
- (!(animancer is Object obj) || obj != null);
- #endif
- }
-
- #if UNITY_ASSERTIONS
-
- private static System.Reflection.FieldInfo _DelegatesField;
- private static bool _GotDelegatesField;
-
-
-
-
-
-
-
-
-
-
- public static bool TryGetInvocationListNonAlloc(MulticastDelegate multicast, out Delegate[] delegates)
- {
- if (multicast == null)
- {
- delegates = null;
- return false;
- }
- if (!_GotDelegatesField)
- {
- const string FieldName = "delegates";
- _GotDelegatesField = true;
- _DelegatesField = typeof(MulticastDelegate).GetField("delegates",
- System.Reflection.BindingFlags.Public |
- System.Reflection.BindingFlags.NonPublic |
- System.Reflection.BindingFlags.Instance);
- if (_DelegatesField != null && _DelegatesField.FieldType != typeof(Delegate[]))
- _DelegatesField = null;
- if (_DelegatesField == null)
- Debug.LogError($"Unable to find {nameof(MulticastDelegate)}.{FieldName} field.");
- }
- if (_DelegatesField == null)
- {
- delegates = null;
- return false;
- }
- else
- {
- delegates = (Delegate[])_DelegatesField.GetValue(multicast);
- return true;
- }
- }
-
- #endif
-
- #endregion
-
- }
- }
|