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