123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- namespace Animancer
- {
- /// https://kybernetik.com.au/animancer/api/Animancer/ControllerState
- partial class ControllerState
- {
- /************************************************************************************************************************/
- private SerializableParameterBindings _SerializedParameterBindings;
- /// <summary>Serialized data used to create <see cref="ParameterBinding{T}"/>s at runtime.</summary>
- public SerializableParameterBindings SerializedParameterBindings
- {
- get => _SerializedParameterBindings;
- set
- {
- _SerializedParameterBindings = value;
- DeserializeParameterBindings();
- }
- }
- /// <summary>Deserializes the <see cref="SerializedParameterBindings"/>.</summary>
- private void DeserializeParameterBindings()
- {
- if (Graph == null)
- return;
- DisposeParameterBindings();
- _SerializedParameterBindings?.Deserialize(this);
- }
- /************************************************************************************************************************/
- private List<IDisposable> _ParameterBindings;
- /// <summary>
- /// Adds an object to a list for <see cref="IDisposable.Dispose"/>
- /// to be called in <see cref="Destroy"/>.
- /// </summary>
- private void AddParameterBinding(IDisposable disposable)
- {
- _ParameterBindings ??= new();
- _ParameterBindings.Add(disposable);
- }
- /// <summary>Disposes everything added by <see cref="AddParameterBinding"/>.</summary>
- private void DisposeParameterBindings()
- {
- if (_ParameterBindings == null)
- return;
- for (int i = _ParameterBindings.Count - 1; i >= 0; i--)
- _ParameterBindings[i].Dispose();
- _ParameterBindings.Clear();
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Configures all parameters in the <see cref="Controller"/>
- /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public void BindAllParameters()
- {
- var count = Playable.GetParameterCount();
- for (int i = 0; i < count; i++)
- {
- var parameter = Playable.GetParameter(i);
- BindParameter(parameter.name, parameter);
- }
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public void BindParameter(StringReference name)
- => BindParameter(name, name);
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public void BindParameter(StringReference animancerParameter, string controllerParameterName)
- => BindParameter(animancerParameter, Animator.StringToHash(controllerParameterName));
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public void BindParameter(StringReference animancerParameter, int controllerParameterHash)
- {
- var count = Playable.GetParameterCount();
- for (int i = 0; i < count; i++)
- {
- var parameter = Playable.GetParameter(i);
- if (parameter.nameHash == controllerParameterHash)
- {
- BindParameter(animancerParameter, parameter);
- break;
- }
- }
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Configures all parameters in the <see cref="Controller"/>
- /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public void BindParameter(
- StringReference animancerParameter,
- AnimatorControllerParameter controllerParameter)
- {
- switch (controllerParameter.type)
- {
- case AnimatorControllerParameterType.Float:
- BindFloat(animancerParameter, controllerParameter.nameHash);
- break;
- case AnimatorControllerParameterType.Int:
- BindInt(animancerParameter, controllerParameter.nameHash);
- break;
- case AnimatorControllerParameterType.Bool:
- case AnimatorControllerParameterType.Trigger:
- BindBool(animancerParameter, controllerParameter.nameHash);
- break;
- }
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<bool> BindBool(StringReference name)
- => BindBool(name, name);
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<bool> BindBool(StringReference animancerParameter, string controllerParameterName)
- => BindBool(animancerParameter, Animator.StringToHash(controllerParameterName));
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<bool> BindBool(StringReference animancerParameter, int controllerParameterHash)
- {
- var parameter = Graph.Parameters.GetOrCreate<bool>(animancerParameter);
- var binding = new ParameterBinding<bool>(
- parameter,
- value => Playable.SetBool(controllerParameterHash, value));
- AddParameterBinding(binding);
- return binding;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<float> BindFloat(StringReference name)
- => BindFloat(name, name);
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<float> BindFloat(StringReference animancerParameter, string controllerParameterName)
- => BindFloat(animancerParameter, Animator.StringToHash(controllerParameterName));
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<float> BindFloat(StringReference animancerParameter, int controllerParameterHash)
- {
- var parameter = Graph.Parameters.GetOrCreate<float>(animancerParameter);
- var binding = new ParameterBinding<float>(
- parameter,
- value => Playable.SetFloat(controllerParameterHash, value));
- AddParameterBinding(binding);
- return binding;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<int> BindInt(StringReference name)
- => BindInt(name, name);
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<int> BindInt(StringReference animancerParameter, string controllerParameterName)
- => BindInt(animancerParameter, Animator.StringToHash(controllerParameterName));
- /// <summary>
- /// Configures a parameter in the <see cref="Controller"/>
- /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- public ParameterBinding<int> BindInt(StringReference animancerParameter, int controllerParameterHash)
- {
- var parameter = Graph.Parameters.GetOrCreate<int>(animancerParameter);
- var binding = new ParameterBinding<int>(
- parameter,
- value => Playable.SetInteger(controllerParameterHash, value));
- AddParameterBinding(binding);
- return binding;
- }
- /************************************************************************************************************************/
- /// <summary>An <see cref="IDisposable"/> binding to <see cref="Parameter{T}.OnValueChanged"/>.</summary>
- /// https://kybernetik.com.au/animancer/api/Animancer/ParameterBinding_1
- public class ParameterBinding<T> : IDisposable
- {
- /************************************************************************************************************************/
- /// <summary>The parameter being watched.</summary>
- public readonly Parameter<T> Parameter;
- /// <summary>The callback to invoke when the parameter changes.</summary>
- public readonly Action<T> OnParameterChanged;
- /************************************************************************************************************************/
- /// <summary>
- /// Invokes `onParameterChanged` and adds it to the <see cref="Parameter{T}.OnValueChanged"/>
- /// to be removed by <see cref="Dispose"/>.
- /// </summary>
- public ParameterBinding(
- Parameter<T> parameter,
- Action<T> onParameterChanged)
- {
- Parameter = parameter;
- OnParameterChanged = onParameterChanged;
- OnParameterChanged(Parameter.Value);
- Parameter.OnValueChanged += OnParameterChanged;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Removes <see cref="OnParameterChanged"/> from the <see cref="Parameter{T}.OnValueChanged"/>.
- /// </summary>
- public void Dispose()
- {
- Parameter.OnValueChanged -= OnParameterChanged;
- }
- /************************************************************************************************************************/
- }
- /************************************************************************************************************************/
- /// <summary>
- /// A serializable array of data which can create <see cref="ParameterBinding{T}"/>s at runtime.
- /// </summary>
- /// <remarks>
- /// This data contains a <see cref="Bindings"/> array and <see cref="Mode"/> flag:
- /// <list type="bullet">
- ///
- /// <item>
- /// If the array is empty,
- /// <c>true</c> will bind all parameters by name
- /// and <c>false</c> will bind nothing.
- /// </item>
- ///
- /// <item>
- /// Otherwise, <c>true</c> will bind <c>[i * 2]</c> in the <see cref="RuntimeAnimatorController"/>
- /// to <c>[i * 2 + 1]</c> in the <see cref="AnimancerGraph.Parameters"/>.
- /// </item>
- ///
- /// <item>
- /// And <c>false</c> will bind each of its parameters to the same name in both systems.
- /// </item>
- ///
- /// </list>
- /// </remarks>
- /// https://kybernetik.com.au/animancer/api/Animancer/SerializableParameterBindings
- [Serializable]
- public class SerializableParameterBindings :
- ICloneable<SerializableParameterBindings>
- {
- /************************************************************************************************************************/
- [SerializeField]
- private bool _Mode;
- /// <summary>[<see cref="SerializeField"/>]
- /// Modifies the way the <see cref="Bindings"/> array is interpreted.
- /// </summary>
- /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
- public ref bool Mode
- => ref _Mode;
- #if UNITY_EDITOR
- /// <summary>[Editor-Only] The name of the serialized backing field of <see cref="Mode"/>.</summary>
- public const string ModeFieldName = nameof(_Mode);
- #endif
- /************************************************************************************************************************/
- /// <summary>[<see cref="SerializeField"/>]
- /// Should all parameters in the <see cref="RuntimeAnimatorController"/> be bound by name?
- /// </summary>
- /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
- public bool BindAllParameters
- {
- get => _Mode && _Bindings.Length == 0;
- set
- {
- _Mode = value;
- if (value)
- {
- _Bindings = Array.Empty<StringAsset>();
- }
- else
- {
- Debug.Assert(
- _Bindings.Length == 0,
- $"{nameof(BindAllParameters)} can't be disabled unless the {nameof(Bindings)}" +
- $" array is empty because it changes the meaning of that array.");
- }
- }
- }
- /************************************************************************************************************************/
- /// <summary>[<see cref="SerializeField"/>]
- /// Should the <see cref="Bindings"/> be grouped into pairs
- /// to bind each <see cref="RuntimeAnimatorController"/> parameter
- /// to the subsequent parameter in <see cref="AnimancerGraph.Parameters"/>?
- /// </summary>
- /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
- public bool RebindNames
- {
- get => _Mode && _Bindings.Length > 0;
- set
- {
- _Mode = value;
- if (value)
- {
- if (_Bindings.Length % 2 != 0)
- Array.Resize(ref _Bindings, _Bindings.Length + 1);
- }
- else
- {
- Debug.Assert(
- _Bindings.Length == 0,
- $"{nameof(RebindNames)} can't be disabled unless the {nameof(Bindings)}" +
- $" array is empty because it changes the meaning of that array.");
- }
- }
- }
- /************************************************************************************************************************/
- [SerializeField]
- private StringAsset[] _Bindings = Array.Empty<StringAsset>();
- /// <summary>[<see cref="SerializeField"/>]
- /// Parameter names used to have parameters in the <see cref="RuntimeAnimatorController"/>
- /// follow the value of parameters in the <see cref="AnimancerGraph.Parameters"/>.
- /// </summary>
- /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
- public StringAsset[] Bindings
- {
- get => _Bindings;
- set
- {
- Debug.Assert(
- value != null,
- $"{nameof(Bindings)} can't be null. Use Array.Empty<StringAsset>() instead.");
- _Bindings = value;
- }
- }
- #if UNITY_EDITOR
- /// <summary>[Editor-Only] The name of the serialized backing field of <see cref="Bindings"/>.</summary>
- public const string BindingsFieldName = nameof(_Bindings);
- #endif
- /************************************************************************************************************************/
- /// <summary>Creates runtime bindings for the `state`.</summary>
- /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
- public void Deserialize(ControllerState state)
- {
- if (_Bindings.Length == 0)
- {
- if (_Mode)
- state.BindAllParameters();
- // Else do nothing.
- }
- else
- {
- if (_Mode)
- {
- for (int i = 0; i < _Bindings.Length - 1; i += 2)
- {
- var controller = _Bindings[i];
- var animancer = _Bindings[i + 1];
- if (controller == null ||
- animancer == null)
- continue;
- state.BindParameter(animancer, controller);
- }
- }
- else
- {
- for (int i = 0; i < _Bindings.Length; i++)
- {
- var name = _Bindings[i];
- if (name == null)
- continue;
- state.BindParameter(name);
- }
- }
- }
- }
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public SerializableParameterBindings Clone(CloneContext context)
- {
- var bindingCount = Bindings != null ? Bindings.Length : 0;
- var clone = new SerializableParameterBindings()
- {
- BindAllParameters = BindAllParameters,
- Bindings = new StringAsset[bindingCount],
- };
- for (int i = 0; i < bindingCount; i++)
- clone.Bindings[i] = context.GetCloneOrOriginal(Bindings[i]);
- return clone;
- }
- /************************************************************************************************************************/
- }
- /************************************************************************************************************************/
- }
- }
|