AnimancerEvent.Sequence.Serializable.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
  3. using System;
  4. using UnityEngine;
  5. namespace Animancer
  6. {
  7. /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerEvent
  8. partial struct AnimancerEvent
  9. {
  10. /// https://kybernetik.com.au/animancer/api/Animancer/Sequence
  11. partial class Sequence
  12. {
  13. /// <summary>
  14. /// Serializable data which can be used to construct an <see cref="Sequence"/> using
  15. /// <see cref="StringAsset"/>s and <see cref="IInvokable"/>s.
  16. /// </summary>
  17. /// <remarks>
  18. /// <strong>Documentation:</strong>
  19. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer/serialization">
  20. /// Serialized Events</see>
  21. /// </remarks>
  22. /// https://kybernetik.com.au/animancer/api/Animancer/Serializable
  23. [Serializable]
  24. public class Serializable : ICloneable<Serializable>
  25. #if UNITY_EDITOR
  26. , ISerializationCallbackReceiver
  27. #endif
  28. {
  29. /************************************************************************************************************************/
  30. #region Fields and Properties
  31. /************************************************************************************************************************/
  32. [SerializeField]
  33. private float[] _NormalizedTimes;
  34. /// <summary>[<see cref="SerializeField"/>] The serialized <see cref="normalizedTime"/>s.</summary>
  35. /// <remarks>The last item is used for the <see cref="EndEvent"/>.</remarks>
  36. public ref float[] NormalizedTimes => ref _NormalizedTimes;
  37. /************************************************************************************************************************/
  38. [SerializeReference, Polymorphic]
  39. private IInvokable[] _Callbacks;
  40. /// <summary>[<see cref="SerializeField"/>] The serialized <see cref="callback"/>s.</summary>
  41. /// <remarks>
  42. /// This array only needs to be large enough to hold the last item that isn't null.
  43. /// <para></para>
  44. /// If this array is larger than the <see cref="NormalizedTimes"/>, the first item
  45. /// with no corresponding time will be used as the <see cref="OnEnd"/> callback
  46. /// and any others after that will be ignored.
  47. /// </remarks>
  48. public ref IInvokable[] Callbacks => ref _Callbacks;
  49. /************************************************************************************************************************/
  50. [SerializeField]
  51. private StringAsset[] _Names;
  52. /// <summary>[<see cref="SerializeField"/>] The serialized <see cref="Sequence.Names"/>.</summary>
  53. public ref StringAsset[] Names => ref _Names;
  54. /************************************************************************************************************************/
  55. #if UNITY_EDITOR
  56. /************************************************************************************************************************/
  57. /// <summary>[Editor-Only] [Internal]
  58. /// The name of the array field which stores the <see cref="normalizedTime"/>s.
  59. /// </summary>
  60. internal const string NormalizedTimesField = nameof(_NormalizedTimes);
  61. /// <summary>[Editor-Only] [Internal]
  62. /// The name of the array field which stores the serialized <see cref="Callbacks"/>.
  63. /// </summary>
  64. internal const string CallbacksField = nameof(_Callbacks);
  65. /// <summary>[Editor-Only] [Internal]
  66. /// The name of the array field which stores the serialized <see cref="Names"/>.
  67. /// </summary>
  68. internal const string NamesField = nameof(_Names);
  69. /************************************************************************************************************************/
  70. #endif
  71. /************************************************************************************************************************/
  72. private Sequence _Events;
  73. /// <summary>Returns the <see cref="Events"/> or <c>null</c> if it wasn't yet initialized.</summary>
  74. public Sequence InitializedEvents
  75. => _Events;
  76. /// <summary>
  77. /// The runtime <see cref="Sequence"/> compiled from this <see cref="Serializable"/>.
  78. /// Each call after the first will return the same reference.
  79. /// </summary>
  80. /// <remarks>
  81. /// Unlike <see cref="GetEventsOptional"/>, this property will create an empty
  82. /// <see cref="Sequence"/> instead of returning null if there are no events.
  83. /// </remarks>
  84. public Sequence Events
  85. {
  86. get
  87. {
  88. if (_Events == null)
  89. {
  90. GetEventsOptional();
  91. _Events ??= new();
  92. }
  93. return _Events;
  94. }
  95. set => _Events = value;
  96. }
  97. /************************************************************************************************************************/
  98. #endregion
  99. /************************************************************************************************************************/
  100. #region Initialization
  101. /************************************************************************************************************************/
  102. /// <summary>
  103. /// Returns the runtime <see cref="Sequence"/> compiled from this <see cref="Serializable"/>.
  104. /// Each call after the first will return the same reference.
  105. /// </summary>
  106. /// <remarks>
  107. /// This method returns null if the sequence would be empty anyway and is used by the implicit
  108. /// conversion from <see cref="Serializable"/> to <see cref="Sequence"/>.
  109. /// </remarks>
  110. public Sequence GetEventsOptional()
  111. {
  112. if (_Events != null ||
  113. _NormalizedTimes == null)
  114. return _Events;
  115. var timeCount = _NormalizedTimes.Length;
  116. if (timeCount == 0)
  117. return null;
  118. var callbackCount = _Callbacks != null
  119. ? _Callbacks.Length
  120. : 0;
  121. var callback = callbackCount >= timeCount--
  122. ? GetInvoke(_Callbacks[timeCount])
  123. : null;
  124. var endEvent = new AnimancerEvent(_NormalizedTimes[timeCount], callback);
  125. _Events = new(timeCount)
  126. {
  127. EndEvent = endEvent,
  128. Count = timeCount,
  129. Names = StringAsset.ToStringReferences(_Names),
  130. };
  131. var events = _Events._Events;
  132. for (int i = 0; i < timeCount; i++)
  133. {
  134. callback = i < callbackCount
  135. ? GetInvoke(_Callbacks[i])
  136. : InvokeBoundCallback;
  137. events[i] = new(_NormalizedTimes[i], callback);
  138. }
  139. return _Events;
  140. }
  141. /// <summary>Calls <see cref="GetEventsOptional"/>.</summary>
  142. public static implicit operator Sequence(Serializable serializable)
  143. => serializable?.GetEventsOptional();
  144. /************************************************************************************************************************/
  145. /// <summary>
  146. /// Returns the <see cref="IInvokable.Invoke"/> if the `invokable` isn't <c>null</c>.
  147. /// Otherwise, returns <c>null</c>.
  148. /// </summary>
  149. public static Action GetInvoke(IInvokable invokable)
  150. => invokable != null
  151. ? invokable.Invoke
  152. : InvokeBoundCallback;
  153. /************************************************************************************************************************/
  154. #endregion
  155. /************************************************************************************************************************/
  156. #region End Event
  157. /************************************************************************************************************************/
  158. /// <summary>Returns the <see cref="normalizedTime"/> of the <see cref="EndEvent"/>.</summary>
  159. /// <remarks>If the value is not set, the value is determined by <see cref="GetDefaultNormalizedEndTime"/>.</remarks>
  160. public float GetNormalizedEndTime(float speed = 1)
  161. {
  162. return _NormalizedTimes.IsNullOrEmpty()
  163. ? GetDefaultNormalizedEndTime(speed)
  164. : _NormalizedTimes[^1];
  165. }
  166. /************************************************************************************************************************/
  167. /// <summary>Sets the <see cref="normalizedTime"/> of the <see cref="EndEvent"/>.</summary>
  168. public void SetNormalizedEndTime(float normalizedTime)
  169. {
  170. if (_NormalizedTimes.IsNullOrEmpty())
  171. _NormalizedTimes = new float[] { normalizedTime };
  172. else
  173. _NormalizedTimes[^1] = normalizedTime;
  174. }
  175. /************************************************************************************************************************/
  176. /// <summary>Sets the <see cref="callback"/> of the <see cref="EndEvent"/>.</summary>
  177. public void SetEndCallback(IInvokable callback = null)
  178. {
  179. if (_NormalizedTimes.IsNullOrEmpty())
  180. _NormalizedTimes = new float[] { float.NaN };
  181. InsertOptionalItem(ref _Callbacks, _NormalizedTimes.Length - 1, callback);
  182. }
  183. /************************************************************************************************************************/
  184. /// <summary>Sets the data of the <see cref="EndEvent"/>.</summary>
  185. public void SetEndEvent(float normalizedTime = float.NaN, IInvokable callback = null)
  186. {
  187. if (_NormalizedTimes.IsNullOrEmpty())
  188. _NormalizedTimes = new float[] { normalizedTime };
  189. else
  190. _NormalizedTimes[^1] = normalizedTime;
  191. InsertOptionalItem(ref _Callbacks, _NormalizedTimes.Length - 1, callback);
  192. }
  193. /************************************************************************************************************************/
  194. #endregion
  195. /************************************************************************************************************************/
  196. #region Other Events
  197. /************************************************************************************************************************/
  198. /// <summary>Adds an event to the serialized fields.</summary>
  199. public int AddEvent(float normalizedTime, IInvokable callback = null, StringAsset name = null)
  200. {
  201. int index;
  202. if (_NormalizedTimes.IsNullOrEmpty())
  203. {
  204. _NormalizedTimes = new float[] { normalizedTime, float.NaN };
  205. index = 0;
  206. }
  207. else
  208. {
  209. index = _NormalizedTimes.Length - 1;
  210. for (int i = 0; i < _NormalizedTimes.Length - 1; i++)
  211. {
  212. if (_NormalizedTimes[i] > normalizedTime)
  213. {
  214. index = i;
  215. break;
  216. }
  217. }
  218. AnimancerUtilities.InsertAt(ref _NormalizedTimes, index, normalizedTime);
  219. }
  220. InsertOptionalItem(ref _Callbacks, index, callback);
  221. InsertOptionalItem(ref _Names, index, name);
  222. return index;
  223. }
  224. /************************************************************************************************************************/
  225. /// <summary>Inserts an `item` at the specified `index` in an optional `array`.</summary>
  226. /// <remarks>
  227. /// If the `item` is <c>null</c> then the array only needs
  228. /// to be expanded if it was already larger than the `index`.
  229. /// </remarks>
  230. private static void InsertOptionalItem<T>(ref T[] array, int index, T item)
  231. where T : class
  232. {
  233. if (item == null &&
  234. (array == null || array.Length < index))
  235. return;
  236. AnimancerUtilities.InsertAt(ref array, index, item);
  237. }
  238. /************************************************************************************************************************/
  239. /// <summary>Removes an event from the serialized fields.</summary>
  240. public void RemoveEvent(int index)
  241. {
  242. if (_NormalizedTimes.IsNullOrEmpty())
  243. return;
  244. AnimancerUtilities.RemoveAt(ref _NormalizedTimes, index);
  245. if (_Callbacks != null && _Callbacks.Length > index)
  246. AnimancerUtilities.RemoveAt(ref _Callbacks, index);
  247. if (_Names != null && _Names.Length > index)
  248. AnimancerUtilities.RemoveAt(ref _Names, index);
  249. }
  250. /************************************************************************************************************************/
  251. /// <summary>Removes all events.</summary>
  252. public void Clear(bool keepEndEvent = false)
  253. {
  254. if (keepEndEvent)
  255. {
  256. if (_NormalizedTimes != null && _NormalizedTimes.Length > 0)
  257. _NormalizedTimes = new float[] { _NormalizedTimes[^1] };
  258. else
  259. _NormalizedTimes = null;
  260. if (_Callbacks != null && _Callbacks.Length > 0)
  261. _Callbacks = new IInvokable[] { _Callbacks[^1] };
  262. else
  263. _Callbacks = null;
  264. }
  265. else
  266. {
  267. _NormalizedTimes = null;
  268. _Callbacks = null;
  269. }
  270. _Names = null;
  271. }
  272. /************************************************************************************************************************/
  273. #endregion
  274. /************************************************************************************************************************/
  275. #region Copying
  276. /************************************************************************************************************************/
  277. /// <summary>Creates a new <see cref="Serializable"/> and copies the contents of <c>this</c> into it.</summary>
  278. /// <remarks>To copy into an existing sequence, use <see cref="CopyFrom"/> instead.</remarks>
  279. public Serializable Clone()
  280. {
  281. var clone = new Serializable();
  282. clone.CopyFrom(this);
  283. return clone;
  284. }
  285. /// <inheritdoc/>
  286. public Serializable Clone(CloneContext context)
  287. => Clone();
  288. /************************************************************************************************************************/
  289. /// <inheritdoc/>
  290. public void CopyFrom(Serializable copyFrom)
  291. {
  292. if (copyFrom == null)
  293. {
  294. _NormalizedTimes = default;
  295. _Callbacks = default;
  296. _Names = default;
  297. return;
  298. }
  299. AnimancerUtilities.CopyExactArray(copyFrom._NormalizedTimes, ref _NormalizedTimes);
  300. AnimancerUtilities.CopyExactArray(copyFrom._Callbacks, ref _Callbacks);
  301. AnimancerUtilities.CopyExactArray(copyFrom._Names, ref _Names);
  302. }
  303. /************************************************************************************************************************/
  304. #endregion
  305. /************************************************************************************************************************/
  306. #region Serialization
  307. /************************************************************************************************************************/
  308. #if UNITY_EDITOR
  309. /************************************************************************************************************************/
  310. /// <summary>[Editor-Only] Does nothing.</summary>
  311. void ISerializationCallbackReceiver.OnAfterDeserialize() { }
  312. /************************************************************************************************************************/
  313. /// <summary>[Editor-Only] [Internal]
  314. /// Called by <see cref="ISerializationCallbackReceiver.OnBeforeSerialize"/>.
  315. /// </summary>
  316. internal static event Action<Serializable> OnBeforeSerialize;
  317. /// <summary>[Editor-Only] Ensures that the events are sorted by time (excluding the end event).</summary>
  318. void ISerializationCallbackReceiver.OnBeforeSerialize()
  319. => OnBeforeSerialize?.Invoke(this);
  320. /************************************************************************************************************************/
  321. /// <summary>[Editor-Only] [Internal]
  322. /// Should the arrays be prevented from reducing their size when their last elements are unused?
  323. /// </summary>
  324. internal static bool DisableCompactArrays { get; set; }
  325. /// <summary>[Editor-Only] [Internal]
  326. /// Removes empty data from the ends of the arrays to reduce the serialized data size.
  327. /// </summary>
  328. internal void CompactArrays()
  329. {
  330. if (DisableCompactArrays)
  331. return;
  332. // If there is only one time and it is NaN, we don't need to store anything.
  333. if (_NormalizedTimes == null ||
  334. (_NormalizedTimes.Length == 1 &&
  335. (_Callbacks == null || _Callbacks.Length == 0) &&
  336. (_Names == null || _Names.Length == 0) &&
  337. float.IsNaN(_NormalizedTimes[0])))
  338. {
  339. _NormalizedTimes = Array.Empty<float>();
  340. _Callbacks = Array.Empty<IInvokable>();
  341. _Names = Array.Empty<StringAsset>();
  342. return;
  343. }
  344. Trim(ref _Callbacks, _NormalizedTimes.Length, callback => callback != null);
  345. Trim(ref _Names, _NormalizedTimes.Length, name => name != null);
  346. }
  347. /************************************************************************************************************************/
  348. /// <summary>[Editor-Only] Removes unimportant values from the end of the `array`.</summary>
  349. private static void Trim<T>(ref T[] array, int maxLength, Func<T, bool> isImportant)
  350. {
  351. if (array == null)
  352. return;
  353. var count = Math.Min(array.Length, maxLength);
  354. while (count >= 1)
  355. {
  356. var item = array[count - 1];
  357. if (isImportant(item))
  358. break;
  359. else
  360. count--;
  361. }
  362. Array.Resize(ref array, count);
  363. }
  364. /************************************************************************************************************************/
  365. #endif
  366. /************************************************************************************************************************/
  367. #endregion
  368. /************************************************************************************************************************/
  369. }
  370. }
  371. }
  372. }