// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik // using UnityEngine; namespace Animancer.FSM { /// Base class for states to be used in a . /// /// Documentation: /// /// State Types /// /// https://kybernetik.com.au/animancer/api/Animancer.FSM/StateBehaviour /// // [HelpURL(StateExtensions.APIDocumentationURL + nameof(StateBehaviour))] public abstract class StateBehaviour : MonoBehaviour, IState { /************************************************************************************************************************/ /// [] /// Determines whether the can enter this state. /// Always returns true unless overridden. /// public virtual bool CanEnterState => true; /// [] /// Determines whether the can exit this state. /// Always returns true unless overridden. /// public virtual bool CanExitState => true; /************************************************************************************************************************/ /// [] /// Asserts that this component isn't already enabled, then enables it. /// public virtual void OnEnterState() { AssertEnabledAndRepaintIfSelected(false, nameof(OnEnterState)); enabled = true; } /************************************************************************************************************************/ /// [] /// Asserts that this component isn't already disabled, then disables it. /// public virtual void OnExitState() { if (this == null) return; AssertEnabledAndRepaintIfSelected(true, nameof(OnExitState)); enabled = false; } /************************************************************************************************************************/ #if UNITY_EDITOR /// [Editor-Only] /// Should the Inspector be repainted when a /// is enabled or disabled while it is selected? /// /// Default is true. public static bool ForceRepaintOnEnableDisable { get; set; } = true; private static double _LastRepaintTime; #endif /// [Assert-Conditional] /// Asserts this /// and instructs the Unity Editor to repaint if this object is selected so the Inspector updates properly. /// [System.Diagnostics.Conditional("UNITY_ASSERTIONS")] private void AssertEnabledAndRepaintIfSelected(bool expectEnabled, string callerName) { #if UNITY_ASSERTIONS if (enabled != expectEnabled) Debug.LogError( $"{nameof(StateBehaviour)} was already {(expectEnabled ? "disabled" : "enabled")}" + $" before {callerName}: {this}", this); #endif #if UNITY_EDITOR // Unity doesn't constantly repaint the Inspector if all the components are collapsed. // So we can simply force it here to ensure that it shows the correct state being enabled. if (ForceRepaintOnEnableDisable && UnityEditor.Selection.Contains(gameObject)) UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); #endif } /************************************************************************************************************************/ #if UNITY_EDITOR /// [Editor-Only] States start disabled and only the current state gets enabled at runtime. /// Called in Edit Mode whenever this script is loaded or a value is changed in the Inspector. protected virtual void OnValidate() { if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) return; enabled = false; } #endif /************************************************************************************************************************/ } }