OptionalWarning.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. using System;
  3. using UnityEngine;
  4. using Object = UnityEngine.Object;
  5. namespace Animancer
  6. {
  7. /// <summary>
  8. /// Bitwise flags used to determine which warnings Animancer should give.
  9. /// <para></para>
  10. /// <strong>These warnings are all optional</strong>.
  11. /// Feel free to disable any of them if you understand the <em>potential</em> issues they're referring to.
  12. /// </summary>
  13. ///
  14. /// <remarks>
  15. /// All warnings are enabled by default, but are compiled out of runtime builds (except development builds).
  16. /// <para></para>
  17. /// You can manually disable warnings using the <c>AnimancerSettings</c> asset
  18. /// or the Animancer Settings panel in the Animancer Tools Window (<c>Window/Animation/Animancer Tools</c>).
  19. /// <para></para>
  20. /// <strong>Example:</strong>
  21. /// You can put a method like this in any class to disable whatever warnings you don't want on startup:
  22. /// <para></para><code>
  23. /// #if UNITY_ASSERTIONS
  24. /// [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)]
  25. /// private static void DisableAnimancerWarnings()
  26. /// {
  27. /// Animancer.OptionalWarning.ProOnly.Disable();
  28. ///
  29. /// // You could disable OptionalWarning.All, but that's not recommended for obvious reasons.
  30. /// }
  31. /// #endif
  32. /// </code></remarks>
  33. ///
  34. /// https://kybernetik.com.au/animancer/api/Animancer/OptionalWarning
  35. ///
  36. [Flags]
  37. public enum OptionalWarning
  38. {
  39. /// <summary><c>default</c></summary>
  40. None = 0,
  41. /// <summary>
  42. /// A <see href="https://kybernetik.com.au/animancer/docs/introduction/features">Pro-Only Feature</see>
  43. /// has been used in <see href="https://kybernetik.com.au/animancer/redirect/lite">Animancer Lite</see>.
  44. /// </summary>
  45. ///
  46. /// <remarks>
  47. /// Some <see href="https://kybernetik.com.au/animancer/docs/introduction/features">Features</see>
  48. /// are only available in <see href="https://kybernetik.com.au/animancer/redirect/pro">Animancer Pro</see>.
  49. /// <para></para>
  50. /// <see href="https://kybernetik.com.au/animancer/redirect/lite">Animancer Lite</see>
  51. /// allows you to try out those features in the Unity Editor and gives this warning the
  52. /// first time each one is used to inform you that they will not work in runtime builds.
  53. /// </remarks>
  54. ProOnly = 1 << 0,
  55. /// <summary>
  56. /// An <see cref="AnimancerComponent.Graph"/> is being initialized
  57. /// during a type of GUI event that isn't supposed to cause side effects.
  58. /// </summary>
  59. ///
  60. /// <remarks>
  61. /// <see cref="EventType.Layout"/> and <see cref="EventType.Repaint"/>
  62. /// should display the current details of things, but they should not modify things.
  63. /// </remarks>
  64. CreateGraphDuringGuiEvent = 1 << 1,
  65. /// <summary>
  66. /// The <see cref="AnimancerComponent.Animator"/> is disabled so Animancer won't be able to play animations.
  67. /// </summary>
  68. ///
  69. /// <remarks>
  70. /// The <see cref="Animator"/> doesn't need an Animator Controller,
  71. /// it just needs to be enabled via the checkbox in the Inspector
  72. /// or by setting <c>animancerComponent.Animator.enabled = true;</c> in code.
  73. /// </remarks>
  74. AnimatorDisabled = 1 << 2,
  75. /// <summary>
  76. /// An <see cref="Animator.runtimeAnimatorController"/> is assigned
  77. /// but the Rig is Humanoid so it can't be blended with Animancer.
  78. /// </summary>
  79. ///
  80. /// <remarks>
  81. /// <see href="https://kybernetik.com.au/animancer/docs/manual/animator-controllers#native">Native</see>
  82. /// Animator Controllers can blend with Animancer on Generic Rigs, but not on Humanoid Rigs
  83. /// (you can swap back and forth between the Animator Controller and Animancer,
  84. /// but it won't smoothly blend between them).
  85. /// <para></para>
  86. /// If you don't intend to blend between them, you can just disable this warning.
  87. /// </remarks>
  88. NativeControllerHumanoid = 1 << 3,
  89. /// <summary>
  90. /// An <see cref="Animator.runtimeAnimatorController"/> is assigned while also using a
  91. /// <see cref="HybridAnimancerComponent"/>.
  92. /// </summary>
  93. ///
  94. /// <remarks>
  95. /// Either assign the <see cref="Animator.runtimeAnimatorController"/> to use it as a Native Animator
  96. /// Controller or assign the <see cref="HybridAnimancerComponent.Controller"/> to use it as a Hybrid Animator
  97. /// Controller. The differences are explained on the
  98. /// <see href="https://kybernetik.com.au/animancer/docs/manual/animator-controllers">Animator Controllers</see>
  99. /// page.
  100. /// <para></para>
  101. /// It is possible to use both, but it usually only happens when misunderstanding how the system works.
  102. /// If you do want both, just disable this warning.
  103. /// </remarks>
  104. NativeControllerHybrid = 1 << 4,
  105. /// <summary>
  106. /// An <see href="https://kybernetik.com.au/animancer/docs/manual/events/end">End Event</see>
  107. /// didn't actually end the animation.
  108. /// </summary>
  109. ///
  110. /// <remarks>
  111. /// Animancer doesn't automatically do anything during an End Event
  112. /// so it's up to you to end the animation (usually by playing something else).
  113. /// <para></para>
  114. /// This warning is given when the event isn't used to stop the animation that caused it
  115. /// (usually by playing something else). This often indicates that the event hasn't been
  116. /// configured correctly, however it is sometimes intentional such as if the event doesn't
  117. /// immediately stop the animation but sets a flag to indicate the animation has ended for
  118. /// another system to act on at a later time. In that case this warning should be disabled.
  119. /// <para></para>
  120. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/end">End Events</see>
  121. /// are triggered every frame after their time has passed, so in this case it might be
  122. /// necessary to clear the event or simply use a regular
  123. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Event</see>.
  124. /// </remarks>
  125. EndEventInterrupt = 1 << 5,
  126. /// <summary>
  127. /// An <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Event</see>
  128. /// triggered by one character was used to play an animation on a different character.
  129. /// </summary>
  130. ///
  131. /// <remarks>
  132. /// This most commonly happens when a Transition is shared by multiple characters and they
  133. /// all register their own callbacks to its events which leads to those events controlling
  134. /// the wrong character. The
  135. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer/shared">Shared Events</see>
  136. /// page explains various ways this issue can be avoided.
  137. /// </remarks>
  138. EventPlayMismatch = 1 << 6,
  139. /// <summary>
  140. /// An <see cref="AnimancerEvent"/> that does nothing was invoked.
  141. /// Most likely it was not configured correctly.
  142. /// </summary>
  143. ///
  144. /// <remarks>
  145. /// Unused events should be removed to avoid wasting performance checking and invoking them.
  146. /// </remarks>
  147. UselessEvent = 1 << 7,
  148. /// <summary>
  149. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Events</see>
  150. /// are being used on a state which does not properly support them so they might not work as intended.
  151. /// </summary>
  152. ///
  153. /// <remarks>
  154. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Events</see> on a
  155. /// <see cref="ControllerState"/> will be triggered based on its <see cref="AnimancerState.NormalizedTime"/>,
  156. /// which comes from the current state of its Animator Controller regardless of which state that may be.
  157. /// <para></para>
  158. /// If you intend for the event to be associated with a specific state inside the Animator Controller,
  159. /// you need to use Unity's regular
  160. /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animation">Animation Events</see>
  161. /// instead.
  162. /// <para></para>
  163. /// But if you intend the event to be triggered by any state inside the Animator Controller,
  164. /// then you can simply disable this warning.
  165. /// </remarks>
  166. UnsupportedEvents = 1 << 8,
  167. /// <summary>
  168. /// <see href="https://kybernetik.com.au/animancer/docs/manual/ik">Inverse Kinematics</see>
  169. /// cannot be dynamically enabled on some
  170. /// <see href="https://kybernetik.com.au/animancer/docs/manual/playing/states">State</see> types.
  171. /// </summary>
  172. ///
  173. /// <remarks>
  174. /// To use IK on a <see cref="ControllerState"/>
  175. /// you must instead enable it on the desired layer inside the Animator Controller.
  176. /// <para></para>
  177. /// IK is not supported by <see cref="PlayableAssetState"/>.
  178. /// <para></para>
  179. /// Setting <see cref="AnimancerNode.ApplyAnimatorIK"/> on such a state will simply do nothing,
  180. /// so feel free to disable this warning if you are enabling IK on states without checking their type.
  181. /// </remarks>
  182. UnsupportedIK = 1 << 9,
  183. /// <summary>
  184. /// A Mixer State is being initialized with its <see cref="AnimancerNode.ChildCount"/> &lt;= 1.
  185. /// </summary>
  186. ///
  187. /// <remarks>
  188. /// The purpose of a mixer is to mix multiple child states
  189. /// so you are probably initializing it with incorrect parameters.
  190. /// <para></para>
  191. /// A mixer with only one child will simply play that child,
  192. /// so feel free to disable this warning if that's what you intend to do.
  193. /// </remarks>
  194. MixerMinChildren = 1 << 10,
  195. /// <summary>
  196. /// A Mixer State is synchronizing a child with <see cref="AnimancerState.Length"/> = 0.
  197. /// </summary>
  198. ///
  199. /// <remarks>
  200. /// Synchronization is based on the <see cref="AnimancerState.NormalizedTime"/>
  201. /// which can't be calculated if the <see cref="AnimancerState.Length"/> is 0.
  202. /// <para></para>
  203. /// Some state types can change their <see cref="AnimancerState.Length"/>,
  204. /// in which case you can just disable this warning.
  205. /// But otherwise, the indicated state should not be added to the synchronization list.
  206. /// </remarks>
  207. MixerSynchronizeZeroLength = 1 << 11,
  208. /// <summary>
  209. /// When a transition with a non-zero <see cref="ITransition.FadeDuration"/>
  210. /// creates a state, that state will log this warning if it's ever played
  211. /// without a fade duration.
  212. /// </summary>
  213. /// <remarks>
  214. /// This helps identify situations where a state is accidentally played directly
  215. /// where the transition should be played instead to allow it to apply its fade
  216. /// and any other details.
  217. /// </remarks>
  218. ExpectFade = 1 << 12,
  219. /// <summary>
  220. /// A <see href="https://kybernetik.com.au/animancer/docs/manual/blending/fading/custom">Custom Easing</see>
  221. /// is being started but its weight calculation does not go from 0 to 1.
  222. /// </summary>
  223. ///
  224. /// <remarks>
  225. /// The <see cref="FadeGroup.Easing"/> method is expected to return 0 when the parameter is 0 and
  226. /// 1 when the parameter is 1. It can do anything you want with other values,
  227. /// but starting or ending at different values will likely lead to undesirable results.
  228. /// <para></para>
  229. /// If your <see cref="FadeGroup.Easing"/> method is expensive you could disable this warning to save
  230. /// some performance, but violating the above guidelines is not recommended.
  231. /// </remarks>
  232. FadeEasingBounds = 1 << 13,
  233. /// <summary>
  234. /// The <see cref="Animator.speed"/> property does not affect Animancer.
  235. /// Use <see cref="AnimancerGraph.Speed"/> instead.
  236. /// </summary>
  237. ///
  238. /// <remarks>
  239. /// The <see cref="Animator.speed"/> property only works with Animator Controllers but does not affect the
  240. /// Playables API so Animancer has its own <see cref="AnimancerGraph.Speed"/> property.
  241. /// </remarks>
  242. AnimatorSpeed = 1 << 14,
  243. /// <summary>
  244. /// An <see cref="AnimancerNodeBase.Graph"/> is null during finalization (garbage collection).
  245. /// </summary>
  246. ///
  247. /// <remarks>
  248. /// This probably means that node was never used for anything and should not have been created.
  249. /// <para></para>
  250. /// This warning can be prevented for a specific node by calling <see cref="AnimancerNodeBase.MarkAsUsed"/>.
  251. /// <para></para>
  252. /// To minimise the performance cost of checking this warning, it does not capture the stack trace of the
  253. /// node's creation by default. However, you can enable <see cref="AnimancerNode.TraceConstructor"/> on startup
  254. /// so that it can include the stack trace in the warning message for any nodes that end up being unused.
  255. /// </remarks>
  256. UnusedNode = 1 << 15,
  257. /// <summary>
  258. /// <see cref="PlayableAssetState.InitializeBindings"/> is trying to bind to the same <see cref="Animator"/>
  259. /// that is being used by Animancer.
  260. /// </summary>
  261. /// <remarks>
  262. /// Doing this will replace Animancer's output so its animations would not work anymore.
  263. /// </remarks>
  264. PlayableAssetAnimatorBinding = 1 << 16,
  265. /// <summary>
  266. /// <see cref="AnimancerLayer.GetOrCreateWeightlessState"/> is cloning a complex state such as a
  267. /// <see cref="ManualMixerState"/> or <see cref="ControllerState"/>.
  268. /// This has a larger performance cost than cloning a <see cref="ClipState"/> and these states
  269. /// generally have parameters that need to be controlled which may result in undesired behaviour
  270. /// if your scripts are only expecting to have one state to control.
  271. /// </summary>
  272. /// <remarks>
  273. /// The <see href="https://kybernetik.com.au/animancer/docs/manual/blending/fading/modes">Fade Modes</see>
  274. /// page explains why clones are created.
  275. /// </remarks>
  276. CloneComplexState = 1 << 17,
  277. /// <summary>
  278. /// Unity doesn't suppport dynamically creating animations for Animancer in runtime builds
  279. /// so this warning is given when attempting to use an animation which isn't saved as an
  280. /// asset to explain this limitation as early as possible.
  281. /// </summary>
  282. ///
  283. /// <remarks>
  284. /// This warning should be disabled if you only intend to use the animation in the
  285. /// Unity Editor and not create it in a runtime build.
  286. /// </remarks>
  287. DynamicAnimation = 1 << 18,
  288. /// <summary>
  289. /// <see cref="Animancer.StringReference"/>s are generally more efficient for comparisons
  290. /// than raw <see cref="string"/>s and are not interchangeable so references should be preferred.
  291. /// </summary>
  292. StringReference = 1 << 19,
  293. /// <summary>All warning types.</summary>
  294. All = ~0,
  295. }
  296. /// https://kybernetik.com.au/animancer/api/Animancer/Validate
  297. public static partial class Validate
  298. {
  299. /************************************************************************************************************************/
  300. #if UNITY_ASSERTIONS
  301. /// <summary>[Assert-Only]
  302. /// The <see cref="OptionalWarning"/> flags that are currently disabled (default none).
  303. /// </summary>
  304. private static OptionalWarning _DisabledWarnings;
  305. #endif
  306. /************************************************************************************************************************/
  307. /// <summary>[Animancer Extension] [Assert-Conditional]
  308. /// Disables the specified warning type. Supports bitwise combinations.
  309. /// </summary>
  310. /// <remarks>
  311. /// <strong>Example:</strong>
  312. /// You can put a method like this in any class to disable whatever warnings you don't want on startup:
  313. /// <para></para><code>
  314. /// #if UNITY_ASSERTIONS
  315. /// [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)]
  316. /// private static void DisableAnimancerWarnings()
  317. /// {
  318. /// Animancer.OptionalWarning.ProOnly.Disable();
  319. ///
  320. /// // You could disable OptionalWarning.All, but that's not recommended for obvious reasons.
  321. /// }
  322. /// #endif
  323. /// </code></remarks>
  324. [System.Diagnostics.Conditional(Strings.Assertions)]
  325. public static void Disable(this OptionalWarning type)
  326. {
  327. #if UNITY_ASSERTIONS
  328. _DisabledWarnings |= type;
  329. #endif
  330. }
  331. /************************************************************************************************************************/
  332. /// <summary>[Animancer Extension] [Assert-Conditional]
  333. /// Enables the specified warning type. Supports bitwise combinations.
  334. /// </summary>
  335. [System.Diagnostics.Conditional(Strings.Assertions)]
  336. public static void Enable(this OptionalWarning type)
  337. {
  338. #if UNITY_ASSERTIONS
  339. _DisabledWarnings &= ~type;
  340. #endif
  341. }
  342. /************************************************************************************************************************/
  343. /// <summary>[Animancer Extension] [Assert-Conditional]
  344. /// Enables or disables the specified warning type. Supports bitwise combinations.
  345. /// </summary>
  346. [System.Diagnostics.Conditional(Strings.Assertions)]
  347. public static void SetEnabled(this OptionalWarning type, bool enable)
  348. {
  349. #if UNITY_ASSERTIONS
  350. if (enable)
  351. type.Enable();
  352. else
  353. type.Disable();
  354. #endif
  355. }
  356. /************************************************************************************************************************/
  357. /// <summary>[Animancer Extension] [Assert-Conditional]
  358. /// Logs the `message` as a warning if the `type` is enabled.
  359. /// </summary>
  360. /// <remarks>Does nothing if the `message` is <c>null</c>.</remarks>
  361. [System.Diagnostics.Conditional(Strings.Assertions)]
  362. [HideInCallstack]
  363. public static void Log(this OptionalWarning type, string message, object context = null)
  364. {
  365. #if UNITY_ASSERTIONS
  366. if (message == null || type.IsDisabled())
  367. return;
  368. Debug.LogWarning($"Possible Issue Detected: {message}" +
  369. $"\n\nThis warning can be disabled via '{Strings.AnimancerSettingsPath}'" +
  370. $" or by calling {nameof(Animancer)}.{nameof(OptionalWarning)}.{type}.{nameof(Disable)}()" +
  371. " and it will automatically be compiled out of Runtime Builds (except for Development Builds)." +
  372. $" More information can be found at {Strings.DocsURLs.OptionalWarning}\n",
  373. context as Object);
  374. #endif
  375. }
  376. /************************************************************************************************************************/
  377. #if UNITY_ASSERTIONS
  378. /************************************************************************************************************************/
  379. /// <summary>[Animancer Extension] [Assert-Only] Are none of the specified warning types disabled?</summary>
  380. public static bool IsEnabled(this OptionalWarning type) => (_DisabledWarnings & type) == 0;
  381. /************************************************************************************************************************/
  382. /// <summary>[Animancer Extension] [Assert-Only] Are all of the specified warning types disabled?</summary>
  383. public static bool IsDisabled(this OptionalWarning type) => (_DisabledWarnings & type) == type;
  384. /************************************************************************************************************************/
  385. /// <summary>[Animancer Extension] [Assert-Only]
  386. /// Disables the specified warnings and returns those that were previously enabled.
  387. /// </summary>
  388. /// <remarks>Call <see cref="Enable"/> on the returned value to re-enable it.</remarks>
  389. public static OptionalWarning DisableTemporarily(this OptionalWarning type)
  390. {
  391. var previous = _DisabledWarnings;
  392. type.Disable();
  393. return ~previous & type;
  394. }
  395. /************************************************************************************************************************/
  396. private const string PermanentlyDisabledWarningsKey = nameof(Animancer) + "." + nameof(PermanentlyDisabledWarnings);
  397. /// <summary>[Assert-Only] Warnings that are automatically disabled</summary>
  398. /// <remarks>
  399. /// This value is stored in <see cref="PlayerPrefs"/>
  400. /// and can be manually edited via <see cref="Strings.AnimancerSettingsPath"/>.
  401. /// </remarks>
  402. public static OptionalWarning PermanentlyDisabledWarnings
  403. {
  404. #if NO_RUNTIME_PLAYER_PREFS && ! UNITY_EDITOR
  405. get => default;
  406. set
  407. {
  408. _DisabledWarnings = value;
  409. }
  410. #else
  411. get => (OptionalWarning)PlayerPrefs.GetInt(PermanentlyDisabledWarningsKey);
  412. set
  413. {
  414. _DisabledWarnings = value;
  415. PlayerPrefs.SetInt(PermanentlyDisabledWarningsKey, (int)value);
  416. }
  417. #endif
  418. }
  419. #if UNITY_EDITOR
  420. [UnityEditor.InitializeOnLoadMethod]
  421. #endif
  422. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
  423. private static void InitializePermanentlyDisabledWarnings()
  424. {
  425. _DisabledWarnings = PermanentlyDisabledWarnings;
  426. }
  427. /************************************************************************************************************************/
  428. #endif
  429. /************************************************************************************************************************/
  430. }
  431. }