// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // using System; using UnityEngine; using Object = UnityEngine.Object; namespace Animancer { /// /// Bitwise flags used to determine which warnings Animancer should give. /// /// These warnings are all optional. /// Feel free to disable any of them if you understand the potential issues they're referring to. /// /// /// /// All warnings are enabled by default, but are compiled out of runtime builds (except development builds). /// /// You can manually disable warnings using the AnimancerSettings asset /// or the Animancer Settings panel in the Animancer Tools Window (Window/Animation/Animancer Tools). /// /// Example: /// You can put a method like this in any class to disable whatever warnings you don't want on startup: /// /// #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 /// /// /// https://kybernetik.com.au/animancer/api/Animancer/OptionalWarning /// [Flags] public enum OptionalWarning { /// default None = 0, /// /// A Pro-Only Feature /// has been used in Animancer Lite. /// /// /// /// Some Features /// are only available in Animancer Pro. /// /// Animancer Lite /// 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. /// ProOnly = 1 << 0, /// /// An is being initialized /// during a type of GUI event that isn't supposed to cause side effects. /// /// /// /// and /// should display the current details of things, but they should not modify things. /// CreateGraphDuringGuiEvent = 1 << 1, /// /// The is disabled so Animancer won't be able to play animations. /// /// /// /// The doesn't need an Animator Controller, /// it just needs to be enabled via the checkbox in the Inspector /// or by setting animancerComponent.Animator.enabled = true; in code. /// AnimatorDisabled = 1 << 2, /// /// An is assigned /// but the Rig is Humanoid so it can't be blended with Animancer. /// /// /// /// Native /// 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). /// /// If you don't intend to blend between them, you can just disable this warning. /// NativeControllerHumanoid = 1 << 3, /// /// An is assigned while also using a /// . /// /// /// /// Either assign the to use it as a Native Animator /// Controller or assign the to use it as a Hybrid Animator /// Controller. The differences are explained on the /// Animator Controllers /// page. /// /// 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. /// NativeControllerHybrid = 1 << 4, /// /// An End Event /// didn't actually end the animation. /// /// /// /// 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). /// /// 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. /// /// End Events /// 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 /// Animancer Event. /// EndEventInterrupt = 1 << 5, /// /// An Animancer Event /// triggered by one character was used to play an animation on a different character. /// /// /// /// 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 /// Shared Events /// page explains various ways this issue can be avoided. /// EventPlayMismatch = 1 << 6, /// /// An that does nothing was invoked. /// Most likely it was not configured correctly. /// /// /// /// Unused events should be removed to avoid wasting performance checking and invoking them. /// UselessEvent = 1 << 7, /// /// Animancer Events /// are being used on a state which does not properly support them so they might not work as intended. /// /// /// /// Animancer Events on a /// will be triggered based on its , /// which comes from the current state of its Animator Controller regardless of which state that may be. /// /// If you intend for the event to be associated with a specific state inside the Animator Controller, /// you need to use Unity's regular /// Animation Events /// instead. /// /// But if you intend the event to be triggered by any state inside the Animator Controller, /// then you can simply disable this warning. /// UnsupportedEvents = 1 << 8, /// /// Inverse Kinematics /// cannot be dynamically enabled on some /// State types. /// /// /// /// To use IK on a /// you must instead enable it on the desired layer inside the Animator Controller. /// /// IK is not supported by . /// /// Setting 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. /// UnsupportedIK = 1 << 9, /// /// A Mixer State is being initialized with its <= 1. /// /// /// /// The purpose of a mixer is to mix multiple child states /// so you are probably initializing it with incorrect parameters. /// /// 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. /// MixerMinChildren = 1 << 10, /// /// A Mixer State is synchronizing a child with = 0. /// /// /// /// Synchronization is based on the /// which can't be calculated if the is 0. /// /// Some state types can change their , /// in which case you can just disable this warning. /// But otherwise, the indicated state should not be added to the synchronization list. /// MixerSynchronizeZeroLength = 1 << 11, /// /// When a transition with a non-zero /// creates a state, that state will log this warning if it's ever played /// without a fade duration. /// /// /// 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. /// ExpectFade = 1 << 12, /// /// A Custom Easing /// is being started but its weight calculation does not go from 0 to 1. /// /// /// /// The 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. /// /// If your method is expensive you could disable this warning to save /// some performance, but violating the above guidelines is not recommended. /// FadeEasingBounds = 1 << 13, /// /// The property does not affect Animancer. /// Use instead. /// /// /// /// The property only works with Animator Controllers but does not affect the /// Playables API so Animancer has its own property. /// AnimatorSpeed = 1 << 14, /// /// An is null during finalization (garbage collection). /// /// /// /// This probably means that node was never used for anything and should not have been created. /// /// This warning can be prevented for a specific node by calling . /// /// 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 on startup /// so that it can include the stack trace in the warning message for any nodes that end up being unused. /// UnusedNode = 1 << 15, /// /// is trying to bind to the same /// that is being used by Animancer. /// /// /// Doing this will replace Animancer's output so its animations would not work anymore. /// PlayableAssetAnimatorBinding = 1 << 16, /// /// is cloning a complex state such as a /// or . /// This has a larger performance cost than cloning a 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. /// /// /// The Fade Modes /// page explains why clones are created. /// CloneComplexState = 1 << 17, /// /// 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. /// /// /// /// 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. /// DynamicAnimation = 1 << 18, /// /// s are generally more efficient for comparisons /// than raw s and are not interchangeable so references should be preferred. /// StringReference = 1 << 19, /// All warning types. All = ~0, } /// https://kybernetik.com.au/animancer/api/Animancer/Validate public static partial class Validate { /************************************************************************************************************************/ #if UNITY_ASSERTIONS /// [Assert-Only] /// The flags that are currently disabled (default none). /// private static OptionalWarning _DisabledWarnings; #endif /************************************************************************************************************************/ /// [Animancer Extension] [Assert-Conditional] /// Disables the specified warning type. Supports bitwise combinations. /// /// /// Example: /// You can put a method like this in any class to disable whatever warnings you don't want on startup: /// /// #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 /// [System.Diagnostics.Conditional(Strings.Assertions)] public static void Disable(this OptionalWarning type) { #if UNITY_ASSERTIONS _DisabledWarnings |= type; #endif } /************************************************************************************************************************/ /// [Animancer Extension] [Assert-Conditional] /// Enables the specified warning type. Supports bitwise combinations. /// [System.Diagnostics.Conditional(Strings.Assertions)] public static void Enable(this OptionalWarning type) { #if UNITY_ASSERTIONS _DisabledWarnings &= ~type; #endif } /************************************************************************************************************************/ /// [Animancer Extension] [Assert-Conditional] /// Enables or disables the specified warning type. Supports bitwise combinations. /// [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 } /************************************************************************************************************************/ /// [Animancer Extension] [Assert-Conditional] /// Logs the `message` as a warning if the `type` is enabled. /// /// Does nothing if the `message` is null. [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 /************************************************************************************************************************/ /// [Animancer Extension] [Assert-Only] Are none of the specified warning types disabled? public static bool IsEnabled(this OptionalWarning type) => (_DisabledWarnings & type) == 0; /************************************************************************************************************************/ /// [Animancer Extension] [Assert-Only] Are all of the specified warning types disabled? public static bool IsDisabled(this OptionalWarning type) => (_DisabledWarnings & type) == type; /************************************************************************************************************************/ /// [Animancer Extension] [Assert-Only] /// Disables the specified warnings and returns those that were previously enabled. /// /// Call on the returned value to re-enable it. public static OptionalWarning DisableTemporarily(this OptionalWarning type) { var previous = _DisabledWarnings; type.Disable(); return ~previous & type; } /************************************************************************************************************************/ private const string PermanentlyDisabledWarningsKey = nameof(Animancer) + "." + nameof(PermanentlyDisabledWarnings); /// [Assert-Only] Warnings that are automatically disabled /// /// This value is stored in /// and can be manually edited via . /// 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 /************************************************************************************************************************/ } }