TransitionLibrary.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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.TransitionLibraries
  6. {
  7. /// <summary>[Pro-Only]
  8. /// A library of <see cref="ITransition"/>s which allows specific
  9. /// transition combinations to be overridden without needing to be hard coded.
  10. /// </summary>
  11. /// <remarks>
  12. /// <strong>Documentation:</strong>
  13. /// <see href="https://kybernetik.com.au/animancer/docs/manual/transitions/libraries">
  14. /// Transition Libraries</see>
  15. /// </remarks>
  16. /// https://kybernetik.com.au/animancer/api/Animancer.TransitionLibraries/TransitionLibrary
  17. public class TransitionLibrary :
  18. IAnimationClipSource,
  19. ICopyable<TransitionLibrary>
  20. {
  21. /************************************************************************************************************************/
  22. #region Fields and Properties
  23. /************************************************************************************************************************/
  24. /// <summary>[Pro-Only] Modifiers in the order they are created.</summary>
  25. /// <remarks>The <see cref="TransitionModifierGroup.Index"/> of each item corresponds to its position in this list.</remarks>
  26. private readonly List<TransitionModifierGroup>
  27. TransitionModifiers = new();
  28. /// <summary>[Pro-Only] Modifiers registered by their <see cref="IHasKey.Key"/> as well as any custom aliases.</summary>
  29. private readonly Dictionary<object, TransitionModifierGroup>
  30. KeyedTransitionModifiers = new();
  31. /************************************************************************************************************************/
  32. /// <summary>[Pro-Only] The number of transitions in this library.</summary>
  33. public int Count
  34. => TransitionModifiers.Count;
  35. /// <summary>[Pro-Only] The number of transitions in this library plus any additional aliases.</summary>
  36. public int AliasCount
  37. => KeyedTransitionModifiers.Count;
  38. /************************************************************************************************************************/
  39. #endregion
  40. /************************************************************************************************************************/
  41. #region Queries
  42. /************************************************************************************************************************/
  43. /// <summary>[Pro-Only]
  44. /// Does this library contain a transition registered with the `key`?
  45. /// </summary>
  46. public bool ContainsKey(object key)
  47. => KeyedTransitionModifiers.ContainsKey(key);
  48. /// <summary>[Pro-Only]
  49. /// Does this library contain a transition registered with the <see cref="IHasKey.Key"/>?
  50. /// </summary>
  51. public bool ContainsKey(IHasKey hasKey)
  52. => ContainsKey(hasKey.Key);
  53. /************************************************************************************************************************/
  54. /// <summary>[Pro-Only]
  55. /// Tries to find a <see cref="TransitionModifierGroup"/> registered with the `key`.
  56. /// </summary>
  57. public bool TryGetTransition(object key, out TransitionModifierGroup transition)
  58. {
  59. #if UNITY_ASSERTIONS
  60. if (KeyedTransitionModifiers.TryGetValue(key, out transition))
  61. return true;
  62. AssertStringReference(key);
  63. return false;
  64. #else
  65. return KeyedTransitionModifiers.TryGetValue(key, out transition);
  66. #endif
  67. }
  68. /// <summary>[Pro-Only]
  69. /// Tries to find a <see cref="TransitionModifierGroup"/> registered with the <see cref="IHasKey.Key"/>.
  70. /// </summary>
  71. public bool TryGetTransition(IHasKey hasKey, out TransitionModifierGroup transition)
  72. => TryGetTransition(hasKey.Key, out transition);
  73. /// <summary>[Pro-Only]
  74. /// Tries to find a <see cref="TransitionModifierGroup"/>
  75. /// via its <see cref="TransitionModifierGroup.Index"/>.
  76. /// </summary>
  77. public bool TryGetTransition(int index, out TransitionModifierGroup transition)
  78. => TransitionModifiers.TryGet(index, out transition)
  79. && transition != null;
  80. /************************************************************************************************************************/
  81. /// <summary>[Pro-Only]
  82. /// Finds the <see cref="TransitionModifierGroup.Index"/> of the group registered with the `key`
  83. /// or returns <c>-1</c>.
  84. /// </summary>
  85. public int IndexOf(object key)
  86. => TryGetTransition(key, out var group)
  87. ? group.Index
  88. : -1;
  89. /// <summary>[Pro-Only]
  90. /// Finds the <see cref="TransitionModifierGroup.Index"/> of the group registered with the `key`
  91. /// or returns <c>-1</c>.
  92. /// </summary>
  93. public int IndexOf(IHasKey hasKey)
  94. => IndexOf(hasKey.Key);
  95. /************************************************************************************************************************/
  96. /// <summary>[Pro-Only]
  97. /// Returns the fade duration to use when transitioning from `from` to the `transition`.
  98. /// </summary>
  99. public float GetFadeDuration(
  100. object from,
  101. ITransition to)
  102. {
  103. if (from != null &&
  104. TryGetTransition(to.Key, out var group))
  105. return group.GetFadeDuration(from);
  106. return to.FadeDuration;
  107. }
  108. /// <summary>[Pro-Only]
  109. /// Returns the fade duration to use when transitioning from `from` to the `transition`.
  110. /// </summary>
  111. public float GetFadeDuration(
  112. IHasKey from,
  113. ITransition to)
  114. => GetFadeDuration(from?.Key, to);
  115. /// <summary>[Pro-Only]
  116. /// Returns the fade duration to use when transitioning from the
  117. /// <see cref="AnimancerLayer.CurrentState"/> to the `transition`.
  118. /// </summary>
  119. public float GetFadeDuration(
  120. AnimancerLayer layer,
  121. ITransition transition)
  122. => GetFadeDuration(layer.CurrentState?.Key, transition);
  123. /// <summary>[Pro-Only]
  124. /// Returns the fade duration to use when transitioning from the
  125. /// <see cref="AnimancerLayer.CurrentState"/> to the `key`.
  126. /// </summary>
  127. public float GetFadeDuration(
  128. AnimancerLayer layer,
  129. object key,
  130. float fadeDuration)
  131. {
  132. AssertStringReference(key);
  133. var from = layer.CurrentState?.Key;
  134. if (from != null &&
  135. TryGetTransition(key, out var group))
  136. return group.GetFadeDuration(from);
  137. return fadeDuration;
  138. }
  139. /************************************************************************************************************************/
  140. /// <summary>[Pro-Only] Gathers all the animations in this library.</summary>
  141. public void GetAnimationClips(List<AnimationClip> results)
  142. {
  143. for (int i = TransitionModifiers.Count - 1; i >= 0; i--)
  144. results.GatherFromSource(TransitionModifiers[i].Transition);
  145. }
  146. /************************************************************************************************************************/
  147. #endregion
  148. /************************************************************************************************************************/
  149. #region Add
  150. /************************************************************************************************************************/
  151. /// <summary>[Pro-Only] Adds the contents of the `definition` to this library.</summary>
  152. /// <remarks>Existing values will be completely replaced.</remarks>
  153. public void Initialize(TransitionLibraryDefinition definition)
  154. {
  155. Clear();
  156. if (definition == null)
  157. return;
  158. var count = definition.Transitions.Length;
  159. if (TransitionModifiers.Capacity < count)
  160. {
  161. var capacity = Math.Max(count, 16);
  162. TransitionModifiers.Capacity = capacity;
  163. KeyedTransitionModifiers.EnsureCapacity(capacity);
  164. }
  165. for (int i = 0; i < count; i++)
  166. {
  167. var transition = definition.Transitions[i];
  168. if (transition != null)
  169. SetTransition(transition);
  170. }
  171. for (int i = 0; i < definition.Modifiers.Length; i++)
  172. SetFadeDuration(definition.Modifiers[i]);
  173. if (definition.AliasAllTransitions)
  174. {
  175. for (int i = 0; i < count; i++)
  176. {
  177. var transition = definition.Transitions[i];
  178. var modifier = TransitionModifiers[i];
  179. KeyedTransitionModifiers[StringReference.Get(transition.name)] = modifier;
  180. }
  181. }
  182. for (int i = 0; i < definition.Aliases.Length; i++)
  183. {
  184. var alias = definition.Aliases[i];
  185. if (alias.Name != null &&
  186. TransitionModifiers.TryGet(alias.Index, out var group))
  187. KeyedTransitionModifiers[alias.Name.Name] = group;
  188. }
  189. }
  190. /************************************************************************************************************************/
  191. /// <summary>[Pro-Only] Adds the `transition` to this library.</summary>
  192. /// <exception cref="ArgumentException">A transition is already registered with the `key`.</exception>
  193. public TransitionModifierGroup AddTransition(
  194. object key,
  195. ITransition transition)
  196. {
  197. AssertStringReference(key);
  198. var modifier = new TransitionModifierGroup(TransitionModifiers.Count, transition);
  199. KeyedTransitionModifiers.Add(key, modifier);
  200. TransitionModifiers.Add(modifier);
  201. return modifier;
  202. }
  203. /// <summary>[Pro-Only] Adds the `transition` to this library.</summary>
  204. /// <exception cref="ArgumentException">A transition is already registered with the `key`.</exception>
  205. public TransitionModifierGroup AddTransition(
  206. IHasKey hasKey,
  207. ITransition transition)
  208. => AddTransition(hasKey.Key, transition);
  209. /// <summary>[Pro-Only] Adds the `transition` to this library.</summary>
  210. /// <exception cref="ArgumentException">A transition is already registered with the `key`.</exception>
  211. public TransitionModifierGroup AddTransition(
  212. ITransition transition)
  213. => AddTransition(transition, transition);
  214. /************************************************************************************************************************/
  215. /// <summary>[Pro-Only]
  216. /// Adds the `transition` to this library or replaces the existing one registered with the `key`.
  217. /// </summary>
  218. public TransitionModifierGroup SetTransition(
  219. object key,
  220. ITransition transition)
  221. {
  222. if (TryGetTransition(key, out var oldModifier))
  223. {
  224. oldModifier.Transition = transition;
  225. return oldModifier;
  226. }
  227. return AddTransition(key, transition);
  228. }
  229. /// <summary>[Pro-Only]
  230. /// Adds the `transition` to this library or replaces the existing one registered with the `key`.
  231. /// </summary>
  232. public TransitionModifierGroup SetTransition(
  233. IHasKey hasKey,
  234. ITransition transition)
  235. => SetTransition(hasKey.Key, transition);
  236. /// <summary>[Pro-Only]
  237. /// Adds the `transition` to this library or replaces the existing one registered with the `key`.
  238. /// </summary>
  239. public TransitionModifierGroup SetTransition(
  240. ITransition transition)
  241. => SetTransition(transition, transition);
  242. /************************************************************************************************************************/
  243. /// <summary>[Pro-Only]
  244. /// Sets the <see cref="ITransition.FadeDuration"/> to use when transitioning from `from` to `to`.
  245. /// </summary>
  246. public void SetFadeDuration(
  247. object from,
  248. ITransition to,
  249. float fadeDuration)
  250. {
  251. var group = SetTransition(to.Key, to);
  252. group.SetFadeDuration(
  253. from,
  254. fadeDuration);
  255. }
  256. /// <summary>[Pro-Only]
  257. /// Sets the <see cref="ITransition.FadeDuration"/> to use when transitioning from `from` to `to`.
  258. /// </summary>
  259. public void SetFadeDuration(
  260. IHasKey from,
  261. ITransition to,
  262. float fadeDuration)
  263. => SetFadeDuration(from.Key, to, fadeDuration);
  264. /// <summary>[Pro-Only]
  265. /// Sets the <see cref="ITransition.FadeDuration"/> to use when transitioning from
  266. /// <see cref="TransitionModifierDefinition.FromIndex"/> to <see cref="TransitionModifierDefinition.ToIndex"/>.
  267. /// </summary>
  268. public bool SetFadeDuration(
  269. TransitionModifierDefinition modifier)
  270. {
  271. if (!TransitionModifiers.TryGet(modifier.FromIndex, out var from) ||
  272. !TransitionModifiers.TryGet(modifier.ToIndex, out var to))
  273. return false;
  274. to.SetFadeDuration(
  275. from.Transition.Key,
  276. modifier.FadeDuration);
  277. return true;
  278. }
  279. /************************************************************************************************************************/
  280. /// <summary>[Pro-Only] Registers the `group` with another `key`.</summary>
  281. public void AddAlias(
  282. object key,
  283. TransitionModifierGroup group)
  284. {
  285. AssertStringReference(key);
  286. AssertGroup(group);
  287. KeyedTransitionModifiers.Add(key, group);
  288. }
  289. /// <summary>[Pro-Only] Registers the `transition` with the `key`.</summary>
  290. /// <remarks>Also registers it with its <see cref="IHasKey.Key"/> if it wasn't already.</remarks>
  291. public TransitionModifierGroup AddAlias(
  292. object key,
  293. ITransition transition)
  294. {
  295. var group = SetTransition(transition);
  296. AddAlias(key, group);
  297. return group;
  298. }
  299. /************************************************************************************************************************/
  300. /// <summary>[Pro-Only] Adds the contents of `copyFrom` into this library.</summary>
  301. /// <remarks>
  302. /// This method adds and replaces values, but does not remove any
  303. /// (unlike <see cref="CopyFrom(TransitionLibrary, CloneContext)"/>.
  304. /// </remarks>
  305. public void AddLibrary(TransitionLibrary library, CloneContext context)
  306. {
  307. if (library == null)
  308. return;
  309. for (int i = 0; i < TransitionModifiers.Count; i++)
  310. {
  311. var group = TransitionModifiers[i];
  312. context[group.Transition] = group;
  313. }
  314. foreach (var group in library.KeyedTransitionModifiers)
  315. {
  316. var transition = group.Value.Transition;
  317. if (context.TryGetClone(transition, out var clone) &&
  318. clone is TransitionModifierGroup cloneGroup)
  319. {
  320. AssertGroup(cloneGroup);
  321. KeyedTransitionModifiers[group.Key] = cloneGroup;
  322. }
  323. else
  324. {
  325. cloneGroup = SetTransition(group.Key, group.Value.Transition);
  326. cloneGroup.CopyFrom(group.Value);
  327. context[transition] = cloneGroup;
  328. }
  329. }
  330. }
  331. /// <summary>[Pro-Only] Adds the contents of `copyFrom` into this library.</summary>
  332. /// <remarks>
  333. /// This method adds and replaces values, but does not remove any
  334. /// (unlike <see cref="CopyFrom(TransitionLibrary, CloneContext)"/>.
  335. /// </remarks>
  336. public void AddLibrary(TransitionLibrary library)
  337. {
  338. var context = CloneContext.Pool.Instance.Acquire();
  339. AddLibrary(library, context);
  340. CloneContext.Pool.Instance.Release(context);
  341. }
  342. /************************************************************************************************************************/
  343. /// <inheritdoc/>
  344. /// <remarks>See also <see cref="AddLibrary(TransitionLibrary, CloneContext)"/>.</remarks>
  345. public void CopyFrom(TransitionLibrary copyFrom, CloneContext context)
  346. {
  347. Clear();
  348. if (copyFrom == null)
  349. return;
  350. var count = copyFrom.TransitionModifiers.Count;
  351. for (int i = 0; i < count; i++)
  352. TransitionModifiers.Add(copyFrom.TransitionModifiers[i].Clone(context));
  353. foreach (var group in copyFrom.KeyedTransitionModifiers)
  354. {
  355. var clone = TransitionModifiers[group.Value.Index];
  356. KeyedTransitionModifiers.Add(group.Key, clone);
  357. }
  358. }
  359. /************************************************************************************************************************/
  360. #endregion
  361. /************************************************************************************************************************/
  362. #region Remove
  363. /************************************************************************************************************************/
  364. // Remove from the dictionary but not the list because there might be multiple aliases for that index.
  365. /// <summary>[Pro-Only] Removes the transition registered with the `key`.</summary>
  366. public bool RemoveTransition(object key)
  367. => KeyedTransitionModifiers.Remove(key);
  368. /// <summary>[Pro-Only] Removes the transition registered with the <see cref="IHasKey.Key"/>.</summary>
  369. public bool RemoveTransition(IHasKey hasKey)
  370. => RemoveTransition(hasKey.Key);
  371. /************************************************************************************************************************/
  372. /// <summary>[Pro-Only] Removes a modified fade duration for transitioning from `from` to `to`.</summary>
  373. public bool RemoveFadeDuration(object from, object to)
  374. => TryGetTransition(to, out var group)
  375. && group.FromKeyToFadeDuration != null
  376. && group.FromKeyToFadeDuration.Remove(from);
  377. /// <summary>[Pro-Only] Removes a modified fade duration for transitioning from `from` to `to`.</summary>
  378. public bool RemoveFadeDuration(IHasKey from, IHasKey to)
  379. => RemoveFadeDuration(from.Key, to.Key);
  380. /************************************************************************************************************************/
  381. /// <summary>[Pro-Only] Removes everything from this library, leaving it empty.</summary>
  382. public void Clear()
  383. {
  384. TransitionModifiers.Clear();
  385. KeyedTransitionModifiers.Clear();
  386. }
  387. /************************************************************************************************************************/
  388. #endregion
  389. /************************************************************************************************************************/
  390. #region Play
  391. /************************************************************************************************************************/
  392. /// <summary>
  393. /// Calls <see cref="AnimancerLayer.Play(ITransition, float, FadeMode)"/>
  394. /// with the fade duration potentially modified by this library.
  395. /// </summary>
  396. public AnimancerState Play(
  397. AnimancerLayer layer,
  398. ITransition transition)
  399. => layer.Play(
  400. transition,
  401. GetFadeDuration(layer, transition),
  402. transition.FadeMode);
  403. /// <summary>
  404. /// Calls <see cref="AnimancerLayer.Play(ITransition, float, FadeMode)"/>
  405. /// with the fade duration potentially modified by this library.
  406. /// </summary>
  407. public AnimancerState Play(
  408. AnimancerLayer layer,
  409. TransitionModifierGroup transition)
  410. {
  411. var from = layer.CurrentState?.Key;
  412. var to = transition.Transition;
  413. var fadeDuration = from != null
  414. ? transition.GetFadeDuration(from)
  415. : to.FadeDuration;
  416. return layer.Play(
  417. to,
  418. fadeDuration,
  419. to.FadeMode);
  420. }
  421. /************************************************************************************************************************/
  422. /// <summary>
  423. /// Plays the transition registered with the specified `key` if there is one.
  424. /// Otherwise, returns <c>null</c>.
  425. /// </summary>
  426. public AnimancerState TryPlay(
  427. AnimancerLayer layer,
  428. object key)
  429. => TryGetTransition(key, out var transition)
  430. ? Play(layer, transition)
  431. : null;
  432. /************************************************************************************************************************/
  433. #endregion
  434. /************************************************************************************************************************/
  435. #region Assertions
  436. /************************************************************************************************************************/
  437. /// <summary>[Assert-Conditional]
  438. /// Logs <see cref="OptionalWarning.StringReference"/> if the `key` is a <see cref="string"/>.
  439. /// </summary>
  440. [System.Diagnostics.Conditional(Strings.Assertions)]
  441. private void AssertStringReference(object key)
  442. {
  443. #if UNITY_ASSERTIONS
  444. if (key is string keyString)
  445. {
  446. if (StringReference.TryGet(keyString, out var keyReference) &&
  447. KeyedTransitionModifiers.ContainsKey(keyReference))
  448. Debug.LogError(
  449. $"{nameof(TransitionLibrary)} key type mismatch:" +
  450. $" attempted to use string '{keyString}'," +
  451. $" but that value is registered as a {nameof(StringReference)}." +
  452. $" Use a {nameof(StringReference)} to ensure the correct lookup.");
  453. else
  454. OptionalWarning.StringReference.Log(
  455. $"A string '{keyString}' is being used as a key in a {nameof(TransitionLibrary)}." +
  456. $" {nameof(StringReference)}s should be used instead of strings because they are more efficient" +
  457. $" and to avoid mismatches with aliases in a {nameof(TransitionLibraryDefinition)}.");
  458. }
  459. #endif
  460. }
  461. /************************************************************************************************************************/
  462. /// <summary>[Assert-Conditional]
  463. /// Asserts that the <see cref="TransitionModifierGroup.Index"/>
  464. /// corresponds to the <see cref="TransitionModifiers"/>.
  465. /// </summary>
  466. [System.Diagnostics.Conditional(Strings.Assertions)]
  467. [HideInCallstack]
  468. internal void AssertGroup(TransitionModifierGroup group)
  469. {
  470. #if UNITY_ASSERTIONS
  471. if (!TransitionModifiers.TryGet(group.Index, out var registered) ||
  472. registered != group)
  473. Debug.LogError(
  474. $"{nameof(CloneContext)} contains an {nameof(TransitionModifierGroup)}" +
  475. $" which isn't part of this {nameof(TransitionLibrary)}." +
  476. $" It must have been added to the context manually.");
  477. #endif
  478. }
  479. /************************************************************************************************************************/
  480. #endregion
  481. /************************************************************************************************************************/
  482. }
  483. }