chore: initial commit
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 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,50 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik //
|
||||
|
||||
namespace Animancer.TransitionLibraries
|
||||
{
|
||||
/// <summary>Values which determine how a transition is played.</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/TransitionDetails
|
||||
public struct TransitionDetails
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="TransitionDetails"/> with all values as <see cref="float.NaN"/>.</summary>
|
||||
public static TransitionDetails NaN
|
||||
=> new(float.NaN, float.NaN);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary><see cref="ITransition.FadeDuration"/></summary>
|
||||
public float FadeDuration;
|
||||
|
||||
/// <summary><see cref="ITransition.NormalizedStartTime"/></summary>
|
||||
public float NormalizedStartTime;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="TransitionDetails"/>.</summary>
|
||||
public TransitionDetails(
|
||||
float fadeDuration,
|
||||
float normalizedStartTime)
|
||||
{
|
||||
FadeDuration = fadeDuration;
|
||||
NormalizedStartTime = normalizedStartTime;
|
||||
}
|
||||
|
||||
/// <summary>Creates a new <see cref="TransitionDetails"/>.</summary>
|
||||
public TransitionDetails(
|
||||
ITransition transition)
|
||||
{
|
||||
FadeDuration = transition.FadeDuration;
|
||||
NormalizedStartTime = transition.NormalizedStartTime;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f515e60d52111f4789b11bfc4d1816a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,640 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 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 :
|
||||
IAnimationClipCollection,
|
||||
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 (KeyedTransitionModifiers.TryGetValue(key, out transition))
|
||||
return true;
|
||||
|
||||
if (key is AnimancerState state)
|
||||
{
|
||||
key = AnimancerUtilities.GetRootKey(state.Key);
|
||||
|
||||
return
|
||||
key != null &&
|
||||
KeyedTransitionModifiers.TryGetValue(key, out transition);
|
||||
}
|
||||
|
||||
AssertStringReference(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <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 virtual void GatherAnimationClips(ICollection<AnimationClip> clips)
|
||||
{
|
||||
for (int i = TransitionModifiers.Count - 1; i >= 0; i--)
|
||||
clips.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++)
|
||||
SetModifier(definition.Modifiers[i]);
|
||||
|
||||
if (definition.AliasAllTransitions)
|
||||
{
|
||||
var modifierIndex = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var transition = definition.Transitions[i];
|
||||
if (transition == null)
|
||||
continue;
|
||||
|
||||
var modifier = TransitionModifiers[modifierIndex++];
|
||||
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.NormalizedStartTime"/>
|
||||
/// to use when transitioning from `from` to `to`.
|
||||
/// </summary>
|
||||
public void SetNormalizedStartTime(
|
||||
object from,
|
||||
ITransition to,
|
||||
float normalizedStartTime)
|
||||
{
|
||||
var group = SetTransition(to.Key, to);
|
||||
group.SetNormalizedStartTime(from, normalizedStartTime);
|
||||
}
|
||||
|
||||
/// <summary>[Pro-Only]
|
||||
/// Sets the <see cref="ITransition.NormalizedStartTime"/>
|
||||
/// to use when transitioning from `from` to `to`.
|
||||
/// </summary>
|
||||
public void SetNormalizedStartTime(
|
||||
IHasKey from,
|
||||
ITransition to,
|
||||
float normalizedStartTime)
|
||||
=> SetNormalizedStartTime(from.Key, to, normalizedStartTime);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Pro-Only]
|
||||
/// Sets the <see cref="ITransition.FadeDuration"/> and <see cref="ITransition.NormalizedStartTime"/>
|
||||
/// to use when transitioning from `from` to `to`.
|
||||
/// </summary>
|
||||
public void SetModifier(
|
||||
object from,
|
||||
ITransition to,
|
||||
TransitionDetails modifier)
|
||||
{
|
||||
var group = SetTransition(to.Key, to);
|
||||
group.SetModifier(from, modifier);
|
||||
}
|
||||
|
||||
/// <summary>[Pro-Only]
|
||||
/// Sets the <see cref="ITransition.FadeDuration"/> and <see cref="ITransition.NormalizedStartTime"/>
|
||||
/// to use when transitioning from `from` to `to`.
|
||||
/// </summary>
|
||||
public void SetModifier(
|
||||
IHasKey from,
|
||||
ITransition to,
|
||||
TransitionDetails modifier)
|
||||
=> SetModifier(from.Key, to, modifier);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Pro-Only]
|
||||
/// Sets the <see cref="ITransition.FadeDuration"/> and <see cref="ITransition.NormalizedStartTime"/>
|
||||
/// to use when transitioning from <see cref="TransitionModifierDefinition.FromIndex"/>
|
||||
/// to <see cref="TransitionModifierDefinition.ToIndex"/>.
|
||||
/// </summary>
|
||||
public bool SetModifier(
|
||||
TransitionModifierDefinition modifier)
|
||||
{
|
||||
if (!TransitionModifiers.TryGet(modifier.FromIndex, out var from) ||
|
||||
!TransitionModifiers.TryGet(modifier.ToIndex, out var to))
|
||||
return false;
|
||||
|
||||
to.SetModifier(
|
||||
from.Transition.Key,
|
||||
modifier.ToTransitionDetails());
|
||||
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.FromKeyToModifier != null
|
||||
&& group.FromKeyToModifier.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)
|
||||
=> TryGetTransition(transition, out var modifier)
|
||||
? Play(layer, modifier)
|
||||
: layer.Play(transition);
|
||||
|
||||
/// <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 details = transition.GetDetails(from);
|
||||
|
||||
if (float.IsNaN(details.FadeDuration))
|
||||
details.FadeDuration = to.FadeDuration;
|
||||
|
||||
if (float.IsNaN(details.NormalizedStartTime))
|
||||
return layer.Play(
|
||||
to,
|
||||
details.FadeDuration,
|
||||
to.FadeMode);
|
||||
|
||||
var state = layer.Play(
|
||||
to,
|
||||
details.FadeDuration,
|
||||
FadeMode.FromStart);
|
||||
state.NormalizedTime = details.NormalizedStartTime;
|
||||
return state;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <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,97 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 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,
|
||||
IAnimationClipCollection
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[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 virtual void GatherAnimationClips(ICollection<AnimationClip> clips)
|
||||
{
|
||||
clips.GatherFromSource(_Definition);
|
||||
clips.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,407 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 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 :
|
||||
IAnimationClipCollection,
|
||||
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 details
|
||||
/// 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;
|
||||
}
|
||||
|
||||
float fadeDuration, normalizedStartTime;
|
||||
if (TryGetTransition(toIndex, out var transition))
|
||||
{
|
||||
fadeDuration = transition.TryGetFadeDuration();
|
||||
normalizedStartTime = transition.TryGetNormalizedStartTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
fadeDuration = float.NaN;
|
||||
normalizedStartTime = float.NaN;
|
||||
}
|
||||
|
||||
modifier = new(fromIndex, toIndex, fadeDuration, normalizedStartTime);
|
||||
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, replaces, or removes an item in the <see cref="Modifiers"/>.</summary>
|
||||
public void SetModifier(
|
||||
TransitionModifierDefinition modifier)
|
||||
{
|
||||
if (!modifier.Validate())
|
||||
{
|
||||
RemoveModifier(modifier);
|
||||
return;
|
||||
}
|
||||
|
||||
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, static (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 virtual void GatherAnimationClips(ICollection<AnimationClip> clips)
|
||||
=> clips.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,162 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 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 modifier to use instead of the transition's default value.</summary>
|
||||
public readonly float FadeDuration
|
||||
=> _Fade;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField]
|
||||
private float _NormalizedStartTime;
|
||||
|
||||
/// <summary>The normalized start time for this modifier to use instead of the transition's default value.</summary>
|
||||
public readonly float NormalizedStartTime
|
||||
=> _NormalizedStartTime;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="TransitionModifierDefinition"/>.</summary>
|
||||
public TransitionModifierDefinition(
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
float fadeDuration,
|
||||
float normalizedStartTime)
|
||||
{
|
||||
_From = fromIndex;
|
||||
_To = toIndex;
|
||||
_Fade = fadeDuration;
|
||||
_NormalizedStartTime = normalizedStartTime;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Does this modifier contain valid values?</summary>
|
||||
public bool Validate()
|
||||
{
|
||||
var noFade = float.IsNaN(_Fade);
|
||||
var noStart = float.IsNaN(_NormalizedStartTime);
|
||||
if (noFade && noStart)
|
||||
return false;
|
||||
|
||||
if (!noFade)
|
||||
{
|
||||
if (_Fade < 0)
|
||||
_Fade = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a copy of this modifier with the specified <see cref="FadeDuration"/>.</summary>
|
||||
public readonly TransitionModifierDefinition WithFadeDuration(float fadeDuration)
|
||||
=> new(_From, _To, fadeDuration, _NormalizedStartTime);
|
||||
|
||||
/// <summary>Creates a copy of this modifier with the specified <see cref="NormalizedStartTime"/>.</summary>
|
||||
public readonly TransitionModifierDefinition WithNormalizedStartTime(float normalizedStartTime)
|
||||
=> new(_From, _To, _Fade, normalizedStartTime);
|
||||
|
||||
/// <summary>Creates a copy of this modifier with the specified <see cref="FadeDuration"/> and <see cref="NormalizedStartTime"/>.</summary>
|
||||
public readonly TransitionModifierDefinition WithDetails(float fadeDuration, float normalizedStartTime)
|
||||
=> new(_From, _To, fadeDuration, normalizedStartTime);
|
||||
|
||||
/// <summary>Creates a copy of this modifier with the specified <see cref="FromIndex"/> and <see cref="ToIndex"/>.</summary>
|
||||
public readonly TransitionModifierDefinition WithIndices(int fromIndex, int toIndex)
|
||||
=> new(fromIndex, toIndex, _Fade, _NormalizedStartTime);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="TransitionDetails"/> from this modifier.</summary>
|
||||
public readonly TransitionDetails ToTransitionDetails()
|
||||
=> new(_Fade, _NormalizedStartTime);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new string describing this modifier.</summary>
|
||||
public override readonly string ToString()
|
||||
=> $"{nameof(TransitionModifierDefinition)}({_From}->{_To}, F={_Fade}, S={_NormalizedStartTime})";
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#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)
|
||||
&& _NormalizedStartTime.IsEqualOrBothNaN(other._NormalizedStartTime);
|
||||
|
||||
/// <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.GetHashCode(),
|
||||
_To.GetHashCode(),
|
||||
_Fade.GetHashCode(),
|
||||
_NormalizedStartTime.GetHashCode());
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#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,181 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 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 modifiers to use when playing a <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="SetModifier"/> adds something.</remarks>
|
||||
public Dictionary<object, TransitionDetails> FromKeyToModifier;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="TransitionModifierGroup"/>.</summary>
|
||||
public TransitionModifierGroup(
|
||||
int index,
|
||||
ITransition transition)
|
||||
{
|
||||
Index = index;
|
||||
Transition = transition;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Sets the `modifier` to use when transitioning from `from` to the <see cref="Transition"/>.</summary>
|
||||
public void SetModifier(object from, TransitionDetails modifier)
|
||||
{
|
||||
FromKeyToModifier ??= new();
|
||||
FromKeyToModifier[from] = modifier;
|
||||
}
|
||||
|
||||
/// <summary>Removes the fade duration modifier set for transitioning from `from` to the <see cref="Transition"/>.</summary>
|
||||
public void ResetModifier(object from)
|
||||
=> FromKeyToModifier?.Remove(from);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="TransitionDetails.FadeDuration"/>
|
||||
/// to use when transitioning from `from` to the <see cref="Transition"/>.
|
||||
/// </summary>
|
||||
public void SetFadeDuration(object from, float fadeDuration)
|
||||
{
|
||||
FromKeyToModifier ??= new();
|
||||
|
||||
if (!FromKeyToModifier.TryGetValue(from, out var modifier))
|
||||
modifier = TransitionDetails.NaN;
|
||||
|
||||
modifier.FadeDuration = fadeDuration;
|
||||
|
||||
FromKeyToModifier[from] = modifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="TransitionDetails.NormalizedStartTime"/>
|
||||
/// to use when transitioning from `from` to the <see cref="Transition"/>.
|
||||
/// </summary>
|
||||
public void SetNormalizedStartTime(object from, float normalizedStartTime)
|
||||
{
|
||||
FromKeyToModifier ??= new();
|
||||
|
||||
if (!FromKeyToModifier.TryGetValue(from, out var modifier))
|
||||
modifier = TransitionDetails.NaN;
|
||||
|
||||
modifier.NormalizedStartTime = normalizedStartTime;
|
||||
|
||||
FromKeyToModifier[from] = modifier;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Returns the fade duration to use when transitioning from `from` to the <see cref="Transition"/>.</summary>
|
||||
public TransitionDetails GetDetails(object from)
|
||||
{
|
||||
if (FromKeyToModifier != null && from != null)
|
||||
{
|
||||
from = AnimancerUtilities.GetRootKey(from);
|
||||
if (FromKeyToModifier.TryGetValue(from, out var details))
|
||||
{
|
||||
if (float.IsNaN(details.FadeDuration))
|
||||
details.FadeDuration = Transition.FadeDuration;
|
||||
|
||||
if (float.IsNaN(details.NormalizedStartTime))
|
||||
details.NormalizedStartTime = Transition.NormalizedStartTime;
|
||||
|
||||
return details;
|
||||
}
|
||||
}
|
||||
|
||||
return new(Transition);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Returns the fade duration to use when transitioning from `from` to the <see cref="Transition"/>.</summary>
|
||||
public float GetFadeDuration(object from)
|
||||
=> FromKeyToModifier != null
|
||||
&& FromKeyToModifier.TryGetValue(AnimancerUtilities.GetRootKey(from), out var modifier)
|
||||
? modifier.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.FromKeyToModifier == null)
|
||||
{
|
||||
FromKeyToModifier?.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
FromKeyToModifier ??= new();
|
||||
foreach (var item in copyFrom.FromKeyToModifier)
|
||||
FromKeyToModifier[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:
|
||||
Reference in New Issue
Block a user