AnimancerState.Events.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. using System.Runtime.CompilerServices;
  3. using UnityEngine;
  4. namespace Animancer
  5. {
  6. /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerState
  7. partial class AnimancerState
  8. {
  9. /************************************************************************************************************************/
  10. /// <summary>The system which manages the <see cref="SharedEvents"/>.</summary>
  11. private AnimancerEvent.Dispatcher _EventDispatcher;
  12. /************************************************************************************************************************/
  13. /// <summary>
  14. /// Events which will be triggered while this state plays
  15. /// based on its <see cref="NormalizedTime"/>.
  16. /// </summary>
  17. ///
  18. /// <remarks>
  19. /// This property tries to ensure that the event sequence is only referenced by this state.
  20. /// <list type="bullet">
  21. /// <item>
  22. /// If the reference was <c>null</c>,
  23. /// a new sequence will be created.
  24. /// </item>
  25. /// <item>
  26. /// If a reference was assigned to <see cref="SharedEvents"/>,
  27. /// it will be cloned so this state owns the clone.
  28. /// </item>
  29. /// </list>
  30. /// <para></para>
  31. /// Using <see cref="Events(object)"/> or <see cref="Events(object, out AnimancerEvent.Sequence)"/>
  32. /// is often safer than this property since they help detect if multiple scripts are using the same
  33. /// state which could lead to unexpected bugs if they each assign conflicting callbacks.
  34. /// <para></para>
  35. /// <strong>Documentation:</strong>
  36. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">
  37. /// Animancer Events</see>
  38. /// </remarks>
  39. public AnimancerEvent.Sequence OwnedEvents
  40. {
  41. get
  42. {
  43. _EventDispatcher ??= new(this);
  44. _EventDispatcher.InitializeEvents(out var events);
  45. return events;
  46. }
  47. set
  48. {
  49. if (value != null)
  50. (_EventDispatcher ??= new(this)).SetEvents(value, true);
  51. else
  52. _EventDispatcher = null;
  53. }
  54. }
  55. /************************************************************************************************************************/
  56. /// <summary>
  57. /// Events which will be triggered while this state plays
  58. /// based on its <see cref="NormalizedTime"/>.
  59. /// </summary>
  60. ///
  61. /// <remarks>
  62. /// This reference is <c>null</c> by default and once assigned it may be shared by multiple states.
  63. /// <para></para>
  64. /// <strong>Documentation:</strong>
  65. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">
  66. /// Animancer Events</see>
  67. /// </remarks>
  68. public AnimancerEvent.Sequence SharedEvents
  69. {
  70. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  71. get => _EventDispatcher?.Events;
  72. set
  73. {
  74. if (value != null)
  75. (_EventDispatcher ??= new(this)).SetEvents(value, false);
  76. else
  77. _EventDispatcher = null;
  78. }
  79. }
  80. /************************************************************************************************************************/
  81. /// <summary>Have the <see cref="SharedEvents"/> or <see cref="OwnedEvents"/> been initialized?</summary>
  82. /// <remarks>
  83. /// <strong>Documentation:</strong>
  84. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">
  85. /// Animancer Events</see>
  86. /// </remarks>
  87. public bool HasEvents
  88. => _EventDispatcher != null;
  89. /************************************************************************************************************************/
  90. /// <summary>Have the <see cref="OwnedEvents"/> been initialized?</summary>
  91. /// <remarks>
  92. /// <strong>Documentation:</strong>
  93. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">
  94. /// Animancer Events</see>
  95. /// </remarks>
  96. public bool HasOwnedEvents
  97. => _EventDispatcher != null
  98. && _EventDispatcher.HasOwnEvents;
  99. /************************************************************************************************************************/
  100. /// <summary>
  101. /// If the <see cref="OwnedEvents"/> haven't been initialized yet,
  102. /// this method gets them and returns <c>true</c>.
  103. /// </summary>
  104. ///
  105. /// <remarks>
  106. /// This method tries to ensure that the event sequence is only referenced by this state.
  107. /// <list type="bullet">
  108. /// <item>
  109. /// If the reference was <c>null</c>,
  110. /// a new sequence will be created.
  111. /// </item>
  112. /// <item>
  113. /// If a reference was assigned to <see cref="SharedEvents"/>,
  114. /// it will be cloned so this state owns the clone.
  115. /// </item>
  116. /// </list>
  117. /// In both of those cases, this method returns <c>true</c>
  118. /// to indicate that the caller should initialize their event callbacks.
  119. /// <para></para>
  120. /// Also calls <see cref="AssertOwnership"/>.
  121. /// <para></para>
  122. /// <strong>Documentation:</strong>
  123. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">
  124. /// Animancer Events</see>
  125. /// <para></para>
  126. /// <strong>Example:</strong>
  127. /// <code>
  128. /// public static readonly StringReference EventName = "Event Name";
  129. ///
  130. /// ...
  131. ///
  132. /// AnimancerState state = animancerComponent.Play(animation);
  133. /// if (state.Events(this, out AnimancerEvent.Sequence events))
  134. /// {
  135. /// events.SetCallback(EventName, OnAnimationEvent);
  136. /// events.OnEnd = OnAnimationEnded;
  137. /// }
  138. /// </code>
  139. /// If you only need to initialize the End Event,
  140. /// consider using <see cref="Events(object)"/> instead.
  141. /// </remarks>
  142. public bool Events(object owner, out AnimancerEvent.Sequence events)
  143. {
  144. AssertOwnership(owner);
  145. _EventDispatcher ??= new(this);
  146. return _EventDispatcher.InitializeEvents(out events);
  147. }
  148. /************************************************************************************************************************/
  149. /// <summary>
  150. /// If the <see cref="OwnedEvents"/> haven't been initialized yet,
  151. /// this method gets them and returns <c>true</c>.
  152. /// </summary>
  153. ///
  154. /// <remarks>
  155. /// This method tries to ensure that the event sequence is only referenced by this state.
  156. /// <list type="bullet">
  157. /// <item>
  158. /// If the reference was <c>null</c>,
  159. /// a new sequence will be created.
  160. /// </item>
  161. /// <item>
  162. /// If a reference was assigned to <see cref="SharedEvents"/>,
  163. /// it will be cloned so this state owns the clone.
  164. /// </item>
  165. /// </list>
  166. /// <para></para>
  167. /// Also calls <see cref="AssertOwnership"/>.
  168. /// <para></para>
  169. /// <strong>Documentation:</strong>
  170. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">
  171. /// Animancer Events</see>
  172. /// <para></para>
  173. /// <strong>Example:</strong>
  174. /// <code>
  175. /// AnimancerState state = animancerComponent.Play(animation);
  176. /// state.Events(this).OnEnd ??= OnAnimationEnded;
  177. /// </code>
  178. /// If you need to initialize more than just the End Event,
  179. /// consider using <see cref="Events(object, out AnimancerEvent.Sequence)"/> instead.
  180. /// </remarks>
  181. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  182. public AnimancerEvent.Sequence Events(object owner)
  183. {
  184. Events(owner, out var events);
  185. return events;
  186. }
  187. /************************************************************************************************************************/
  188. /// <summary>Copies the contents of the <see cref="_EventDispatcher"/>.</summary>
  189. private void CopyEvents(AnimancerState copyFrom, CloneContext context)
  190. {
  191. if (copyFrom._EventDispatcher != null)
  192. {
  193. var original = copyFrom._EventDispatcher.Events;
  194. var events = context.GetCloneOrOriginal(original);
  195. if (events != null)
  196. {
  197. _EventDispatcher ??= new(this);
  198. _EventDispatcher.SetEvents(events, false);
  199. if (events == original)
  200. copyFrom._EventDispatcher.DismissEventOwnership();
  201. return;
  202. }
  203. }
  204. _EventDispatcher = null;
  205. }
  206. /************************************************************************************************************************/
  207. /// <summary>Should events be raised on a state which is currently fading out?</summary>
  208. /// <remarks>
  209. /// Default <c>false</c>.
  210. /// <para></para>
  211. /// <strong>Documentation:</strong>
  212. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">
  213. /// Animancer Events</see>
  214. /// </remarks>
  215. public static bool RaiseEventsDuringFadeOut { get; set; }
  216. /// <summary>[Internal] Should this state check for events to invoke?</summary>
  217. internal bool ShouldRaiseEvents
  218. => TargetWeight > 0
  219. || RaiseEventsDuringFadeOut;
  220. /************************************************************************************************************************/
  221. /// <summary>
  222. /// Checks if any events should be invoked based on the current time of this state.
  223. /// </summary>
  224. protected internal virtual void UpdateEvents()
  225. => _EventDispatcher?.UpdateEvents(ShouldRaiseEvents);
  226. /// <summary>
  227. /// Checks if any events should be invoked on the `parent` and its children recursively.
  228. /// </summary>
  229. public static void UpdateEventsRecursive(AnimancerState parent)
  230. => UpdateEventsRecursive(
  231. parent,
  232. parent.ShouldRaiseEvents);
  233. /// <summary>
  234. /// Checks if any events should be invoked on the `parent` and its children recursively.
  235. /// </summary>
  236. public static void UpdateEventsRecursive(AnimancerState parent, bool raiseEvents)
  237. {
  238. parent._EventDispatcher?.UpdateEvents(raiseEvents);
  239. for (int i = parent.ChildCount - 1; i >= 0; i--)
  240. UpdateEventsRecursive(parent.GetChild(i), raiseEvents);
  241. }
  242. /************************************************************************************************************************/
  243. #if UNITY_ASSERTIONS
  244. /************************************************************************************************************************/
  245. /// <summary>[Assert-Only]
  246. /// Returns <c>null</c> if Animancer Events will work properly on this type of state,
  247. /// or a message explaining why they might not work.
  248. /// </summary>
  249. protected internal virtual string UnsupportedEventsMessage
  250. => null;
  251. /************************************************************************************************************************/
  252. /// <summary>[Assert-Only] An optional reference to the object that owns this state.</summary>
  253. public object Owner { get; private set; }
  254. /************************************************************************************************************************/
  255. #endif
  256. /************************************************************************************************************************/
  257. /// <summary>[Assert-Conditional]
  258. /// Sets the <see cref="Owner"/> and asserts that it wasn't already set to a different object.
  259. /// </summary>
  260. /// <remarks>This helps detect if multiple scripts attempt to manage the same state.</remarks>
  261. [System.Diagnostics.Conditional(Strings.Assertions)]
  262. public void AssertOwnership(object owner)
  263. {
  264. #if UNITY_ASSERTIONS
  265. if (Owner == owner)
  266. return;
  267. if (Owner != null)
  268. {
  269. Debug.LogError(
  270. $"Multiple objects have asserted ownership over the state '{ToString()}':" +
  271. $"\n• Old Owner: {AnimancerUtilities.ToStringOrNull(Owner)}" +
  272. $"\n• New Owner: {AnimancerUtilities.ToStringOrNull(owner)}" +
  273. $"\n• State: {GetPath()}" +
  274. $"\n• Graph: {Graph?.GetDescription("\n• ")}",
  275. Graph?.Component as Object);
  276. }
  277. Owner = owner;
  278. #endif
  279. }
  280. /************************************************************************************************************************/
  281. }
  282. }