AnimancerEvent.Sequence.Serializable.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
  3. //#define ANIMANCER_ULT_EVENTS
  4. // If you edit this file to change the callback type to something other than UltEvents, you will need to change this
  5. // alias as well as the HasPersistentCalls method below.
  6. #if ANIMANCER_ULT_EVENTS
  7. using SerializableCallback = UltEvents.UltEvent;
  8. #else
  9. using SerializableCallback = UnityEngine.Events.UnityEvent;
  10. #endif
  11. using UnityEngine;
  12. using System;
  13. namespace Animancer
  14. {
  15. /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerEvent
  16. partial struct AnimancerEvent
  17. {
  18. /// https://kybernetik.com.au/animancer/api/Animancer/Sequence
  19. partial class Sequence
  20. {
  21. /// <summary>
  22. /// An <see cref="Sequence"/> that can be serialized and uses <see cref="SerializableCallback"/>s to define
  23. /// the <see cref="callback"/>s.
  24. /// </summary>
  25. /// <remarks>
  26. /// If you have Animancer Pro you can replace <see cref="SerializableCallback"/>s with
  27. /// <see href="https://kybernetik.com.au/ultevents">UltEvents</see> using the following procedure:
  28. /// <list type="number">
  29. /// <item>Select the <c>Assets/Plugins/Animancer/Animancer.asmdef</c> and add a Reference to the
  30. /// <c>UltEvents</c> Assembly Definition.</item>
  31. /// <item>Go into the Player Settings of your project and add <c>ANIMANCER_ULT_EVENTS</c> as a Scripting
  32. /// Define Symbol. Or you can simply edit this script to change the event type (it is located at
  33. /// <c>Assets/Plugins/Animancer/Internal/Core/AnimancerEvent.Sequence.Serializable.cs</c> by default.</item>
  34. /// </list>
  35. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Events</see>
  36. /// </remarks>
  37. /// https://kybernetik.com.au/animancer/api/Animancer/Serializable
  38. ///
  39. [Serializable]
  40. public class Serializable : ICopyable<Serializable>
  41. #if UNITY_EDITOR
  42. , ISerializationCallbackReceiver
  43. #endif
  44. {
  45. /************************************************************************************************************************/
  46. [SerializeField]
  47. private float[] _NormalizedTimes;
  48. /// <summary>[<see cref="SerializeField"/>] The serialized <see cref="normalizedTime"/>s.</summary>
  49. public ref float[] NormalizedTimes => ref _NormalizedTimes;
  50. /************************************************************************************************************************/
  51. [SerializeField]
  52. private SerializableCallback[] _Callbacks;
  53. /// <summary>[<see cref="SerializeField"/>] The serialized <see cref="callback"/>s.</summary>
  54. /// <remarks>
  55. /// This array only needs to be large enough to hold the last event that actually contains any calls.
  56. /// Any empty or missing elements will simply use the <see cref="DummyCallback"/> at runtime.
  57. /// </remarks>
  58. public ref SerializableCallback[] Callbacks => ref _Callbacks;
  59. /************************************************************************************************************************/
  60. [SerializeField]
  61. private string[] _Names;
  62. /// <summary>[<see cref="SerializeField"/>] The serialized <see cref="Sequence.Names"/>.</summary>
  63. public ref string[] Names => ref _Names;
  64. /************************************************************************************************************************/
  65. #if UNITY_EDITOR
  66. /************************************************************************************************************************/
  67. /// <summary>[Editor-Only, Internal] The name of the array field which stores the <see cref="normalizedTime"/>s.</summary>
  68. internal const string NormalizedTimesField = nameof(_NormalizedTimes);
  69. /// <summary>[Editor-Only, Internal] The name of the array field which stores the serialized <see cref="callback"/>s.</summary>
  70. internal const string CallbacksField = nameof(_Callbacks);
  71. /// <summary>[Editor-Only, Internal] The name of the array field which stores the serialized <see cref="Names"/>.</summary>
  72. internal const string NamesField = nameof(_Names);
  73. /************************************************************************************************************************/
  74. #endif
  75. /************************************************************************************************************************/
  76. private Sequence _Events;
  77. /// <summary>
  78. /// The runtime <see cref="Sequence"/> compiled from this <see cref="Serializable"/>.
  79. /// Each call after the first will return the same value.
  80. /// </summary>
  81. /// <remarks>
  82. /// Unlike <see cref="GetEventsOptional"/>, this property will create an empty
  83. /// <see cref="Sequence"/> instead of returning null if there are no events.
  84. /// </remarks>
  85. public Sequence Events
  86. {
  87. get
  88. {
  89. if (_Events == null)
  90. {
  91. GetEventsOptional();
  92. if (_Events == null)
  93. _Events = new Sequence();
  94. }
  95. return _Events;
  96. }
  97. set => _Events = value;
  98. }
  99. /************************************************************************************************************************/
  100. /// <summary>
  101. /// Returns the runtime <see cref="Sequence"/> compiled from this <see cref="Serializable"/>.
  102. /// Each call after the first will return the same value.
  103. /// </summary>
  104. /// <remarks>
  105. /// This method returns null if the sequence would be empty anyway and is used by the implicit
  106. /// conversion from <see cref="Serializable"/> to <see cref="Sequence"/>.
  107. /// </remarks>
  108. public Sequence GetEventsOptional()
  109. {
  110. if (_Events != null ||
  111. _NormalizedTimes == null)
  112. return _Events;
  113. var timeCount = _NormalizedTimes.Length;
  114. if (timeCount == 0)
  115. return null;
  116. var callbackCount = _Callbacks != null ? _Callbacks.Length : 0;
  117. var callback = callbackCount >= timeCount-- ?
  118. GetInvoker(_Callbacks[timeCount]) :
  119. null;
  120. var endEvent = new AnimancerEvent(_NormalizedTimes[timeCount], callback);
  121. _Events = new Sequence(timeCount)
  122. {
  123. EndEvent = endEvent,
  124. Count = timeCount,
  125. _Names = _Names,
  126. };
  127. for (int i = 0; i < timeCount; i++)
  128. {
  129. callback = i < callbackCount ? GetInvoker(_Callbacks[i]) : DummyCallback;
  130. _Events._Events[i] = new AnimancerEvent(_NormalizedTimes[i], callback);
  131. }
  132. return _Events;
  133. }
  134. /// <summary>Calls <see cref="GetEventsOptional"/>.</summary>
  135. public static implicit operator Sequence(Serializable serializable) => serializable?.GetEventsOptional();
  136. /************************************************************************************************************************/
  137. /// <summary>Returns the <see cref="Events"/> or <c>null</c> if it wasn't yet initialized.</summary>
  138. internal Sequence InitializedEvents => _Events;
  139. /************************************************************************************************************************/
  140. /// <summary>
  141. /// If the `callback` has any persistent calls, this method returns a delegate to call its
  142. /// <see cref="SerializableCallback.Invoke"/> method. Otherwise it returns the
  143. /// <see cref="DummyCallback"/>.
  144. /// </summary>
  145. public static Action GetInvoker(SerializableCallback callback)
  146. => HasPersistentCalls(callback) ? callback.Invoke : DummyCallback;
  147. #if UNITY_EDITOR
  148. /// <summary>[Editor-Only]
  149. /// Casts the `callback` and calls <see cref="GetInvoker(SerializableCallback)"/>.
  150. /// </summary>
  151. public static Action GetInvoker(object callback)
  152. => GetInvoker((SerializableCallback)callback);
  153. #endif
  154. /************************************************************************************************************************/
  155. /// <summary>
  156. /// Determines if the `callback` contains any method calls that will be serialized (otherwise the
  157. /// <see cref="DummyCallback"/> can be used instead of creating a new delegate to invoke the empty
  158. /// `callback`).
  159. /// </summary>
  160. public static bool HasPersistentCalls(SerializableCallback callback)
  161. {
  162. if (callback == null)
  163. return false;
  164. // UnityEvents do not allow us to check if any dynamic calls are present.
  165. // But we are not giving runtime access to the events so it does not really matter.
  166. // UltEvents does allow it (via the HasCalls property), but we might as well be consistent.
  167. #if ANIMANCER_ULT_EVENTS
  168. var calls = callback.PersistentCallsList;
  169. return calls != null && calls.Count > 0;
  170. #else
  171. return callback.GetPersistentEventCount() > 0;
  172. #endif
  173. }
  174. #if UNITY_EDITOR
  175. /// <summary>[Editor-Only]
  176. /// Casts the `callback` and calls <see cref="HasPersistentCalls(SerializableCallback)"/>.
  177. /// </summary>
  178. public static bool HasPersistentCalls(object callback) => HasPersistentCalls((SerializableCallback)callback);
  179. #endif
  180. /************************************************************************************************************************/
  181. /// <summary>Returns the <see cref="normalizedTime"/> of the <see cref="EndEvent"/>.</summary>
  182. /// <remarks>If the value is not set, the value is determined by <see cref="GetDefaultNormalizedEndTime"/>.</remarks>
  183. public float GetNormalizedEndTime(float speed = 1)
  184. {
  185. if (_NormalizedTimes.IsNullOrEmpty())
  186. return GetDefaultNormalizedEndTime(speed);
  187. else
  188. return _NormalizedTimes[_NormalizedTimes.Length - 1];
  189. }
  190. /************************************************************************************************************************/
  191. /// <summary>Sets the <see cref="normalizedTime"/> of the <see cref="EndEvent"/>.</summary>
  192. public void SetNormalizedEndTime(float normalizedTime)
  193. {
  194. if (_NormalizedTimes.IsNullOrEmpty())
  195. _NormalizedTimes = new float[] { normalizedTime };
  196. else
  197. _NormalizedTimes[_NormalizedTimes.Length - 1] = normalizedTime;
  198. }
  199. /************************************************************************************************************************/
  200. /// <inheritdoc/>
  201. public void CopyFrom(Serializable copyFrom)
  202. {
  203. if (copyFrom == null)
  204. {
  205. _NormalizedTimes = default;
  206. _Callbacks = default;
  207. _Names = default;
  208. return;
  209. }
  210. AnimancerUtilities.CopyExactArray(copyFrom._NormalizedTimes, ref _NormalizedTimes);
  211. AnimancerUtilities.CopyExactArray(copyFrom._Callbacks, ref _Callbacks);
  212. AnimancerUtilities.CopyExactArray(copyFrom._Names, ref _Names);
  213. }
  214. /************************************************************************************************************************/
  215. #if UNITY_EDITOR
  216. /************************************************************************************************************************/
  217. /// <summary>[Editor-Only] Does nothing.</summary>
  218. /// <remarks>
  219. /// Keeping the runtime <see cref="Events"/> in sync with the serialized data is handled by
  220. /// <see cref="Editor.SerializableEventSequenceDrawer"/>.
  221. /// </remarks>
  222. void ISerializationCallbackReceiver.OnAfterDeserialize() { }
  223. /************************************************************************************************************************/
  224. /// <summary>[Editor-Only] Ensures that the events are sorted by time (excluding the end event).</summary>
  225. void ISerializationCallbackReceiver.OnBeforeSerialize()
  226. {
  227. if (_NormalizedTimes == null ||
  228. _NormalizedTimes.Length <= 2)
  229. {
  230. CompactArrays();
  231. return;
  232. }
  233. var eventContext = Editor.SerializableEventSequenceDrawer.Context.Current;
  234. var selectedEvent = eventContext?.Property != null ? eventContext.SelectedEvent : -1;
  235. var timeCount = _NormalizedTimes.Length - 1;
  236. var previousTime = _NormalizedTimes[0];
  237. // Bubble Sort based on the normalized times.
  238. for (int i = 1; i < timeCount; i++)
  239. {
  240. var time = _NormalizedTimes[i];
  241. if (time >= previousTime)
  242. {
  243. previousTime = time;
  244. continue;
  245. }
  246. _NormalizedTimes.Swap(i, i - 1);
  247. DynamicSwap(ref _Callbacks, i);
  248. DynamicSwap(ref _Names, i);
  249. if (selectedEvent == i)
  250. selectedEvent = i - 1;
  251. else if (selectedEvent == i - 1)
  252. selectedEvent = i;
  253. if (i == 1)
  254. {
  255. i = 0;
  256. previousTime = float.NegativeInfinity;
  257. }
  258. else
  259. {
  260. i -= 2;
  261. previousTime = _NormalizedTimes[i];
  262. }
  263. }
  264. // If the current animation is looping, clamp all times within the 0-1 range.
  265. var transitionContext = Editor.TransitionDrawer.Context;
  266. if (transitionContext != null &&
  267. transitionContext.Transition != null &&
  268. transitionContext.Transition.IsLooping)
  269. {
  270. for (int i = _NormalizedTimes.Length - 1; i >= 0; i--)
  271. {
  272. var time = _NormalizedTimes[i];
  273. if (time < 0)
  274. _NormalizedTimes[i] = 0;
  275. else if (time > AlmostOne)
  276. _NormalizedTimes[i] = AlmostOne;
  277. }
  278. }
  279. // If the selected event was moved adjust the selection.
  280. if (eventContext?.Property != null && eventContext.SelectedEvent != selectedEvent)
  281. {
  282. eventContext.SelectedEvent = selectedEvent;
  283. Editor.TransitionPreviewWindow.PreviewNormalizedTime = _NormalizedTimes[selectedEvent];
  284. }
  285. CompactArrays();
  286. }
  287. /************************************************************************************************************************/
  288. /// <summary>[Editor-Only]
  289. /// Swaps <c>array[index]</c> with <c>array[index - 1]</c> while accounting for the possibility of the
  290. /// `index` being beyond the bounds of the `array`.
  291. /// </summary>
  292. private static void DynamicSwap<T>(ref T[] array, int index)
  293. {
  294. var count = array != null ? array.Length : 0;
  295. if (index == count)
  296. Array.Resize(ref array, ++count);
  297. if (index < count)
  298. array.Swap(index, index - 1);
  299. }
  300. /************************************************************************************************************************/
  301. /// <summary>[Internal]
  302. /// Should the arrays be prevented from reducing their size when their last elements are unused?
  303. /// </summary>
  304. internal static bool DisableCompactArrays { get; set; }
  305. /// <summary>[Editor-Only]
  306. /// Removes empty data from the ends of the arrays to reduce the serialized data size.
  307. /// </summary>
  308. private void CompactArrays()
  309. {
  310. if (DisableCompactArrays)
  311. return;
  312. // If there is only one time and it is NaN, we don't need to store anything.
  313. if (_NormalizedTimes == null ||
  314. (_NormalizedTimes.Length == 1 &&
  315. (_Callbacks == null || _Callbacks.Length == 0) &&
  316. (_Names == null || _Names.Length == 0) &&
  317. float.IsNaN(_NormalizedTimes[0])))
  318. {
  319. _NormalizedTimes = Array.Empty<float>();
  320. _Callbacks = Array.Empty<SerializableCallback>();
  321. _Names = Array.Empty<string>();
  322. return;
  323. }
  324. Trim(ref _Callbacks, _NormalizedTimes.Length, (callback) => HasPersistentCalls(callback));
  325. Trim(ref _Names, _NormalizedTimes.Length, (name) => !string.IsNullOrEmpty(name));
  326. }
  327. /************************************************************************************************************************/
  328. /// <summary>[Editor-Only] Removes unimportant values from the end of the `array`.</summary>
  329. private static void Trim<T>(ref T[] array, int maxLength, Func<T, bool> isImportant)
  330. {
  331. if (array == null)
  332. return;
  333. var count = Math.Min(array.Length, maxLength);
  334. while (count >= 1)
  335. {
  336. var item = array[count - 1];
  337. if (isImportant(item))
  338. break;
  339. else
  340. count--;
  341. }
  342. Array.Resize(ref array, count);
  343. }
  344. /************************************************************************************************************************/
  345. #endif
  346. /************************************************************************************************************************/
  347. }
  348. }
  349. }
  350. }
  351. /************************************************************************************************************************/
  352. #if UNITY_EDITOR
  353. /************************************************************************************************************************/
  354. namespace Animancer.Editor
  355. {
  356. /// <summary>[Editor-Only, Internal]
  357. /// A serializable container which holds a <see cref="SerializableCallback"/> in a field named "_Callback".
  358. /// </summary>
  359. /// <remarks>
  360. /// <see cref="DummySerializableCallback"/> needs to be in a file with the same name as it (otherwise it can't
  361. /// draw the callback properly) and this class needs to be in the same file as
  362. /// <see cref="AnimancerEvent.Sequence.Serializable"/> to use the <see cref="SerializableCallback"/> alias.
  363. /// </remarks>
  364. [Serializable]
  365. internal sealed class SerializableCallbackHolder
  366. {
  367. #pragma warning disable CS0169 // Field is never used.
  368. [SerializeField]
  369. private SerializableCallback _Callback;
  370. #pragma warning restore CS0169 // Field is never used.
  371. /// <summary>The name of the field which stores the <see cref="SerializableCallback"/>.</summary>
  372. internal const string CallbackField = nameof(_Callback);
  373. }
  374. }
  375. /************************************************************************************************************************/
  376. #endif
  377. /************************************************************************************************************************/