// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik // using System; using UnityEngine; using UnityEngine.Playables; using Object = UnityEngine.Object; namespace Animancer { /// /// Bitwise flags used by and 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 are 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 Settings in the /// (Window/Animation/Animancer Tools). /// /// /// /// You can put the following method 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 is not recommended for obvious reasons. /// } /// #endif /// /// https://kybernetik.com.au/animancer/api/Animancer/OptionalWarning /// [Flags] public enum OptionalWarning { /// /// 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 while its is /// inactive. /// /// /// /// Unity will not call if the is never /// enabled. That would prevent it from destroying the internal , leading to a /// memory leak. /// /// Animations usually shouldn't be played on inactive objects so you most likely just need to call /// first. /// /// If you do intend to use it while inactive, you will need to disable this warning and call /// manually when the object is destroyed (such as when its scene is /// unloaded). /// CreateGraphWhileDisabled = 1 << 1, /// /// An is being initialized during a type of GUI event that shouldn't /// cause side effects. /// /// /// /// and should display the current details of /// things, but they should not modify things. /// CreateGraphDuringGuiEvent = 1 << 2, /// /// 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 << 3, /// /// 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 << 4, /// /// 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 in the /// Documentation /// /// 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 << 5, /// /// An Animancer Event is /// being added to an which already contains an identical event. /// /// /// /// This warning often occurs due to a misunderstanding about the way events are /// Automatically /// Cleared. /// /// If you play an , its will be empty so you /// can add whatever events you want. /// /// But Transitions store their own /// events, so if you play one then modify its you are actually modifying /// the transition's events. Then if you play the same transition again, you will modify the events again, /// often leading to the same event being added multiple times. /// /// If that is not the case, you can simply disable this warning. There is nothing inherently wrong with having /// multiple identical events in the same sequence. /// DuplicateEvent = 1 << 6, /// /// An End Event did not actually /// end the animation. /// /// /// /// End Events are triggered every /// frame after their time has passed, so in this case it might be necessary to explicitly clear the event or /// simply use a regular Animancer Event. /// /// If you intend for the event to keep getting triggered, you can just disable this warning. /// EndEventInterrupt = 1 << 7, /// /// 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 << 8, /// /// An is being modified even though its /// is set. /// /// /// /// This is primarily used by transitions. Their events should generally be configured on startup rather /// than repeating the setup on the state after the transition is played because such modifications will apply /// back to the transition's events (which is usually not intended). /// LockedEvents = 1 << 9, /// /// Animancer Events are /// being used on a state that 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 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 << 10, /// is being used on a state that doesn't support it. /// /// /// does nothing on s so there is no /// way to directly control their speed. The /// Animator Controller Speed /// page explains a possible workaround for this issue. /// /// The only reason you would disable this warning is if you are setting the speed of states in general and /// not depending on it to actually take effect. /// UnsupportedSpeed = 1 << 11, /// /// Inverse Kinematics cannot be /// dynamically enabled on some States /// 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 << 12, /// /// A 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 is /// what you intend to do. /// MixerMinChildren = 1 << 13, /// /// A 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 << 14, /// /// A Custom Fade /// 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 violating that guideline will /// trigger this warning because it would 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. /// CustomFadeBounds = 1 << 15, /// /// A weight calculation method was not specified when attempting to start a /// Custom Fade. /// /// /// /// Passing a null parameter into and /// other similar methods will trigger this warning and return null because a /// serves no purpose if it doesn't have a method for calculating the weight. /// CustomFadeNotNull = 1 << 16, /// /// 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 << 17, /// 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 passing it into . /// /// 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 << 18, /// /// 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 << 19, /// /// 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 << 20, /// 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. /// /// /// You can put the following method 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.EndEventInterrupt.Disable(); /// /// // You could disable OptionalWarning.All, but that is 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. /// [System.Diagnostics.Conditional(Strings.Assertions)] public static void Log(this OptionalWarning type, string message, object context = null) { #if UNITY_ASSERTIONS if (message == null || type.IsDisabled()) return; Debug.LogWarning($"Possible Bug Detected: {message}" + $"\n\nThis warning can be disabled via the Settings in '{Strings.AnimancerToolsMenuPath}'" + $" 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 and stored in . 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 /************************************************************************************************************************/ } }