// 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
/************************************************************************************************************************/
}
}