OptionalWarning.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using System;
  3. using UnityEngine;
  4. using UnityEngine.Playables;
  5. using Object = UnityEngine.Object;
  6. namespace Animancer
  7. {
  8. /// <summary>
  9. /// Bitwise flags used by <see cref="Validate.IsEnabled"/> and <see cref="Validate.Disable"/> to determine which
  10. /// warnings Animancer should give.
  11. /// <para></para>
  12. /// <strong>These warnings are all optional</strong>. Feel free to disable any of them if you understand the
  13. /// <em>potential</em> issues they are referring to.
  14. /// </summary>
  15. ///
  16. /// <remarks>
  17. /// All warnings are enabled by default, but are compiled out of runtime builds (except development builds).
  18. /// <para></para>
  19. /// You can manually disable warnings using the Settings in the <see cref="Editor.Tools.AnimancerToolsWindow"/>
  20. /// (<c>Window/Animation/Animancer Tools</c>).
  21. /// </remarks>
  22. ///
  23. /// <example>
  24. /// You can put the following method in any class to disable whatever warnings you don't want on startup:
  25. /// <para></para><code>
  26. /// #if UNITY_ASSERTIONS
  27. /// [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)]
  28. /// private static void DisableAnimancerWarnings()
  29. /// {
  30. /// Animancer.OptionalWarning.ProOnly.Disable();
  31. ///
  32. /// // You could disable OptionalWarning.All, but that is not recommended for obvious reasons.
  33. /// }
  34. /// #endif
  35. /// </code></example>
  36. /// https://kybernetik.com.au/animancer/api/Animancer/OptionalWarning
  37. ///
  38. [Flags]
  39. public enum OptionalWarning
  40. {
  41. /// <summary>
  42. /// A <see href="https://kybernetik.com.au/animancer/docs/introduction/features">Pro-Only Feature</see> has been
  43. /// used in <see href="https://kybernetik.com.au/animancer/lite">Animancer Lite</see>.
  44. /// </summary>
  45. ///
  46. /// <remarks>
  47. /// Some <see href="https://kybernetik.com.au/animancer/docs/introduction/features">Features</see> are only
  48. /// available in <see href="https://kybernetik.com.au/animancer/pro">Animancer Pro</see>.
  49. /// <para></para>
  50. /// <see href="https://kybernetik.com.au/animancer/lite">Animancer Lite</see> allows you to try out those
  51. /// features in the Unity Editor and gives this warning the first time each one is used to inform you that they
  52. /// will not work in runtime builds.
  53. /// </remarks>
  54. ProOnly = 1 << 0,
  55. /// <summary>
  56. /// An <see cref="AnimancerComponent.Playable"/> is being initialized while its <see cref="GameObject"/> is
  57. /// inactive.
  58. /// </summary>
  59. ///
  60. /// <remarks>
  61. /// Unity will not call <see cref="AnimancerComponent.OnDestroy"/> if the <see cref="GameObject"/> is never
  62. /// enabled. That would prevent it from destroying the internal <see cref="PlayableGraph"/>, leading to a
  63. /// memory leak.
  64. /// <para></para>
  65. /// Animations usually shouldn't be played on inactive objects so you most likely just need to call
  66. /// <see cref="GameObject.SetActive(bool)"/> first.
  67. /// <para></para>
  68. /// If you do intend to use it while inactive, you will need to disable this warning and call
  69. /// <see cref="AnimancerComponent.OnDestroy"/> manually when the object is destroyed (such as when its scene is
  70. /// unloaded).
  71. /// </remarks>
  72. CreateGraphWhileDisabled = 1 << 1,
  73. /// <summary>
  74. /// An <see cref="AnimancerComponent.Playable"/> is being initialized during a type of GUI event that shouldn't
  75. /// cause side effects.
  76. /// </summary>
  77. ///
  78. /// <remarks>
  79. /// <see cref="EventType.Layout"/> and <see cref="EventType.Repaint"/> should display the current details of
  80. /// things, but they should not modify things.
  81. /// </remarks>
  82. CreateGraphDuringGuiEvent = 1 << 2,
  83. /// <summary>
  84. /// An <see cref="Animator.runtimeAnimatorController"/> is assigned but the Rig is Humanoid so it can't be
  85. /// blended with Animancer.
  86. /// </summary>
  87. ///
  88. /// <remarks>
  89. /// <see href="https://kybernetik.com.au/animancer/docs/manual/animator-controllers#native">Native</see>
  90. /// Animator Controllers can blend with Animancer on Generic Rigs, but not on Humanoid Rigs (you can swap back
  91. /// and forth between the Animator Controller and Animancer, but it won't smoothly blend between them).
  92. /// <para></para>
  93. /// If you don't intend to blend between them, you can just disable this warning.
  94. /// </remarks>
  95. NativeControllerHumanoid = 1 << 3,
  96. /// <summary>
  97. /// An <see cref="Animator.runtimeAnimatorController"/> is assigned while also using a
  98. /// <see cref="HybridAnimancerComponent"/>.
  99. /// </summary>
  100. ///
  101. /// <remarks>
  102. /// Either assign the <see cref="Animator.runtimeAnimatorController"/> to use it as a Native Animator
  103. /// Controller or assign the <see cref="HybridAnimancerComponent.Controller"/> to use it as a Hybrid Animator
  104. /// Controller. The differences are explained in the
  105. /// <see href="https://kybernetik.com.au/animancer/docs/manual/animator-controllers">Documentation</see>
  106. /// <para></para>
  107. /// It is possible to use both, but it usually only happens when misunderstanding how the system works. If you
  108. /// do want both, just disable this warning.
  109. /// </remarks>
  110. NativeControllerHybrid = 1 << 4,
  111. /// <summary>
  112. /// An <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Event</see> is
  113. /// being added to an <see cref="AnimancerEvent.Sequence"/> which already contains an identical event.
  114. /// </summary>
  115. ///
  116. /// <remarks>
  117. /// This warning often occurs due to a misunderstanding about the way events are
  118. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer#auto-clear">Automatically
  119. /// Cleared</see>.
  120. /// <para></para>
  121. /// If you play an <see cref="AnimationClip"/>, its <see cref="AnimancerState.Events"/> will be empty so you
  122. /// can add whatever events you want.
  123. /// <para></para>
  124. /// But <see href="https://kybernetik.com.au/animancer/docs/manual/transitions">Transitions</see> store their own
  125. /// events, so if you play one then modify its <see cref="AnimancerState.Events"/> you are actually modifying
  126. /// the transition's events. Then if you play the same transition again, you will modify the events again,
  127. /// often leading to the same event being added multiple times.
  128. /// <para></para>
  129. /// If that is not the case, you can simply disable this warning. There is nothing inherently wrong with having
  130. /// multiple identical events in the same sequence.
  131. /// </remarks>
  132. DuplicateEvent = 1 << 5,
  133. /// <summary>
  134. /// An <see href="https://kybernetik.com.au/animancer/docs/manual/events/end">End Event</see> did not actually
  135. /// end the animation.
  136. /// </summary>
  137. ///
  138. /// <remarks>
  139. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/end">End Events</see> are triggered every
  140. /// frame after their time has passed, so in this case it might be necessary to explicitly clear the event or
  141. /// simply use a regular <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Event</see>.
  142. /// <para></para>
  143. /// If you intend for the event to keep getting triggered, you can just disable this warning.
  144. /// </remarks>
  145. EndEventInterrupt = 1 << 6,
  146. /// <summary>
  147. /// An <see cref="AnimancerEvent"/> that does nothing was invoked. Most likely it was not configured correctly.
  148. /// </summary>
  149. ///
  150. /// <remarks>
  151. /// Unused events should be removed to avoid wasting performance checking and invoking them.
  152. /// </remarks>
  153. UselessEvent = 1 << 7,
  154. /// <summary>
  155. /// An <see cref="AnimancerEvent.Sequence"/> is being modified even though its
  156. /// <see cref="AnimancerEvent.Sequence.ShouldNotModifyReason"/> is set.
  157. /// </summary>
  158. ///
  159. /// <remarks>
  160. /// This is primarily used by transitions. Their events should generally be configured on startup rather
  161. /// than repeating the setup on the state after the transition is played because such modifications will apply
  162. /// back to the transition's events (which is usually not intended).
  163. /// </remarks>
  164. LockedEvents = 1 << 8,
  165. /// <summary>
  166. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Events</see> are
  167. /// being used on a state that does not properly support them so they might not work as intended.
  168. /// </summary>
  169. ///
  170. /// <remarks>
  171. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Events</see> on a
  172. /// <see cref="ControllerState"/> will be triggered based on its <see cref="AnimancerState.NormalizedTime"/>,
  173. /// which comes from the current state of its Animator Controller regardless of which state that may be.
  174. /// <para></para>
  175. /// If you intend for the event to be associated with a specific state inside the Animator Controller, you need
  176. /// to use <see href="https://kybernetik.com.au/animancer/docs/manual/events/animation">Animation Events</see>
  177. /// instead.
  178. /// <para></para>
  179. /// But if you intend the event to be triggered by any state inside the Animator Controller, then you can
  180. /// simply disable this warning.
  181. /// </remarks>
  182. UnsupportedEvents = 1 << 9,
  183. /// <summary><see cref="AnimancerNode.Speed"/> is being used on a state that doesn't support it.</summary>
  184. ///
  185. /// <remarks>
  186. /// <see cref="PlayableExtensions.SetSpeed"/> does nothing on <see cref="ControllerState"/>s so there is no
  187. /// way to directly control their speed. The
  188. /// <see href="https://kybernetik.com.au/animancer/docs/bugs/animator-controller-speed">Animator Controller Speed</see>
  189. /// page explains a possible workaround for this issue.
  190. /// <para></para>
  191. /// The only reason you would disable this warning is if you are setting the speed of states in general and
  192. /// not depending on it to actually take effect.
  193. /// </remarks>
  194. UnsupportedSpeed = 1 << 10,
  195. /// <summary>
  196. /// <see href="https://kybernetik.com.au/animancer/docs/manual/ik">Inverse Kinematics</see> cannot be
  197. /// dynamically enabled on some <see href="https://kybernetik.com.au/animancer/docs/manual/playing/states">States</see>
  198. /// Types.
  199. /// </summary>
  200. ///
  201. /// <remarks>
  202. /// To use IK on a <see cref="ControllerState"/> you must instead enable it on the desired layer inside the
  203. /// Animator Controller.
  204. /// <para></para>
  205. /// IK is not supported by <see cref="PlayableAssetState"/>.
  206. /// <para></para>
  207. /// Setting <see cref="AnimancerNode.ApplyAnimatorIK"/> on such a state will simply do nothing, so feel free to
  208. /// disable this warning if you are enabling IK on states without checking their type.
  209. /// </remarks>
  210. UnsupportedIK = 1 << 11,
  211. /// <summary>
  212. /// A <see cref="MixerState"/> is being initialized with its <see cref="AnimancerNode.ChildCount"/> &lt;= 1.
  213. /// </summary>
  214. ///
  215. /// <remarks>
  216. /// The purpose of a mixer is to mix multiple child states so you are probably initialising it with incorrect
  217. /// parameters.
  218. /// <para></para>
  219. /// A mixer with only one child will simply play that child, so feel free to disable this warning if that is
  220. /// what you intend to do.
  221. /// </remarks>
  222. MixerMinChildren = 1 << 12,
  223. /// <summary>
  224. /// A <see cref="MixerState"/> is synchronizing a child with <see cref="AnimancerState.Length"/> = 0.
  225. /// </summary>
  226. ///
  227. /// <remarks>
  228. /// Synchronization is based on the <see cref="AnimancerState.NormalizedTime"/> which can't be calculated if
  229. /// the <see cref="AnimancerState.Length"/> is 0.
  230. /// <para></para>
  231. /// Some state types can change their <see cref="AnimancerState.Length"/>, in which case you can just disable
  232. /// this warning. But otherwise, the indicated state should not be added to the synchronization list.
  233. /// </remarks>
  234. MixerSynchronizeZeroLength = 1 << 13,
  235. /// <summary>
  236. /// A <see href="https://kybernetik.com.au/animancer/docs/manual/blending/fading#custom-fade">Custom Fade</see>
  237. /// is being started but its weight calculation does not go from 0 to 1.
  238. /// </summary>
  239. ///
  240. /// <remarks>
  241. /// The <see cref="CustomFade.CalculateWeight"/> method is expected to return 0 when the parameter is 0 and
  242. /// 1 when the parameter is 1. It can do anything you want with other values, but violating that guideline will
  243. /// trigger this warning because it would likely lead to undesirable results.
  244. /// <para></para>
  245. /// If your <see cref="CustomFade.CalculateWeight"/> method is expensive you could disable this warning to save
  246. /// some performance, but violating the above guidelines is not recommended.
  247. /// </remarks>
  248. CustomFadeBounds = 1 << 14,
  249. /// <summary>
  250. /// A weight calculation method was not specified when attempting to start a
  251. /// <see href="https://kybernetik.com.au/animancer/docs/manual/blending/fading#custom-fade">Custom Fade</see>.
  252. /// </summary>
  253. ///
  254. /// <remarks>
  255. /// Passing a <c>null</c> parameter into <see cref="CustomFade.Apply(AnimancerState, AnimationCurve)"/> and
  256. /// other similar methods will trigger this warning and return <c>null</c> because a <see cref="CustomFade"/>
  257. /// serves no purpose if it doesn't have a method for calculating the weight.
  258. /// </remarks>
  259. CustomFadeNotNull = 1 << 15,
  260. /// <summary>
  261. /// The <see cref="Animator.speed"/> property does not affect Animancer.
  262. /// Use <see cref="AnimancerPlayable.Speed"/> instead.
  263. /// </summary>
  264. ///
  265. /// <remarks>
  266. /// The <see cref="Animator.speed"/> property only works with Animator Controllers but does not affect the
  267. /// Playables API so Animancer has its own <see cref="AnimancerPlayable.Speed"/> property.
  268. /// </remarks>
  269. AnimatorSpeed = 1 << 16,
  270. /// <summary>An <see cref="AnimancerNode.Root"/> is null during finalization (garbage collection).</summary>
  271. /// <remarks>
  272. /// This probably means that node was never used for anything and should not have been created.
  273. /// <para></para>
  274. /// This warning can be prevented for a specific node by passing it into <see cref="GC.SuppressFinalize"/>.
  275. /// <para></para>
  276. /// To minimise the performance cost of checking this warning, it does not capture the stack trace of the
  277. /// node's creation by default. However, you can enable <see cref="AnimancerNode.TraceConstructor"/> on startup
  278. /// so that it can include the stack trace in the warning message for any nodes that end up being unused.
  279. /// </remarks>
  280. UnusedNode = 1 << 17,
  281. /// <summary>
  282. /// <see cref="PlayableAssetState.InitializeBindings"/> is trying to bind to the same <see cref="Animator"/>
  283. /// that is being used by Animancer.
  284. /// </summary>
  285. /// <remarks>
  286. /// Doing this will replace Animancer's output so its animations would not work anymore.
  287. /// </remarks>
  288. PlayableAssetAnimatorBinding = 1 << 18,
  289. /// <summary>
  290. /// <see cref="AnimancerLayer.GetOrCreateWeightlessState"/> has created too many clones of a particular state.
  291. /// </summary>
  292. /// <remarks>
  293. /// <see cref="AnimancerLayer.SetMaxStateDepth"/> can be used to increase the allowed number of clones.
  294. /// <para></para>
  295. /// The <see href="https://kybernetik.com.au/animancer/docs/manual/blending/fading/modes">Fade Modes</see> page
  296. /// explains how this system works in more detail.
  297. /// </remarks>
  298. MaxStateDepth = 1 << 19,
  299. /// <summary>All warning types.</summary>
  300. All = ~0,
  301. }
  302. /// https://kybernetik.com.au/animancer/api/Animancer/Validate
  303. public static partial class Validate
  304. {
  305. /************************************************************************************************************************/
  306. #if UNITY_ASSERTIONS
  307. /// <summary>[Assert-Only] The <see cref="OptionalWarning"/> flags that are currently disabled (default none).</summary>
  308. private static OptionalWarning _DisabledWarnings;
  309. #endif
  310. /************************************************************************************************************************/
  311. /// <summary>[Animancer Extension] [Assert-Conditional]
  312. /// Disables the specified warning type. Supports bitwise combinations.
  313. /// </summary>
  314. /// <example>
  315. /// You can put the following method in any class to disable whatever warnings you don't want on startup:
  316. /// <para></para><code>
  317. /// #if UNITY_ASSERTIONS
  318. /// [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)]
  319. /// private static void DisableAnimancerWarnings()
  320. /// {
  321. /// Animancer.OptionalWarning.EndEventInterrupt.Disable();
  322. ///
  323. /// // You could disable OptionalWarning.All, but that is not recommended for obvious reasons.
  324. /// }
  325. /// #endif
  326. /// </code></example>
  327. [System.Diagnostics.Conditional(Strings.Assertions)]
  328. public static void Disable(this OptionalWarning type)
  329. {
  330. #if UNITY_ASSERTIONS
  331. _DisabledWarnings |= type;
  332. #endif
  333. }
  334. /************************************************************************************************************************/
  335. /// <summary>[Animancer Extension] [Assert-Conditional]
  336. /// Enables the specified warning type. Supports bitwise combinations.
  337. /// </summary>
  338. [System.Diagnostics.Conditional(Strings.Assertions)]
  339. public static void Enable(this OptionalWarning type)
  340. {
  341. #if UNITY_ASSERTIONS
  342. _DisabledWarnings &= ~type;
  343. #endif
  344. }
  345. /************************************************************************************************************************/
  346. /// <summary>[Animancer Extension] [Assert-Conditional]
  347. /// Enables or disables the specified warning type. Supports bitwise combinations.
  348. /// </summary>
  349. [System.Diagnostics.Conditional(Strings.Assertions)]
  350. public static void SetEnabled(this OptionalWarning type, bool enable)
  351. {
  352. #if UNITY_ASSERTIONS
  353. if (enable)
  354. type.Enable();
  355. else
  356. type.Disable();
  357. #endif
  358. }
  359. /************************************************************************************************************************/
  360. /// <summary>[Animancer Extension] [Assert-Conditional]
  361. /// Logs the `message` as a warning if the `type` is enabled.
  362. /// </summary>
  363. [System.Diagnostics.Conditional(Strings.Assertions)]
  364. public static void Log(this OptionalWarning type, string message, object context = null)
  365. {
  366. #if UNITY_ASSERTIONS
  367. if (message == null || type.IsDisabled())
  368. return;
  369. Debug.LogWarning($"Possible Bug Detected: {message}\n\nThis warning can be disabled via the " +
  370. $"Settings in '{Strings.AnimancerToolsMenuPath}'" +
  371. $" or by calling {nameof(Animancer)}.{nameof(OptionalWarning)}.{type}.{nameof(Disable)}()" +
  372. " and it will automatically be compiled out of Runtime Builds (except for Development Builds)." +
  373. $" More information can be found at {Strings.DocsURLs.OptionalWarning}\n",
  374. context as Object);
  375. #endif
  376. }
  377. /************************************************************************************************************************/
  378. #if UNITY_ASSERTIONS
  379. /************************************************************************************************************************/
  380. /// <summary>[Animancer Extension] [Assert-Only] Are none of the specified warning types disabled?</summary>
  381. public static bool IsEnabled(this OptionalWarning type) => (_DisabledWarnings & type) == 0;
  382. /************************************************************************************************************************/
  383. /// <summary>[Animancer Extension] [Assert-Only] Are all of the specified warning types disabled?</summary>
  384. public static bool IsDisabled(this OptionalWarning type) => (_DisabledWarnings & type) == type;
  385. /************************************************************************************************************************/
  386. /// <summary>[Animancer Extension] [Assert-Only]
  387. /// Disables the specified warnings and returns those that were previously enabled.
  388. /// </summary>
  389. /// <remarks>Call <see cref="Enable"/> on the returned value to re-enable it.</remarks>
  390. public static OptionalWarning DisableTemporarily(this OptionalWarning type)
  391. {
  392. var previous = _DisabledWarnings;
  393. type.Disable();
  394. return ~previous & type;
  395. }
  396. /************************************************************************************************************************/
  397. private const string PermanentlyDisabledWarningsKey = nameof(Animancer) + "." + nameof(PermanentlyDisabledWarnings);
  398. /// <summary>[Assert-Only] Warnings that are automatically disabled and stored in <see cref="PlayerPrefs"/>.</summary>
  399. public static OptionalWarning PermanentlyDisabledWarnings
  400. {
  401. #if NO_RUNTIME_PLAYER_PREFS && ! UNITY_EDITOR
  402. get => default;
  403. set
  404. {
  405. _DisabledWarnings = value;
  406. }
  407. #else
  408. get => (OptionalWarning)PlayerPrefs.GetInt(PermanentlyDisabledWarningsKey);
  409. set
  410. {
  411. _DisabledWarnings = value;
  412. PlayerPrefs.SetInt(PermanentlyDisabledWarningsKey, (int)value);
  413. }
  414. #endif
  415. }
  416. #if UNITY_EDITOR
  417. [UnityEditor.InitializeOnLoadMethod]
  418. #endif
  419. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
  420. private static void InitializePermanentlyDisabledWarnings()
  421. {
  422. _DisabledWarnings |= PermanentlyDisabledWarnings;
  423. }
  424. /************************************************************************************************************************/
  425. #endif
  426. /************************************************************************************************************************/
  427. }
  428. }