This commit is contained in:
2025-08-18 09:22:24 +08:00
commit cef5623ab0
1333 changed files with 305844 additions and 0 deletions
@@ -0,0 +1,117 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
using System;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace Animancer.TransitionLibraries
{
/// <summary>[<see cref="SerializableAttribute"/>] A <see cref="StringAsset"/> and <see cref="int"/> pair.</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/NamedIndex
[Serializable]
public struct NamedIndex :
IComparable<NamedIndex>,
IEquatable<NamedIndex>
{
/************************************************************************************************************************/
[SerializeField]
private StringAsset _Name;
/// <summary>The name.</summary>
public readonly StringAsset Name
=> _Name;
/************************************************************************************************************************/
[SerializeField]
private int _Index;
/// <summary>The index.</summary>
public readonly int Index
=> _Index;
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="NamedIndex"/>.</summary>
public NamedIndex(StringAsset name, int index)
{
_Name = name;
_Index = index;
}
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="NamedIndex"/>.</summary>
public readonly NamedIndex With(StringAsset name)
=> new(name, _Index);
/// <summary>Creates a new <see cref="NamedIndex"/>.</summary>
public readonly NamedIndex With(int index)
=> new(_Name, index);
/************************************************************************************************************************/
/// <summary>Describes this value.</summary>
public override readonly string ToString()
=> $"[{_Index}]{_Name}";
/************************************************************************************************************************/
#region Equality
/************************************************************************************************************************/
/// <summary>Compares the <see cref="Index"/> then <see cref="Name"/>.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly int CompareTo(NamedIndex other)
{
var result = _Index.CompareTo(other._Index);
if (result != 0)
return result;
else
return StringAsset.Compare(_Name, other._Name);
}
/************************************************************************************************************************/
/// <summary>Are all fields in this object equal to the equivalent in `obj`?</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override readonly bool Equals(object obj)
=> obj is NamedIndex value
&& Equals(value);
/// <summary>Are all fields in this object equal to the equivalent fields in `other`?</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(NamedIndex other)
=> _Index == other._Index
&& _Name == other._Name;
/// <summary>Are all fields in `a` equal to the equivalent fields in `b`?</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(NamedIndex a, NamedIndex b)
=> a.Equals(b);
/// <summary>Are any fields in `a` not equal to the equivalent fields in `b`?</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(NamedIndex a, NamedIndex b)
=> !(a == b);
/************************************************************************************************************************/
/// <summary>Returns a hash code based on the values of this object's fields.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override readonly int GetHashCode()
=> AnimancerUtilities.Hash(-871379578,
_Index.SafeGetHashCode(),
_Name.SafeGetHashCode());
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b95ff193221b8141b0f3c98600a8f55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,571 @@
// 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
/************************************************************************************************************************/
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab6185b55c780f744ada43f1408d8482
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,99 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
using System.Collections.Generic;
using UnityEngine;
namespace Animancer.TransitionLibraries
{
/// <summary>[Pro-Only]
/// A <see cref="ScriptableObject"/> which serializes a <see cref="TransitionLibraryDefinition"/>
/// and creates a <see cref="TransitionLibrary"/> from it at runtime.
/// </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/TransitionLibraryAsset
[CreateAssetMenu(
menuName = Strings.MenuPrefix + "Transition Library",
order = Strings.AssetMenuOrder + 0)]
[AnimancerHelpUrl(typeof(TransitionLibraryAsset))]
public class TransitionLibraryAsset : ScriptableObject,
IAnimationClipSource
{
/************************************************************************************************************************/
[SerializeField]
private TransitionLibraryDefinition _Definition;
/// <summary>[<see cref="SerializeField"/>]
/// The serialized data which will be used to initialize the <see cref="Library"/> at runtime.
/// </summary>
/// <remarks>
/// If you modify the contents of this reference, either re-assign this property
/// or call <see cref="OnDefinitionModified"/> to apply any changes to the <see cref="Library"/>.
/// </remarks>
public TransitionLibraryDefinition Definition
{
get => _Definition;
set
{
_Definition = value ?? new();
OnDefinitionModified();
}
}
#if UNITY_EDITOR
/// <summary>[Editor-Only] [Internal]
/// The name of the field which stores the <see cref="Definition"/>.
/// </summary>
internal const string DefinitionField = nameof(_Definition);
#endif
/************************************************************************************************************************/
/// <summary>The runtime <see cref="TransitionLibrary"/> created from the <see cref="Definition"/>.</summary>
public TransitionLibrary Library { get; private set; }
/************************************************************************************************************************/
/// <summary>Initializes the <see cref="Library"/>.</summary>
protected virtual void OnEnable()
{
_Definition ??= new();
Library = new();
Library.Initialize(_Definition);
}
/************************************************************************************************************************/
/// <summary>
/// Adds the contents of the <see cref="Definition"/>
/// to the <see cref="Library"/> if it was already initialized.
/// </summary>
/// <remarks>
/// Call this after modifying the contents of the <see cref="Definition"/>
/// to ensure that the <see cref="Library"/> reflects any changes.
/// <para></para>
/// Note that this doesn't remove anything from the <see cref="Library"/>,
/// it only adds or replaces values.
/// </remarks>
public void OnDefinitionModified()
{
Library.Initialize(_Definition);
}
/************************************************************************************************************************/
/// <summary>Gathers all the animations in the <see cref="Definition"/> and <see cref="Library"/>.</summary>
public void GetAnimationClips(List<AnimationClip> results)
{
results.GatherFromSource(_Definition);
results.GatherFromSource(Library);
}
/************************************************************************************************************************/
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6f08fdd50e79d0c4bb235a5c2696516d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,401 @@
// 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
/************************************************************************************************************************/
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 42db79bf33227a542840e18860864e0a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,116 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
using System;
using UnityEngine;
namespace Animancer.TransitionLibraries
{
/// <summary>[<see cref="SerializableAttribute"/>]
/// Details about how to modify a transition when it comes from a specific source.
/// </summary>
/// <remarks>
/// Multiple of these can be used to build a <see cref="TransitionModifierGroup"/> at runtime.
/// <para></para>
/// <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/TransitionModifierDefinition
[Serializable]
public struct TransitionModifierDefinition :
IEquatable<TransitionModifierDefinition>
{
/************************************************************************************************************************/
[SerializeField]
private int _From;
/// <summary>The index of the source transition in the <see cref="TransitionLibraryDefinition"/>.</summary>
public readonly int FromIndex
=> _From;
/************************************************************************************************************************/
[SerializeField]
private int _To;
/// <summary>The index of the destination transition in the <see cref="TransitionLibraryDefinition"/>.</summary>
public readonly int ToIndex
=> _To;
/************************************************************************************************************************/
[SerializeField]
private float _Fade;
/// <summary>The fade duration for this override to use.</summary>
public readonly float FadeDuration
=> _Fade;
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="TransitionModifierDefinition"/>.</summary>
public TransitionModifierDefinition(
int fromIndex,
int toIndex,
float fadeDuration)
{
_From = fromIndex;
_To = toIndex;
_Fade = fadeDuration;
}
/************************************************************************************************************************/
/// <summary>Creates a copy of this override with the specified <see cref="FadeDuration"/>.</summary>
public readonly TransitionModifierDefinition WithFadeDuration(float fadeDuration)
=> new(_From, _To, fadeDuration);
/// <summary>Creates a copy of this override with the specified <see cref="FromIndex"/> and <see cref="ToIndex"/>.</summary>
public readonly TransitionModifierDefinition WithIndices(int fromIndex, int toIndex)
=> new(fromIndex, toIndex, _Fade);
/************************************************************************************************************************/
/// <summary>Creates a new string describing this override.</summary>
public override readonly string ToString()
=> $"{nameof(TransitionModifierDefinition)}({_From}->{_To}={_Fade})";
/************************************************************************************************************************/
#region Equality
/************************************************************************************************************************/
/// <summary>Are all fields in this object equal to the equivalent in `obj`?</summary>
public override readonly bool Equals(object obj)
=> obj is TransitionModifierDefinition value
&& Equals(value);
/// <summary>Are all fields in this object equal to the equivalent fields in `other`?</summary>
public readonly bool Equals(TransitionModifierDefinition other)
=> _From == other._From
&& _To == other._To
&& _Fade.IsEqualOrBothNaN(other._Fade);
/// <summary>Are all fields in `a` equal to the equivalent fields in `b`?</summary>
public static bool operator ==(TransitionModifierDefinition a, TransitionModifierDefinition b)
=> a.Equals(b);
/// <summary>Are any fields in `a` not equal to the equivalent fields in `b`?</summary>
public static bool operator !=(TransitionModifierDefinition a, TransitionModifierDefinition b)
=> !(a == b);
/************************************************************************************************************************/
/// <summary>Returns a hash code based on the values of this object's fields.</summary>
public override readonly int GetHashCode()
=> AnimancerUtilities.Hash(-871379578,
_From.SafeGetHashCode(),
_To.SafeGetHashCode(),
_Fade.SafeGetHashCode());
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88850ffc4123d1e468ff9c086823689c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,124 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
using System.Collections.Generic;
namespace Animancer.TransitionLibraries
{
/// <summary>
/// An <see cref="ITransition"/> and a dictionary to modify it based on the previous state.
/// </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/TransitionModifierGroup
public class TransitionModifierGroup :
ICloneable<TransitionModifierGroup>,
ICopyable<TransitionModifierGroup>
{
/************************************************************************************************************************/
/// <summary>The index at which this group was added to its <see cref="TransitionLibrary"/>.</summary>
public readonly int Index;
/************************************************************************************************************************/
private ITransition _Transition;
/// <summary>The target transition of this group.</summary>
/// <remarks>Can't be <c>null</c>.</remarks>
public ITransition Transition
{
get => _Transition;
set
{
AnimancerUtilities.Assert(
value != null,
$"{nameof(TransitionModifierGroup)}.{nameof(Transition)} can't be null.");
_Transition = value;
}
}
/************************************************************************************************************************/
/// <summary>
/// Custom <see cref="ITransition.FadeDuration"/>s to use when playing the <see cref="Transition"/>
/// depending on the <see cref="IHasKey.Key"/> of the source state it is coming from.
/// </summary>
/// <remarks>This is <c>null</c> by default until <see cref="SetFadeDuration"/> adds something.</remarks>
public Dictionary<object, float> FromKeyToFadeDuration { get; set; }
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="TransitionModifierGroup"/>.</summary>
public TransitionModifierGroup(
int index,
ITransition transition)
{
Index = index;
Transition = transition;
}
/************************************************************************************************************************/
/// <summary>Sets the `fadeDuration` to use when transitioning from `from` to the <see cref="Transition"/>.</summary>
public void SetFadeDuration(object from, float fadeDuration)
{
FromKeyToFadeDuration ??= new();
FromKeyToFadeDuration[from] = fadeDuration;
}
/// <summary>Removes the fade duration modifier set for transitioning from `from` to the <see cref="Transition"/>.</summary>
public void ResetFadeDuration(object from)
=> FromKeyToFadeDuration?.Remove(from);
/************************************************************************************************************************/
/// <summary>Returns the fade duration to use when transitioning from `from` to the <see cref="Transition"/>.</summary>
public float GetFadeDuration(object from)
=> FromKeyToFadeDuration != null
&& FromKeyToFadeDuration.TryGetValue(AnimancerUtilities.GetRootKey(from), out var fadeDuration)
? fadeDuration
: Transition.FadeDuration;
/************************************************************************************************************************/
/// <inheritdoc/>
public TransitionModifierGroup Clone(CloneContext context)
{
var clone = new TransitionModifierGroup(Index, null);
clone.CopyFrom(this);
return clone;
}
/************************************************************************************************************************/
/// <inheritdoc/>
public void CopyFrom(TransitionModifierGroup copyFrom, CloneContext context)
{
Transition = copyFrom.Transition;
if (copyFrom.FromKeyToFadeDuration == null)
{
FromKeyToFadeDuration?.Clear();
}
else
{
FromKeyToFadeDuration ??= new();
foreach (var item in copyFrom.FromKeyToFadeDuration)
FromKeyToFadeDuration[item.Key] = item.Value;
}
}
/************************************************************************************************************************/
/// <summary>Describes this object.</summary>
public override string ToString()
=> $"{nameof(TransitionModifierGroup)}([{Index}] {AnimancerUtilities.ToStringOrNull(Transition)})";
/************************************************************************************************************************/
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 637963091ecbba44baffa2316cf8cd32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: