123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- using System;
- using UnityEngine;
- using Object = UnityEngine.Object;
- namespace Animancer
- {
- /// <summary>
- /// Bitwise flags used to determine which warnings Animancer should give.
- /// <para></para>
- /// <strong>These warnings are all optional</strong>.
- /// Feel free to disable any of them if you understand the <em>potential</em> issues they're referring to.
- /// </summary>
- ///
- /// <remarks>
- /// All warnings are enabled by default, but are compiled out of runtime builds (except development builds).
- /// <para></para>
- /// You can manually disable warnings using the <c>AnimancerSettings</c> asset
- /// or the Animancer Settings panel in the Animancer Tools Window (<c>Window/Animation/Animancer Tools</c>).
- /// <para></para>
- /// <strong>Example:</strong>
- /// You can put a method like this in any class to disable whatever warnings you don't want on startup:
- /// <para></para><code>
- /// #if UNITY_ASSERTIONS
- /// [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)]
- /// private static void DisableAnimancerWarnings()
- /// {
- /// Animancer.OptionalWarning.ProOnly.Disable();
- ///
- /// // You could disable OptionalWarning.All, but that's not recommended for obvious reasons.
- /// }
- /// #endif
- /// </code></remarks>
- ///
- /// https://kybernetik.com.au/animancer/api/Animancer/OptionalWarning
- ///
- [Flags]
- public enum OptionalWarning
- {
- /// <summary><c>default</c></summary>
- None = 0,
- /// <summary>
- /// A <see href="https://kybernetik.com.au/animancer/docs/introduction/features">Pro-Only Feature</see>
- /// has been used in <see href="https://kybernetik.com.au/animancer/redirect/lite">Animancer Lite</see>.
- /// </summary>
- ///
- /// <remarks>
- /// Some <see href="https://kybernetik.com.au/animancer/docs/introduction/features">Features</see>
- /// are only available in <see href="https://kybernetik.com.au/animancer/redirect/pro">Animancer Pro</see>.
- /// <para></para>
- /// <see href="https://kybernetik.com.au/animancer/redirect/lite">Animancer Lite</see>
- /// allows you to try out those features in the Unity Editor and gives this warning the
- /// first time each one is used to inform you that they will not work in runtime builds.
- /// </remarks>
- ProOnly = 1 << 0,
- /// <summary>
- /// An <see cref="AnimancerComponent.Graph"/> is being initialized
- /// during a type of GUI event that isn't supposed to cause side effects.
- /// </summary>
- ///
- /// <remarks>
- /// <see cref="EventType.Layout"/> and <see cref="EventType.Repaint"/>
- /// should display the current details of things, but they should not modify things.
- /// </remarks>
- CreateGraphDuringGuiEvent = 1 << 1,
- /// <summary>
- /// The <see cref="AnimancerComponent.Animator"/> is disabled so Animancer won't be able to play animations.
- /// </summary>
- ///
- /// <remarks>
- /// The <see cref="Animator"/> doesn't need an Animator Controller,
- /// it just needs to be enabled via the checkbox in the Inspector
- /// or by setting <c>animancerComponent.Animator.enabled = true;</c> in code.
- /// </remarks>
- AnimatorDisabled = 1 << 2,
- /// <summary>
- /// An <see cref="Animator.runtimeAnimatorController"/> is assigned
- /// but the Rig is Humanoid so it can't be blended with Animancer.
- /// </summary>
- ///
- /// <remarks>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/animator-controllers#native">Native</see>
- /// Animator Controllers can blend with Animancer on Generic Rigs, but not on Humanoid Rigs
- /// (you can swap back and forth between the Animator Controller and Animancer,
- /// but it won't smoothly blend between them).
- /// <para></para>
- /// If you don't intend to blend between them, you can just disable this warning.
- /// </remarks>
- NativeControllerHumanoid = 1 << 3,
- /// <summary>
- /// An <see cref="Animator.runtimeAnimatorController"/> is assigned while also using a
- /// <see cref="HybridAnimancerComponent"/>.
- /// </summary>
- ///
- /// <remarks>
- /// Either assign the <see cref="Animator.runtimeAnimatorController"/> to use it as a Native Animator
- /// Controller or assign the <see cref="HybridAnimancerComponent.Controller"/> to use it as a Hybrid Animator
- /// Controller. The differences are explained on the
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/animator-controllers">Animator Controllers</see>
- /// page.
- /// <para></para>
- /// It is possible to use both, but it usually only happens when misunderstanding how the system works.
- /// If you do want both, just disable this warning.
- /// </remarks>
- NativeControllerHybrid = 1 << 4,
- /// <summary>
- /// An <see href="https://kybernetik.com.au/animancer/docs/manual/events/end">End Event</see>
- /// didn't actually end the animation.
- /// </summary>
- ///
- /// <remarks>
- /// Animancer doesn't automatically do anything during an End Event
- /// so it's up to you to end the animation (usually by playing something else).
- /// <para></para>
- /// This warning is given when the event isn't used to stop the animation that caused it
- /// (usually by playing something else). This often indicates that the event hasn't been
- /// configured correctly, however it is sometimes intentional such as if the event doesn't
- /// immediately stop the animation but sets a flag to indicate the animation has ended for
- /// another system to act on at a later time. In that case this warning should be disabled.
- /// <para></para>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/end">End Events</see>
- /// are triggered every frame after their time has passed, so in this case it might be
- /// necessary to clear the event or simply use a regular
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Event</see>.
- /// </remarks>
- EndEventInterrupt = 1 << 5,
- /// <summary>
- /// An <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Event</see>
- /// triggered by one character was used to play an animation on a different character.
- /// </summary>
- ///
- /// <remarks>
- /// This most commonly happens when a Transition is shared by multiple characters and they
- /// all register their own callbacks to its events which leads to those events controlling
- /// the wrong character. The
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer/shared">Shared Events</see>
- /// page explains various ways this issue can be avoided.
- /// </remarks>
- EventPlayMismatch = 1 << 6,
- /// <summary>
- /// An <see cref="AnimancerEvent"/> that does nothing was invoked.
- /// Most likely it was not configured correctly.
- /// </summary>
- ///
- /// <remarks>
- /// Unused events should be removed to avoid wasting performance checking and invoking them.
- /// </remarks>
- UselessEvent = 1 << 7,
- /// <summary>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Events</see>
- /// are being used on a state which does not properly support them so they might not work as intended.
- /// </summary>
- ///
- /// <remarks>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animancer">Animancer Events</see> on a
- /// <see cref="ControllerState"/> will be triggered based on its <see cref="AnimancerState.NormalizedTime"/>,
- /// which comes from the current state of its Animator Controller regardless of which state that may be.
- /// <para></para>
- /// If you intend for the event to be associated with a specific state inside the Animator Controller,
- /// you need to use Unity's regular
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/events/animation">Animation Events</see>
- /// instead.
- /// <para></para>
- /// But if you intend the event to be triggered by any state inside the Animator Controller,
- /// then you can simply disable this warning.
- /// </remarks>
- UnsupportedEvents = 1 << 8,
- /// <summary>
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/ik">Inverse Kinematics</see>
- /// cannot be dynamically enabled on some
- /// <see href="https://kybernetik.com.au/animancer/docs/manual/playing/states">State</see> types.
- /// </summary>
- ///
- /// <remarks>
- /// To use IK on a <see cref="ControllerState"/>
- /// you must instead enable it on the desired layer inside the Animator Controller.
- /// <para></para>
- /// IK is not supported by <see cref="PlayableAssetState"/>.
- /// <para></para>
- /// Setting <see cref="AnimancerNode.ApplyAnimatorIK"/> on such a state will simply do nothing,
- /// so feel free to disable this warning if you are enabling IK on states without checking their type.
- /// </remarks>
- UnsupportedIK = 1 << 9,
- /// <summary>
- /// A Mixer State is being initialized with its <see cref="AnimancerNode.ChildCount"/> <= 1.
- /// </summary>
- ///
- /// <remarks>
- /// The purpose of a mixer is to mix multiple child states
- /// so you are probably initializing it with incorrect parameters.
- /// <para></para>
- /// A mixer with only one child will simply play that child,
- /// so feel free to disable this warning if that's what you intend to do.
- /// </remarks>
- MixerMinChildren = 1 << 10,
- /// <summary>
- /// A Mixer State is synchronizing a child with <see cref="AnimancerState.Length"/> = 0.
- /// </summary>
- ///
- /// <remarks>
- /// Synchronization is based on the <see cref="AnimancerState.NormalizedTime"/>
- /// which can't be calculated if the <see cref="AnimancerState.Length"/> is 0.
- /// <para></para>
- /// Some state types can change their <see cref="AnimancerState.Length"/>,
- /// in which case you can just disable this warning.
- /// But otherwise, the indicated state should not be added to the synchronization list.
- /// </remarks>
- MixerSynchronizeZeroLength = 1 << 11,
- /// <summary>
- /// When a transition with a non-zero <see cref="ITransition.FadeDuration"/>
- /// creates a state, that state will log this warning if it's ever played
- /// without a fade duration.
- /// </summary>
- /// <remarks>
- /// This helps identify situations where a state is accidentally played directly
- /// where the transition should be played instead to allow it to apply its fade
- /// and any other details.
- /// </remarks>
- ExpectFade = 1 << 12,
- /// <summary>
- /// A <see href="https://kybernetik.com.au/animancer/docs/manual/blending/fading/custom">Custom Easing</see>
- /// is being started but its weight calculation does not go from 0 to 1.
- /// </summary>
- ///
- /// <remarks>
- /// The <see cref="FadeGroup.Easing"/> method is expected to return 0 when the parameter is 0 and
- /// 1 when the parameter is 1. It can do anything you want with other values,
- /// but starting or ending at different values will likely lead to undesirable results.
- /// <para></para>
- /// If your <see cref="FadeGroup.Easing"/> method is expensive you could disable this warning to save
- /// some performance, but violating the above guidelines is not recommended.
- /// </remarks>
- FadeEasingBounds = 1 << 13,
- /// <summary>
- /// The <see cref="Animator.speed"/> property does not affect Animancer.
- /// Use <see cref="AnimancerGraph.Speed"/> instead.
- /// </summary>
- ///
- /// <remarks>
- /// The <see cref="Animator.speed"/> property only works with Animator Controllers but does not affect the
- /// Playables API so Animancer has its own <see cref="AnimancerGraph.Speed"/> property.
- /// </remarks>
- AnimatorSpeed = 1 << 14,
- /// <summary>
- /// An <see cref="AnimancerNodeBase.Graph"/> is null during finalization (garbage collection).
- /// </summary>
- ///
- /// <remarks>
- /// This probably means that node was never used for anything and should not have been created.
- /// <para></para>
- /// This warning can be prevented for a specific node by calling <see cref="AnimancerNodeBase.MarkAsUsed"/>.
- /// <para></para>
- /// To minimise the performance cost of checking this warning, it does not capture the stack trace of the
- /// node's creation by default. However, you can enable <see cref="AnimancerNode.TraceConstructor"/> on startup
- /// so that it can include the stack trace in the warning message for any nodes that end up being unused.
- /// </remarks>
- UnusedNode = 1 << 15,
- /// <summary>
- /// <see cref="PlayableAssetState.InitializeBindings"/> is trying to bind to the same <see cref="Animator"/>
- /// that is being used by Animancer.
- /// </summary>
- /// <remarks>
- /// Doing this will replace Animancer's output so its animations would not work anymore.
- /// </remarks>
- PlayableAssetAnimatorBinding = 1 << 16,
- /// <summary>
- /// <see cref="AnimancerLayer.GetOrCreateWeightlessState"/> is cloning a complex state such as a
- /// <see cref="ManualMixerState"/> or <see cref="ControllerState"/>.
- /// This has a larger performance cost than cloning a <see cref="ClipState"/> and these states
- /// generally have parameters that need to be controlled which may result in undesired behaviour
- /// if your scripts are only expecting to have one state to control.
- /// </summary>
- /// <remarks>
- /// The <see href="https://kybernetik.com.au/animancer/docs/manual/blending/fading/modes">Fade Modes</see>
- /// page explains why clones are created.
- /// </remarks>
- CloneComplexState = 1 << 17,
- /// <summary>
- /// Unity doesn't suppport dynamically creating animations for Animancer in runtime builds
- /// so this warning is given when attempting to use an animation which isn't saved as an
- /// asset to explain this limitation as early as possible.
- /// </summary>
- ///
- /// <remarks>
- /// This warning should be disabled if you only intend to use the animation in the
- /// Unity Editor and not create it in a runtime build.
- /// </remarks>
- DynamicAnimation = 1 << 18,
- /// <summary>
- /// <see cref="Animancer.StringReference"/>s are generally more efficient for comparisons
- /// than raw <see cref="string"/>s and are not interchangeable so references should be preferred.
- /// </summary>
- StringReference = 1 << 19,
- /// <summary>All warning types.</summary>
- All = ~0,
- }
- /// https://kybernetik.com.au/animancer/api/Animancer/Validate
- public static partial class Validate
- {
- /************************************************************************************************************************/
- #if UNITY_ASSERTIONS
- /// <summary>[Assert-Only]
- /// The <see cref="OptionalWarning"/> flags that are currently disabled (default none).
- /// </summary>
- private static OptionalWarning _DisabledWarnings;
- #endif
- /************************************************************************************************************************/
- /// <summary>[Animancer Extension] [Assert-Conditional]
- /// Disables the specified warning type. Supports bitwise combinations.
- /// </summary>
- /// <remarks>
- /// <strong>Example:</strong>
- /// You can put a method like this in any class to disable whatever warnings you don't want on startup:
- /// <para></para><code>
- /// #if UNITY_ASSERTIONS
- /// [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)]
- /// private static void DisableAnimancerWarnings()
- /// {
- /// Animancer.OptionalWarning.ProOnly.Disable();
- ///
- /// // You could disable OptionalWarning.All, but that's not recommended for obvious reasons.
- /// }
- /// #endif
- /// </code></remarks>
- [System.Diagnostics.Conditional(Strings.Assertions)]
- public static void Disable(this OptionalWarning type)
- {
- #if UNITY_ASSERTIONS
- _DisabledWarnings |= type;
- #endif
- }
- /************************************************************************************************************************/
- /// <summary>[Animancer Extension] [Assert-Conditional]
- /// Enables the specified warning type. Supports bitwise combinations.
- /// </summary>
- [System.Diagnostics.Conditional(Strings.Assertions)]
- public static void Enable(this OptionalWarning type)
- {
- #if UNITY_ASSERTIONS
- _DisabledWarnings &= ~type;
- #endif
- }
- /************************************************************************************************************************/
- /// <summary>[Animancer Extension] [Assert-Conditional]
- /// Enables or disables the specified warning type. Supports bitwise combinations.
- /// </summary>
- [System.Diagnostics.Conditional(Strings.Assertions)]
- public static void SetEnabled(this OptionalWarning type, bool enable)
- {
- #if UNITY_ASSERTIONS
- if (enable)
- type.Enable();
- else
- type.Disable();
- #endif
- }
- /************************************************************************************************************************/
- /// <summary>[Animancer Extension] [Assert-Conditional]
- /// Logs the `message` as a warning if the `type` is enabled.
- /// </summary>
- /// <remarks>Does nothing if the `message` is <c>null</c>.</remarks>
- [System.Diagnostics.Conditional(Strings.Assertions)]
- [HideInCallstack]
- public static void Log(this OptionalWarning type, string message, object context = null)
- {
- #if UNITY_ASSERTIONS
- if (message == null || type.IsDisabled())
- return;
- Debug.LogWarning($"Possible Issue Detected: {message}" +
- $"\n\nThis warning can be disabled via '{Strings.AnimancerSettingsPath}'" +
- $" or by calling {nameof(Animancer)}.{nameof(OptionalWarning)}.{type}.{nameof(Disable)}()" +
- " and it will automatically be compiled out of Runtime Builds (except for Development Builds)." +
- $" More information can be found at {Strings.DocsURLs.OptionalWarning}\n",
- context as Object);
- #endif
- }
- /************************************************************************************************************************/
- #if UNITY_ASSERTIONS
- /************************************************************************************************************************/
- /// <summary>[Animancer Extension] [Assert-Only] Are none of the specified warning types disabled?</summary>
- public static bool IsEnabled(this OptionalWarning type) => (_DisabledWarnings & type) == 0;
- /************************************************************************************************************************/
- /// <summary>[Animancer Extension] [Assert-Only] Are all of the specified warning types disabled?</summary>
- public static bool IsDisabled(this OptionalWarning type) => (_DisabledWarnings & type) == type;
- /************************************************************************************************************************/
- /// <summary>[Animancer Extension] [Assert-Only]
- /// Disables the specified warnings and returns those that were previously enabled.
- /// </summary>
- /// <remarks>Call <see cref="Enable"/> on the returned value to re-enable it.</remarks>
- public static OptionalWarning DisableTemporarily(this OptionalWarning type)
- {
- var previous = _DisabledWarnings;
- type.Disable();
- return ~previous & type;
- }
- /************************************************************************************************************************/
- private const string PermanentlyDisabledWarningsKey = nameof(Animancer) + "." + nameof(PermanentlyDisabledWarnings);
- /// <summary>[Assert-Only] Warnings that are automatically disabled</summary>
- /// <remarks>
- /// This value is stored in <see cref="PlayerPrefs"/>
- /// and can be manually edited via <see cref="Strings.AnimancerSettingsPath"/>.
- /// </remarks>
- public static OptionalWarning PermanentlyDisabledWarnings
- {
- #if NO_RUNTIME_PLAYER_PREFS && ! UNITY_EDITOR
- get => default;
- set
- {
- _DisabledWarnings = value;
- }
- #else
- get => (OptionalWarning)PlayerPrefs.GetInt(PermanentlyDisabledWarningsKey);
- set
- {
- _DisabledWarnings = value;
- PlayerPrefs.SetInt(PermanentlyDisabledWarningsKey, (int)value);
- }
- #endif
- }
- #if UNITY_EDITOR
- [UnityEditor.InitializeOnLoadMethod]
- #endif
- [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
- private static void InitializePermanentlyDisabledWarnings()
- {
- _DisabledWarnings = PermanentlyDisabledWarnings;
- }
- /************************************************************************************************************************/
- #endif
- /************************************************************************************************************************/
- }
- }
|