123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- using System;
- using System.Collections.Generic;
- using System.Text;
- using UnityEngine;
- namespace Animancer.TransitionLibraries
- {
- /// <summary>[<see cref="SerializableAttribute"/>]
- /// A library of transitions and other details which can create a <see cref="TransitionLibrary"/>.
- /// </summary>
- /// <remarks>
- /// <strong>Documentation:</strong>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/transitions/libraries">
- /// Transition Libraries</see>
- /// </remarks>
- /// https://kybernetik.com.au/animancer/api/Animancer.TransitionLibraries/TransitionLibraryDefinition
- [Serializable]
- public class TransitionLibraryDefinition :
- IAnimationClipSource,
- ICopyable<TransitionLibraryDefinition>,
- IEquatable<TransitionLibraryDefinition>,
- IHasDescription
- {
- /************************************************************************************************************************/
- #region Fields and Properties
- /************************************************************************************************************************/
- [SerializeField]
- private TransitionAssetBase[]
- _Transitions = Array.Empty<TransitionAssetBase>();
- /// <summary>[<see cref="SerializeField"/>] The transitions in this library.</summary>
- /// <remarks>This property uses an empty array instead of <c>null</c>.</remarks>
- public TransitionAssetBase[] Transitions
- {
- get => _Transitions;
- set => _Transitions = value.NullIsEmpty();
- }
- /************************************************************************************************************************/
- [SerializeField]
- private TransitionModifierDefinition[]
- _Modifiers = Array.Empty<TransitionModifierDefinition>();
- /// <summary>[<see cref="SerializeField"/>] Modified fade durations for specific transition combinations.</summary>
- /// <remarks>This property uses an empty array instead of <c>null</c>.</remarks>
- public TransitionModifierDefinition[] Modifiers
- {
- get => _Modifiers;
- set => _Modifiers = value.NullIsEmpty();
- }
- /************************************************************************************************************************/
- [SerializeField]
- private NamedIndex[]
- _Aliases = Array.Empty<NamedIndex>();
- /// <summary>[<see cref="SerializeField"/>] Alternate names that can be used to look up transitions.</summary>
- /// <remarks>
- /// This array should always be sorted, use <see cref="SortAliases"/> if necessary.
- /// <para></para>
- /// This property uses an empty array instead of <c>null</c>.
- /// </remarks>
- public NamedIndex[] Aliases
- {
- get => _Aliases;
- set => _Aliases = value.NullIsEmpty();
- }
- /************************************************************************************************************************/
- [SerializeField]
- [Tooltip(AliasAllTransitionsTooltip)]
- private bool _AliasAllTransitions;
- /// <summary>[<see cref="SerializeField"/>]
- /// Should all Transitions automatically be registered using their name as an Alias?
- /// </summary>
- public ref bool AliasAllTransitions
- => ref _AliasAllTransitions;
- #if UNITY_EDITOR
- /// <summary>[Editor-Only] [Internal]
- /// The name of the field which stores the <see cref="AliasAllTransitions"/>.
- /// </summary>
- internal const string AliasAllTransitionsField = nameof(_AliasAllTransitions);
- #endif
- /// <summary>Tooltip for the <see cref="AliasAllTransitions"/> field.</summary>
- public const string AliasAllTransitionsTooltip =
- "Should all Transitions automatically be registered using their name as an Alias?";
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Transitions
- /************************************************************************************************************************/
- /// <summary>
- /// <see cref="AnimancerUtilities.TryGet{T}(IList{T}, int, out T)"/> for the <see cref="Transitions"/>.
- /// </summary>
- public bool TryGetTransition(
- int index,
- out TransitionAssetBase transition)
- => _Transitions.TryGet(index, out transition)
- && transition != null;
- /************************************************************************************************************************/
- /// <summary>Adds an item to the end of the <see cref="Transitions"/>.</summary>
- public void AddTransition(
- TransitionAssetBase transition)
- => AnimancerUtilities.InsertAt(
- ref _Transitions,
- _Transitions.Length,
- transition);
- /************************************************************************************************************************/
- /// <summary>
- /// Removes an item from the <see cref="Transitions"/>
- /// and adjusts the other fields to account for the moved indices.
- /// </summary>
- public void RemoveTransition(int index)
- {
- if ((uint)index >= _Transitions.Length)
- return;
- AnimancerUtilities.RemoveAt(ref _Transitions, index);
- for (int i = _Modifiers.Length - 1; i >= 0; i--)
- {
- var modifier = _Modifiers[i];
- // Remove any modifiers targeting that transition.
- if (modifier.FromIndex == index ||
- modifier.ToIndex == index)
- {
- AnimancerUtilities.RemoveAt(ref _Modifiers, i);
- }
- else// Adjust the indices of any modifiers after it.
- {
- var fromIndex = modifier.FromIndex;
- if (fromIndex > index)
- fromIndex--;
- var toIndex = modifier.ToIndex;
- if (toIndex > index)
- toIndex--;
- _Modifiers[i] = modifier.WithIndices(fromIndex, toIndex);
- }
- }
- for (int i = _Aliases.Length - 1; i >= 0; i--)
- {
- var alias = _Aliases[i];
- // Remove any aliases targeting that transition.
- if (alias.Index == index)
- {
- AnimancerUtilities.RemoveAt(ref _Aliases, i);
- }
- else// Adjust the indices of any aliases after it.
- {
- if (alias.Index > index)
- _Aliases[i] = alias.With(alias.Index - 1);
- }
- }
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Modifiers
- /************************************************************************************************************************/
- /// <summary>Tries to find an item in the <see cref="Modifiers"/> with the specified indices.</summary>
- /// <remarks>
- /// If unsuccessful, the `modifier` is given the <see cref="ITransition.FadeDuration"/>
- /// from the <see cref="Transitions"/> at the `toIndex`. and this method returns false.
- /// </remarks>
- public bool TryGetModifier(
- int fromIndex,
- int toIndex,
- out TransitionModifierDefinition modifier)
- {
- var index = IndexOfModifier(fromIndex, toIndex);
- if (index >= 0)
- {
- modifier = _Modifiers[index];
- return true;
- }
- var fadeDuration = TryGetTransition(toIndex, out var transition)
- ? transition.TryGetFadeDuration()
- : float.NaN;
- modifier = new(fromIndex, toIndex, fadeDuration);
- return false;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Returns the index in the <see cref="Modifiers"/> which matches the given
- /// <see cref="TransitionModifierDefinition.FromIndex"/> and
- /// <see cref="TransitionModifierDefinition.ToIndex"/> or -1 if no such item exists.
- /// </summary>
- public int IndexOfModifier(int fromIndex, int toIndex)
- {
- for (int i = _Modifiers.Length - 1; i >= 0; i--)
- {
- var modifier = _Modifiers[i];
- if (modifier.FromIndex == fromIndex &&
- modifier.ToIndex == toIndex)
- return i;
- }
- return -1;
- }
- /************************************************************************************************************************/
- /// <summary>Adds or replaces an item in the <see cref="Modifiers"/>.</summary>
- public void SetModifier(
- TransitionModifierDefinition modifier)
- {
- if (float.IsNaN(modifier.FadeDuration))
- {
- RemoveModifier(modifier);
- return;
- }
- if (modifier.FadeDuration < 0)
- modifier = modifier.WithFadeDuration(0);
- var index = IndexOfModifier(modifier.FromIndex, modifier.ToIndex);
- if (index >= 0)
- {
- _Modifiers[index] = modifier;
- }
- else
- {
- AnimancerUtilities.InsertAt(ref _Modifiers, _Modifiers.Length, modifier);
- }
- }
- /************************************************************************************************************************/
- /// <summary>Removes an item from the <see cref="Modifiers"/>.</summary>
- public bool RemoveModifier(
- TransitionModifierDefinition modifier)
- => RemoveModifier(modifier.FromIndex, modifier.ToIndex);
- /// <summary>Removes an item from the <see cref="Modifiers"/>.</summary>
- public bool RemoveModifier(int fromIndex, int toIndex)
- {
- var index = IndexOfModifier(fromIndex, toIndex);
- if (index < 0)
- return false;
- AnimancerUtilities.RemoveAt(ref _Modifiers, index);
- return true;
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Aliases
- /************************************************************************************************************************/
- /// <summary>Adds an item to the <see cref="Aliases"/>, sorted by its values.</summary>
- public int AddAlias(NamedIndex alias)
- {
- int i = 0;
- for (; i < _Aliases.Length; i++)
- if (alias.CompareTo(_Aliases[i]) <= 0)
- break;
- AnimancerUtilities.InsertAt(ref _Aliases, i, alias);
- return i;
- }
- /************************************************************************************************************************/
- /// <summary>Removes an item from the <see cref="Aliases"/>.</summary>
- public bool RemoveAlias(NamedIndex alias)
- {
- var index = Array.IndexOf(_Aliases, alias);
- if (index < 0)
- return false;
- RemoveAlias(index);
- return true;
- }
- /// <summary>Removes an item from the <see cref="Aliases"/>.</summary>
- public void RemoveAlias(int index)
- => AnimancerUtilities.RemoveAt(ref _Aliases, index);
- /************************************************************************************************************************/
- /// <summary>Ensures that the <see cref="Aliases"/> are sorted.</summary>
- /// <remarks>This method shouldn't need to be called manually since aliases are always added in order.</remarks>
- public void SortAliases()
- => Array.Sort(_Aliases, (a, b) => a.CompareTo(b));
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Equality
- /************************************************************************************************************************/
- /// <summary>Are all fields in this object equal to the equivalent in `obj`?</summary>
- public override bool Equals(object obj)
- => Equals(obj as TransitionLibraryDefinition);
- /// <summary>Are all fields in this object equal to the equivalent fields in `other`?</summary>
- public bool Equals(TransitionLibraryDefinition other)
- => other != null
- && AnimancerUtilities.ContentsAreEqual(_Transitions, other._Transitions)
- && AnimancerUtilities.ContentsAreEqual(_Modifiers, other._Modifiers)
- && AnimancerUtilities.ContentsAreEqual(_Aliases, other._Aliases)
- && _AliasAllTransitions == other._AliasAllTransitions;
- /// <summary>Are all fields in `a` equal to the equivalent fields in `b`?</summary>
- public static bool operator ==(TransitionLibraryDefinition a, TransitionLibraryDefinition b)
- => a is null
- ? b is null
- : a.Equals(b);
- /// <summary>Are any fields in `a` not equal to the equivalent fields in `b`?</summary>
- public static bool operator !=(TransitionLibraryDefinition a, TransitionLibraryDefinition b)
- => !(a == b);
- /************************************************************************************************************************/
- /// <summary>Returns a hash code based on the values of this object's fields.</summary>
- public override int GetHashCode()
- => AnimancerUtilities.Hash(-871379578,
- _Transitions.SafeGetHashCode(),
- _Modifiers.SafeGetHashCode(),
- _Aliases.SafeGetHashCode(),
- _AliasAllTransitions.SafeGetHashCode());
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Other
- /************************************************************************************************************************/
- /// <summary>Gathers all the animations in this definition.</summary>
- public void GetAnimationClips(List<AnimationClip> results)
- => results.GatherFromSource(_Transitions);
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public void CopyFrom(TransitionLibraryDefinition copyFrom, CloneContext context)
- {
- AnimancerUtilities.CopyExactArray(copyFrom._Transitions, ref _Transitions);
- AnimancerUtilities.CopyExactArray(copyFrom._Modifiers, ref _Modifiers);
- AnimancerUtilities.CopyExactArray(copyFrom._Aliases, ref _Aliases);
- _AliasAllTransitions = copyFrom._AliasAllTransitions;
- }
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public void AppendDescription(StringBuilder text, string separator = "\n")
- {
- text.Append(GetType().Name);
- if (!separator.StartsWithNewLine())
- separator = "\n" + separator;
- var indentedSeparator = separator + Strings.Indent;
- text.AppendField(separator, nameof(Transitions), Transitions.Length);
- for (int i = 0; i < Transitions.Length; i++)
- text.AppendField(indentedSeparator, i.ToString(), Transitions[i]);
- text.AppendField(separator, nameof(Modifiers), Modifiers.Length);
- for (int i = 0; i < Modifiers.Length; i++)
- text.AppendField(indentedSeparator, i.ToString(), Modifiers[i]);
- text.AppendField(separator, nameof(Aliases), Aliases.Length);
- for (int i = 0; i < Aliases.Length; i++)
- text.AppendField(indentedSeparator, i.ToString(), Aliases[i]);
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- }
- }
|