123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- namespace Animancer.TransitionLibraries
- {
- /// <summary>[Pro-Only]
- /// A library of <see cref="ITransition"/>s which allows specific
- /// transition combinations to be overridden without needing to be hard coded.
- /// </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/TransitionLibrary
- public class TransitionLibrary :
- IAnimationClipSource,
- ICopyable<TransitionLibrary>
- {
- /************************************************************************************************************************/
- #region Fields and Properties
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] Modifiers in the order they are created.</summary>
- /// <remarks>The <see cref="TransitionModifierGroup.Index"/> of each item corresponds to its position in this list.</remarks>
- private readonly List<TransitionModifierGroup>
- TransitionModifiers = new();
- /// <summary>[Pro-Only] Modifiers registered by their <see cref="IHasKey.Key"/> as well as any custom aliases.</summary>
- private readonly Dictionary<object, TransitionModifierGroup>
- KeyedTransitionModifiers = new();
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] The number of transitions in this library.</summary>
- public int Count
- => TransitionModifiers.Count;
- /// <summary>[Pro-Only] The number of transitions in this library plus any additional aliases.</summary>
- public int AliasCount
- => KeyedTransitionModifiers.Count;
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Queries
- /************************************************************************************************************************/
- /// <summary>[Pro-Only]
- /// Does this library contain a transition registered with the `key`?
- /// </summary>
- public bool ContainsKey(object key)
- => KeyedTransitionModifiers.ContainsKey(key);
- /// <summary>[Pro-Only]
- /// Does this library contain a transition registered with the <see cref="IHasKey.Key"/>?
- /// </summary>
- public bool ContainsKey(IHasKey hasKey)
- => ContainsKey(hasKey.Key);
- /************************************************************************************************************************/
- /// <summary>[Pro-Only]
- /// Tries to find a <see cref="TransitionModifierGroup"/> registered with the `key`.
- /// </summary>
- public bool TryGetTransition(object key, out TransitionModifierGroup transition)
- {
- #if UNITY_ASSERTIONS
- if (KeyedTransitionModifiers.TryGetValue(key, out transition))
- return true;
- AssertStringReference(key);
- return false;
- #else
- return KeyedTransitionModifiers.TryGetValue(key, out transition);
- #endif
- }
- /// <summary>[Pro-Only]
- /// Tries to find a <see cref="TransitionModifierGroup"/> registered with the <see cref="IHasKey.Key"/>.
- /// </summary>
- public bool TryGetTransition(IHasKey hasKey, out TransitionModifierGroup transition)
- => TryGetTransition(hasKey.Key, out transition);
- /// <summary>[Pro-Only]
- /// Tries to find a <see cref="TransitionModifierGroup"/>
- /// via its <see cref="TransitionModifierGroup.Index"/>.
- /// </summary>
- public bool TryGetTransition(int index, out TransitionModifierGroup transition)
- => TransitionModifiers.TryGet(index, out transition)
- && transition != null;
- /************************************************************************************************************************/
- /// <summary>[Pro-Only]
- /// Finds the <see cref="TransitionModifierGroup.Index"/> of the group registered with the `key`
- /// or returns <c>-1</c>.
- /// </summary>
- public int IndexOf(object key)
- => TryGetTransition(key, out var group)
- ? group.Index
- : -1;
- /// <summary>[Pro-Only]
- /// Finds the <see cref="TransitionModifierGroup.Index"/> of the group registered with the `key`
- /// or returns <c>-1</c>.
- /// </summary>
- public int IndexOf(IHasKey hasKey)
- => IndexOf(hasKey.Key);
- /************************************************************************************************************************/
- /// <summary>[Pro-Only]
- /// Returns the fade duration to use when transitioning from `from` to the `transition`.
- /// </summary>
- public float GetFadeDuration(
- object from,
- ITransition to)
- {
- if (from != null &&
- TryGetTransition(to.Key, out var group))
- return group.GetFadeDuration(from);
- return to.FadeDuration;
- }
- /// <summary>[Pro-Only]
- /// Returns the fade duration to use when transitioning from `from` to the `transition`.
- /// </summary>
- public float GetFadeDuration(
- IHasKey from,
- ITransition to)
- => GetFadeDuration(from?.Key, to);
- /// <summary>[Pro-Only]
- /// Returns the fade duration to use when transitioning from the
- /// <see cref="AnimancerLayer.CurrentState"/> to the `transition`.
- /// </summary>
- public float GetFadeDuration(
- AnimancerLayer layer,
- ITransition transition)
- => GetFadeDuration(layer.CurrentState?.Key, transition);
- /// <summary>[Pro-Only]
- /// Returns the fade duration to use when transitioning from the
- /// <see cref="AnimancerLayer.CurrentState"/> to the `key`.
- /// </summary>
- public float GetFadeDuration(
- AnimancerLayer layer,
- object key,
- float fadeDuration)
- {
- AssertStringReference(key);
- var from = layer.CurrentState?.Key;
- if (from != null &&
- TryGetTransition(key, out var group))
- return group.GetFadeDuration(from);
- return fadeDuration;
- }
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] Gathers all the animations in this library.</summary>
- public void GetAnimationClips(List<AnimationClip> results)
- {
- for (int i = TransitionModifiers.Count - 1; i >= 0; i--)
- results.GatherFromSource(TransitionModifiers[i].Transition);
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Add
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] Adds the contents of the `definition` to this library.</summary>
- /// <remarks>Existing values will be completely replaced.</remarks>
- public void Initialize(TransitionLibraryDefinition definition)
- {
- Clear();
- if (definition == null)
- return;
- var count = definition.Transitions.Length;
- if (TransitionModifiers.Capacity < count)
- {
- var capacity = Math.Max(count, 16);
- TransitionModifiers.Capacity = capacity;
- KeyedTransitionModifiers.EnsureCapacity(capacity);
- }
- for (int i = 0; i < count; i++)
- {
- var transition = definition.Transitions[i];
- if (transition != null)
- SetTransition(transition);
- }
- for (int i = 0; i < definition.Modifiers.Length; i++)
- SetFadeDuration(definition.Modifiers[i]);
- if (definition.AliasAllTransitions)
- {
- for (int i = 0; i < count; i++)
- {
- var transition = definition.Transitions[i];
- var modifier = TransitionModifiers[i];
- KeyedTransitionModifiers[StringReference.Get(transition.name)] = modifier;
- }
- }
- for (int i = 0; i < definition.Aliases.Length; i++)
- {
- var alias = definition.Aliases[i];
- if (alias.Name != null &&
- TransitionModifiers.TryGet(alias.Index, out var group))
- KeyedTransitionModifiers[alias.Name.Name] = group;
- }
- }
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] Adds the `transition` to this library.</summary>
- /// <exception cref="ArgumentException">A transition is already registered with the `key`.</exception>
- public TransitionModifierGroup AddTransition(
- object key,
- ITransition transition)
- {
- AssertStringReference(key);
- var modifier = new TransitionModifierGroup(TransitionModifiers.Count, transition);
- KeyedTransitionModifiers.Add(key, modifier);
- TransitionModifiers.Add(modifier);
- return modifier;
- }
- /// <summary>[Pro-Only] Adds the `transition` to this library.</summary>
- /// <exception cref="ArgumentException">A transition is already registered with the `key`.</exception>
- public TransitionModifierGroup AddTransition(
- IHasKey hasKey,
- ITransition transition)
- => AddTransition(hasKey.Key, transition);
- /// <summary>[Pro-Only] Adds the `transition` to this library.</summary>
- /// <exception cref="ArgumentException">A transition is already registered with the `key`.</exception>
- public TransitionModifierGroup AddTransition(
- ITransition transition)
- => AddTransition(transition, transition);
- /************************************************************************************************************************/
- /// <summary>[Pro-Only]
- /// Adds the `transition` to this library or replaces the existing one registered with the `key`.
- /// </summary>
- public TransitionModifierGroup SetTransition(
- object key,
- ITransition transition)
- {
- if (TryGetTransition(key, out var oldModifier))
- {
- oldModifier.Transition = transition;
- return oldModifier;
- }
- return AddTransition(key, transition);
- }
- /// <summary>[Pro-Only]
- /// Adds the `transition` to this library or replaces the existing one registered with the `key`.
- /// </summary>
- public TransitionModifierGroup SetTransition(
- IHasKey hasKey,
- ITransition transition)
- => SetTransition(hasKey.Key, transition);
- /// <summary>[Pro-Only]
- /// Adds the `transition` to this library or replaces the existing one registered with the `key`.
- /// </summary>
- public TransitionModifierGroup SetTransition(
- ITransition transition)
- => SetTransition(transition, transition);
- /************************************************************************************************************************/
- /// <summary>[Pro-Only]
- /// Sets the <see cref="ITransition.FadeDuration"/> to use when transitioning from `from` to `to`.
- /// </summary>
- public void SetFadeDuration(
- object from,
- ITransition to,
- float fadeDuration)
- {
- var group = SetTransition(to.Key, to);
- group.SetFadeDuration(
- from,
- fadeDuration);
- }
- /// <summary>[Pro-Only]
- /// Sets the <see cref="ITransition.FadeDuration"/> to use when transitioning from `from` to `to`.
- /// </summary>
- public void SetFadeDuration(
- IHasKey from,
- ITransition to,
- float fadeDuration)
- => SetFadeDuration(from.Key, to, fadeDuration);
- /// <summary>[Pro-Only]
- /// Sets the <see cref="ITransition.FadeDuration"/> to use when transitioning from
- /// <see cref="TransitionModifierDefinition.FromIndex"/> to <see cref="TransitionModifierDefinition.ToIndex"/>.
- /// </summary>
- public bool SetFadeDuration(
- TransitionModifierDefinition modifier)
- {
- if (!TransitionModifiers.TryGet(modifier.FromIndex, out var from) ||
- !TransitionModifiers.TryGet(modifier.ToIndex, out var to))
- return false;
- to.SetFadeDuration(
- from.Transition.Key,
- modifier.FadeDuration);
- return true;
- }
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] Registers the `group` with another `key`.</summary>
- public void AddAlias(
- object key,
- TransitionModifierGroup group)
- {
- AssertStringReference(key);
- AssertGroup(group);
- KeyedTransitionModifiers.Add(key, group);
- }
- /// <summary>[Pro-Only] Registers the `transition` with the `key`.</summary>
- /// <remarks>Also registers it with its <see cref="IHasKey.Key"/> if it wasn't already.</remarks>
- public TransitionModifierGroup AddAlias(
- object key,
- ITransition transition)
- {
- var group = SetTransition(transition);
- AddAlias(key, group);
- return group;
- }
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] Adds the contents of `copyFrom` into this library.</summary>
- /// <remarks>
- /// This method adds and replaces values, but does not remove any
- /// (unlike <see cref="CopyFrom(TransitionLibrary, CloneContext)"/>.
- /// </remarks>
- public void AddLibrary(TransitionLibrary library, CloneContext context)
- {
- if (library == null)
- return;
- for (int i = 0; i < TransitionModifiers.Count; i++)
- {
- var group = TransitionModifiers[i];
- context[group.Transition] = group;
- }
- foreach (var group in library.KeyedTransitionModifiers)
- {
- var transition = group.Value.Transition;
- if (context.TryGetClone(transition, out var clone) &&
- clone is TransitionModifierGroup cloneGroup)
- {
- AssertGroup(cloneGroup);
- KeyedTransitionModifiers[group.Key] = cloneGroup;
- }
- else
- {
- cloneGroup = SetTransition(group.Key, group.Value.Transition);
- cloneGroup.CopyFrom(group.Value);
- context[transition] = cloneGroup;
- }
- }
- }
- /// <summary>[Pro-Only] Adds the contents of `copyFrom` into this library.</summary>
- /// <remarks>
- /// This method adds and replaces values, but does not remove any
- /// (unlike <see cref="CopyFrom(TransitionLibrary, CloneContext)"/>.
- /// </remarks>
- public void AddLibrary(TransitionLibrary library)
- {
- var context = CloneContext.Pool.Instance.Acquire();
- AddLibrary(library, context);
- CloneContext.Pool.Instance.Release(context);
- }
- /************************************************************************************************************************/
- /// <inheritdoc/>
- /// <remarks>See also <see cref="AddLibrary(TransitionLibrary, CloneContext)"/>.</remarks>
- public void CopyFrom(TransitionLibrary copyFrom, CloneContext context)
- {
- Clear();
- if (copyFrom == null)
- return;
- var count = copyFrom.TransitionModifiers.Count;
- for (int i = 0; i < count; i++)
- TransitionModifiers.Add(copyFrom.TransitionModifiers[i].Clone(context));
- foreach (var group in copyFrom.KeyedTransitionModifiers)
- {
- var clone = TransitionModifiers[group.Value.Index];
- KeyedTransitionModifiers.Add(group.Key, clone);
- }
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Remove
- /************************************************************************************************************************/
- // Remove from the dictionary but not the list because there might be multiple aliases for that index.
- /// <summary>[Pro-Only] Removes the transition registered with the `key`.</summary>
- public bool RemoveTransition(object key)
- => KeyedTransitionModifiers.Remove(key);
- /// <summary>[Pro-Only] Removes the transition registered with the <see cref="IHasKey.Key"/>.</summary>
- public bool RemoveTransition(IHasKey hasKey)
- => RemoveTransition(hasKey.Key);
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] Removes a modified fade duration for transitioning from `from` to `to`.</summary>
- public bool RemoveFadeDuration(object from, object to)
- => TryGetTransition(to, out var group)
- && group.FromKeyToFadeDuration != null
- && group.FromKeyToFadeDuration.Remove(from);
- /// <summary>[Pro-Only] Removes a modified fade duration for transitioning from `from` to `to`.</summary>
- public bool RemoveFadeDuration(IHasKey from, IHasKey to)
- => RemoveFadeDuration(from.Key, to.Key);
- /************************************************************************************************************************/
- /// <summary>[Pro-Only] Removes everything from this library, leaving it empty.</summary>
- public void Clear()
- {
- TransitionModifiers.Clear();
- KeyedTransitionModifiers.Clear();
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Play
- /************************************************************************************************************************/
- /// <summary>
- /// Calls <see cref="AnimancerLayer.Play(ITransition, float, FadeMode)"/>
- /// with the fade duration potentially modified by this library.
- /// </summary>
- public AnimancerState Play(
- AnimancerLayer layer,
- ITransition transition)
- => layer.Play(
- transition,
- GetFadeDuration(layer, transition),
- transition.FadeMode);
- /// <summary>
- /// Calls <see cref="AnimancerLayer.Play(ITransition, float, FadeMode)"/>
- /// with the fade duration potentially modified by this library.
- /// </summary>
- public AnimancerState Play(
- AnimancerLayer layer,
- TransitionModifierGroup transition)
- {
- var from = layer.CurrentState?.Key;
- var to = transition.Transition;
- var fadeDuration = from != null
- ? transition.GetFadeDuration(from)
- : to.FadeDuration;
- return layer.Play(
- to,
- fadeDuration,
- to.FadeMode);
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Plays the transition registered with the specified `key` if there is one.
- /// Otherwise, returns <c>null</c>.
- /// </summary>
- public AnimancerState TryPlay(
- AnimancerLayer layer,
- object key)
- => TryGetTransition(key, out var transition)
- ? Play(layer, transition)
- : null;
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Assertions
- /************************************************************************************************************************/
- /// <summary>[Assert-Conditional]
- /// Logs <see cref="OptionalWarning.StringReference"/> if the `key` is a <see cref="string"/>.
- /// </summary>
- [System.Diagnostics.Conditional(Strings.Assertions)]
- private void AssertStringReference(object key)
- {
- #if UNITY_ASSERTIONS
- if (key is string keyString)
- {
- if (StringReference.TryGet(keyString, out var keyReference) &&
- KeyedTransitionModifiers.ContainsKey(keyReference))
- Debug.LogError(
- $"{nameof(TransitionLibrary)} key type mismatch:" +
- $" attempted to use string '{keyString}'," +
- $" but that value is registered as a {nameof(StringReference)}." +
- $" Use a {nameof(StringReference)} to ensure the correct lookup.");
- else
- OptionalWarning.StringReference.Log(
- $"A string '{keyString}' is being used as a key in a {nameof(TransitionLibrary)}." +
- $" {nameof(StringReference)}s should be used instead of strings because they are more efficient" +
- $" and to avoid mismatches with aliases in a {nameof(TransitionLibraryDefinition)}.");
- }
- #endif
- }
- /************************************************************************************************************************/
- /// <summary>[Assert-Conditional]
- /// Asserts that the <see cref="TransitionModifierGroup.Index"/>
- /// corresponds to the <see cref="TransitionModifiers"/>.
- /// </summary>
- [System.Diagnostics.Conditional(Strings.Assertions)]
- [HideInCallstack]
- internal void AssertGroup(TransitionModifierGroup group)
- {
- #if UNITY_ASSERTIONS
- if (!TransitionModifiers.TryGet(group.Index, out var registered) ||
- registered != group)
- Debug.LogError(
- $"{nameof(CloneContext)} contains an {nameof(TransitionModifierGroup)}" +
- $" which isn't part of this {nameof(TransitionLibrary)}." +
- $" It must have been added to the context manually.");
- #endif
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- }
- }
|