ControllerState.ParameterBinding.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace Animancer
  6. {
  7. /// https://kybernetik.com.au/animancer/api/Animancer/ControllerState
  8. partial class ControllerState
  9. {
  10. /************************************************************************************************************************/
  11. private SerializableParameterBindings _SerializedParameterBindings;
  12. /// <summary>Serialized data used to create <see cref="ParameterBinding{T}"/>s at runtime.</summary>
  13. public SerializableParameterBindings SerializedParameterBindings
  14. {
  15. get => _SerializedParameterBindings;
  16. set
  17. {
  18. _SerializedParameterBindings = value;
  19. DeserializeParameterBindings();
  20. }
  21. }
  22. /// <summary>Deserializes the <see cref="SerializedParameterBindings"/>.</summary>
  23. private void DeserializeParameterBindings()
  24. {
  25. if (Graph == null)
  26. return;
  27. DisposeParameterBindings();
  28. _SerializedParameterBindings?.Deserialize(this);
  29. }
  30. /************************************************************************************************************************/
  31. private List<IDisposable> _ParameterBindings;
  32. /// <summary>
  33. /// Adds an object to a list for <see cref="IDisposable.Dispose"/>
  34. /// to be called in <see cref="Destroy"/>.
  35. /// </summary>
  36. private void AddParameterBinding(IDisposable disposable)
  37. {
  38. _ParameterBindings ??= new();
  39. _ParameterBindings.Add(disposable);
  40. }
  41. /// <summary>Disposes everything added by <see cref="AddParameterBinding"/>.</summary>
  42. private void DisposeParameterBindings()
  43. {
  44. if (_ParameterBindings == null)
  45. return;
  46. for (int i = _ParameterBindings.Count - 1; i >= 0; i--)
  47. _ParameterBindings[i].Dispose();
  48. _ParameterBindings.Clear();
  49. }
  50. /************************************************************************************************************************/
  51. /// <summary>
  52. /// Configures all parameters in the <see cref="Controller"/>
  53. /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
  54. /// </summary>
  55. public void BindAllParameters()
  56. {
  57. var count = Playable.GetParameterCount();
  58. for (int i = 0; i < count; i++)
  59. {
  60. var parameter = Playable.GetParameter(i);
  61. BindParameter(parameter.name, parameter);
  62. }
  63. }
  64. /************************************************************************************************************************/
  65. /// <summary>
  66. /// Configures a parameter in the <see cref="Controller"/>
  67. /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
  68. /// </summary>
  69. public void BindParameter(StringReference name)
  70. => BindParameter(name, name);
  71. /// <summary>
  72. /// Configures a parameter in the <see cref="Controller"/>
  73. /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
  74. /// </summary>
  75. public void BindParameter(StringReference animancerParameter, string controllerParameterName)
  76. => BindParameter(animancerParameter, Animator.StringToHash(controllerParameterName));
  77. /// <summary>
  78. /// Configures a parameter in the <see cref="Controller"/>
  79. /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
  80. /// </summary>
  81. public void BindParameter(StringReference animancerParameter, int controllerParameterHash)
  82. {
  83. var count = Playable.GetParameterCount();
  84. for (int i = 0; i < count; i++)
  85. {
  86. var parameter = Playable.GetParameter(i);
  87. if (parameter.nameHash == controllerParameterHash)
  88. {
  89. BindParameter(animancerParameter, parameter);
  90. break;
  91. }
  92. }
  93. }
  94. /************************************************************************************************************************/
  95. /// <summary>
  96. /// Configures all parameters in the <see cref="Controller"/>
  97. /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
  98. /// </summary>
  99. public void BindParameter(
  100. StringReference animancerParameter,
  101. AnimatorControllerParameter controllerParameter)
  102. {
  103. switch (controllerParameter.type)
  104. {
  105. case AnimatorControllerParameterType.Float:
  106. BindFloat(animancerParameter, controllerParameter.nameHash);
  107. break;
  108. case AnimatorControllerParameterType.Int:
  109. BindInt(animancerParameter, controllerParameter.nameHash);
  110. break;
  111. case AnimatorControllerParameterType.Bool:
  112. case AnimatorControllerParameterType.Trigger:
  113. BindBool(animancerParameter, controllerParameter.nameHash);
  114. break;
  115. }
  116. }
  117. /************************************************************************************************************************/
  118. /// <summary>
  119. /// Configures a parameter in the <see cref="Controller"/>
  120. /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
  121. /// </summary>
  122. public ParameterBinding<bool> BindBool(StringReference name)
  123. => BindBool(name, name);
  124. /// <summary>
  125. /// Configures a parameter in the <see cref="Controller"/>
  126. /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
  127. /// </summary>
  128. public ParameterBinding<bool> BindBool(StringReference animancerParameter, string controllerParameterName)
  129. => BindBool(animancerParameter, Animator.StringToHash(controllerParameterName));
  130. /// <summary>
  131. /// Configures a parameter in the <see cref="Controller"/>
  132. /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
  133. /// </summary>
  134. public ParameterBinding<bool> BindBool(StringReference animancerParameter, int controllerParameterHash)
  135. {
  136. var parameter = Graph.Parameters.GetOrCreate<bool>(animancerParameter);
  137. var binding = new ParameterBinding<bool>(
  138. parameter,
  139. value => Playable.SetBool(controllerParameterHash, value));
  140. AddParameterBinding(binding);
  141. return binding;
  142. }
  143. /************************************************************************************************************************/
  144. /// <summary>
  145. /// Configures a parameter in the <see cref="Controller"/>
  146. /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
  147. /// </summary>
  148. public ParameterBinding<float> BindFloat(StringReference name)
  149. => BindFloat(name, name);
  150. /// <summary>
  151. /// Configures a parameter in the <see cref="Controller"/>
  152. /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
  153. /// </summary>
  154. public ParameterBinding<float> BindFloat(StringReference animancerParameter, string controllerParameterName)
  155. => BindFloat(animancerParameter, Animator.StringToHash(controllerParameterName));
  156. /// <summary>
  157. /// Configures a parameter in the <see cref="Controller"/>
  158. /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
  159. /// </summary>
  160. public ParameterBinding<float> BindFloat(StringReference animancerParameter, int controllerParameterHash)
  161. {
  162. var parameter = Graph.Parameters.GetOrCreate<float>(animancerParameter);
  163. var binding = new ParameterBinding<float>(
  164. parameter,
  165. value => Playable.SetFloat(controllerParameterHash, value));
  166. AddParameterBinding(binding);
  167. return binding;
  168. }
  169. /************************************************************************************************************************/
  170. /// <summary>
  171. /// Configures a parameter in the <see cref="Controller"/>
  172. /// to follow the value of a parameter with the same name in the <see cref="AnimancerGraph.Parameters"/>.
  173. /// </summary>
  174. public ParameterBinding<int> BindInt(StringReference name)
  175. => BindInt(name, name);
  176. /// <summary>
  177. /// Configures a parameter in the <see cref="Controller"/>
  178. /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
  179. /// </summary>
  180. public ParameterBinding<int> BindInt(StringReference animancerParameter, string controllerParameterName)
  181. => BindInt(animancerParameter, Animator.StringToHash(controllerParameterName));
  182. /// <summary>
  183. /// Configures a parameter in the <see cref="Controller"/>
  184. /// to follow the value of a parameter in the <see cref="AnimancerGraph.Parameters"/>.
  185. /// </summary>
  186. public ParameterBinding<int> BindInt(StringReference animancerParameter, int controllerParameterHash)
  187. {
  188. var parameter = Graph.Parameters.GetOrCreate<int>(animancerParameter);
  189. var binding = new ParameterBinding<int>(
  190. parameter,
  191. value => Playable.SetInteger(controllerParameterHash, value));
  192. AddParameterBinding(binding);
  193. return binding;
  194. }
  195. /************************************************************************************************************************/
  196. /// <summary>An <see cref="IDisposable"/> binding to <see cref="Parameter{T}.OnValueChanged"/>.</summary>
  197. /// https://kybernetik.com.au/animancer/api/Animancer/ParameterBinding_1
  198. public class ParameterBinding<T> : IDisposable
  199. {
  200. /************************************************************************************************************************/
  201. /// <summary>The parameter being watched.</summary>
  202. public readonly Parameter<T> Parameter;
  203. /// <summary>The callback to invoke when the parameter changes.</summary>
  204. public readonly Action<T> OnParameterChanged;
  205. /************************************************************************************************************************/
  206. /// <summary>
  207. /// Invokes `onParameterChanged` and adds it to the <see cref="Parameter{T}.OnValueChanged"/>
  208. /// to be removed by <see cref="Dispose"/>.
  209. /// </summary>
  210. public ParameterBinding(
  211. Parameter<T> parameter,
  212. Action<T> onParameterChanged)
  213. {
  214. Parameter = parameter;
  215. OnParameterChanged = onParameterChanged;
  216. OnParameterChanged(Parameter.Value);
  217. Parameter.OnValueChanged += OnParameterChanged;
  218. }
  219. /************************************************************************************************************************/
  220. /// <summary>
  221. /// Removes <see cref="OnParameterChanged"/> from the <see cref="Parameter{T}.OnValueChanged"/>.
  222. /// </summary>
  223. public void Dispose()
  224. {
  225. Parameter.OnValueChanged -= OnParameterChanged;
  226. }
  227. /************************************************************************************************************************/
  228. }
  229. /************************************************************************************************************************/
  230. /// <summary>
  231. /// A serializable array of data which can create <see cref="ParameterBinding{T}"/>s at runtime.
  232. /// </summary>
  233. /// <remarks>
  234. /// This data contains a <see cref="Bindings"/> array and <see cref="Mode"/> flag:
  235. /// <list type="bullet">
  236. ///
  237. /// <item>
  238. /// If the array is empty,
  239. /// <c>true</c> will bind all parameters by name
  240. /// and <c>false</c> will bind nothing.
  241. /// </item>
  242. ///
  243. /// <item>
  244. /// Otherwise, <c>true</c> will bind <c>[i * 2]</c> in the <see cref="RuntimeAnimatorController"/>
  245. /// to <c>[i * 2 + 1]</c> in the <see cref="AnimancerGraph.Parameters"/>.
  246. /// </item>
  247. ///
  248. /// <item>
  249. /// And <c>false</c> will bind each of its parameters to the same name in both systems.
  250. /// </item>
  251. ///
  252. /// </list>
  253. /// </remarks>
  254. /// https://kybernetik.com.au/animancer/api/Animancer/SerializableParameterBindings
  255. [Serializable]
  256. public class SerializableParameterBindings :
  257. ICloneable<SerializableParameterBindings>
  258. {
  259. /************************************************************************************************************************/
  260. [SerializeField]
  261. private bool _Mode;
  262. /// <summary>[<see cref="SerializeField"/>]
  263. /// Modifies the way the <see cref="Bindings"/> array is interpreted.
  264. /// </summary>
  265. /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
  266. public ref bool Mode
  267. => ref _Mode;
  268. #if UNITY_EDITOR
  269. /// <summary>[Editor-Only] The name of the serialized backing field of <see cref="Mode"/>.</summary>
  270. public const string ModeFieldName = nameof(_Mode);
  271. #endif
  272. /************************************************************************************************************************/
  273. /// <summary>[<see cref="SerializeField"/>]
  274. /// Should all parameters in the <see cref="RuntimeAnimatorController"/> be bound by name?
  275. /// </summary>
  276. /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
  277. public bool BindAllParameters
  278. {
  279. get => _Mode && _Bindings.Length == 0;
  280. set
  281. {
  282. _Mode = value;
  283. if (value)
  284. {
  285. _Bindings = Array.Empty<StringAsset>();
  286. }
  287. else
  288. {
  289. Debug.Assert(
  290. _Bindings.Length == 0,
  291. $"{nameof(BindAllParameters)} can't be disabled unless the {nameof(Bindings)}" +
  292. $" array is empty because it changes the meaning of that array.");
  293. }
  294. }
  295. }
  296. /************************************************************************************************************************/
  297. /// <summary>[<see cref="SerializeField"/>]
  298. /// Should the <see cref="Bindings"/> be grouped into pairs
  299. /// to bind each <see cref="RuntimeAnimatorController"/> parameter
  300. /// to the subsequent parameter in <see cref="AnimancerGraph.Parameters"/>?
  301. /// </summary>
  302. /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
  303. public bool RebindNames
  304. {
  305. get => _Mode && _Bindings.Length > 0;
  306. set
  307. {
  308. _Mode = value;
  309. if (value)
  310. {
  311. if (_Bindings.Length % 2 != 0)
  312. Array.Resize(ref _Bindings, _Bindings.Length + 1);
  313. }
  314. else
  315. {
  316. Debug.Assert(
  317. _Bindings.Length == 0,
  318. $"{nameof(RebindNames)} can't be disabled unless the {nameof(Bindings)}" +
  319. $" array is empty because it changes the meaning of that array.");
  320. }
  321. }
  322. }
  323. /************************************************************************************************************************/
  324. [SerializeField]
  325. private StringAsset[] _Bindings = Array.Empty<StringAsset>();
  326. /// <summary>[<see cref="SerializeField"/>]
  327. /// Parameter names used to have parameters in the <see cref="RuntimeAnimatorController"/>
  328. /// follow the value of parameters in the <see cref="AnimancerGraph.Parameters"/>.
  329. /// </summary>
  330. /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
  331. public StringAsset[] Bindings
  332. {
  333. get => _Bindings;
  334. set
  335. {
  336. Debug.Assert(
  337. value != null,
  338. $"{nameof(Bindings)} can't be null. Use Array.Empty<StringAsset>() instead.");
  339. _Bindings = value;
  340. }
  341. }
  342. #if UNITY_EDITOR
  343. /// <summary>[Editor-Only] The name of the serialized backing field of <see cref="Bindings"/>.</summary>
  344. public const string BindingsFieldName = nameof(_Bindings);
  345. #endif
  346. /************************************************************************************************************************/
  347. /// <summary>Creates runtime bindings for the `state`.</summary>
  348. /// <remarks>See the <see cref="SerializableParameterBindings"/> class for details.</remarks>
  349. public void Deserialize(ControllerState state)
  350. {
  351. if (_Bindings.Length == 0)
  352. {
  353. if (_Mode)
  354. state.BindAllParameters();
  355. // Else do nothing.
  356. }
  357. else
  358. {
  359. if (_Mode)
  360. {
  361. for (int i = 0; i < _Bindings.Length - 1; i += 2)
  362. {
  363. var controller = _Bindings[i];
  364. var animancer = _Bindings[i + 1];
  365. if (controller == null ||
  366. animancer == null)
  367. continue;
  368. state.BindParameter(animancer, controller);
  369. }
  370. }
  371. else
  372. {
  373. for (int i = 0; i < _Bindings.Length; i++)
  374. {
  375. var name = _Bindings[i];
  376. if (name == null)
  377. continue;
  378. state.BindParameter(name);
  379. }
  380. }
  381. }
  382. }
  383. /************************************************************************************************************************/
  384. /// <inheritdoc/>
  385. public SerializableParameterBindings Clone(CloneContext context)
  386. {
  387. var bindingCount = Bindings != null ? Bindings.Length : 0;
  388. var clone = new SerializableParameterBindings()
  389. {
  390. BindAllParameters = BindAllParameters,
  391. Bindings = new StringAsset[bindingCount],
  392. };
  393. for (int i = 0; i < bindingCount; i++)
  394. clone.Bindings[i] = context.GetCloneOrOriginal(Bindings[i]);
  395. return clone;
  396. }
  397. /************************************************************************************************************************/
  398. }
  399. /************************************************************************************************************************/
  400. }
  401. }