Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Feedback Inconsistent StateMachineBehaviour Cloning

Discussion in 'Animation' started by SAPN-Immersive, Sep 18, 2020.

  1. SAPN-Immersive

    SAPN-Immersive

    Joined:
    Oct 9, 2018
    Posts:
    2
    Greetings,

    I'm finding myself very frustrated with mecanim in 2018.4. I have an "event source" behaviour that listens for OnStateTYPE events, and then converts them to C# events to be picked up by an "event receiver" monobehaviour. This is my workaround to allow scene-object binding into the FSM.

    (code includes attributes from Sirenix.OdinInspector)

    Code (CSharp):
    1. public delegate void StateMachineStateEvent(Animator animator, AnimatorStateInfo stateInfo, int layerIndex);
    2.  
    3. [SharedBetweenAnimators]
    4. public class StateMachineStateEventSource : StateMachineBehaviour
    5. {
    6.     public static event StateMachineStateEvent Enter;
    7.     public static event StateMachineStateEvent Update;
    8.     public static event StateMachineStateEvent Exit;
    9.  
    10.     // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    11.     override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    12.         base.OnStateEnter(animator, stateInfo, layerIndex);
    13.         Enter?.Invoke(animator, stateInfo, layerIndex);
    14.     }
    15.  
    16.     // OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
    17.     override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    18.         base.OnStateUpdate(animator, stateInfo, layerIndex);
    19.         Update?.Invoke(animator, stateInfo, layerIndex);
    20.     }
    21.  
    22.     // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    23.     override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    24.         base.OnStateExit(animator, stateInfo, layerIndex);
    25.         Exit?.Invoke(animator, stateInfo, layerIndex);
    26.     }
    27. }
    Code (CSharp):
    1. public class StateMachineEventReceiver : MonoBehaviour
    2. {
    3.  
    4.     [System.Serializable]
    5.     public class MapItem {
    6.         [HideLabel, Tooltip("State/Machine Name or Tag")]
    7.         public string StateId;
    8.  
    9.         [FoldoutGroup("Enter")]
    10.         public UnityEvent OnStateEnter;
    11.         [FoldoutGroup("Update")]
    12.         public UnityEvent OnStateUpdate;
    13.         [FoldoutGroup("Exit")]
    14.         public UnityEvent OnStateExit;
    15.     }
    16.  
    17.     public Animator Filter;
    18.     public List<MapItem> Map;
    19.  
    20.     private void OnEnable() {
    21.         StateMachineStateEventSource.Enter += StateMachineEventSource_OnStateEnter;
    22.         StateMachineStateEventSource.Update += StateMachineEventSource_OnStateUpdate;
    23.         StateMachineStateEventSource.Exit += StateMachineEventSource_OnStateExit;
    24.     }
    25.  
    26.  
    27.     private void OnDisable() {
    28.         StateMachineStateEventSource.Enter -= StateMachineEventSource_OnStateEnter;
    29.         StateMachineStateEventSource.Update -= StateMachineEventSource_OnStateUpdate;
    30.         StateMachineStateEventSource.Exit -= StateMachineEventSource_OnStateExit;
    31.     }
    32.  
    33.  
    34.     private void StateMachineEventSource_OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    35.         if (Filter != null && animator != Filter) return;
    36.  
    37.         foreach (var item in Map) {
    38.             if (stateInfo.IsName(item.StateId) || stateInfo.IsTag(item.StateId) || stateInfo.shortNameHash == Animator.StringToHash(item.StateId)) {
    39.                 item.OnStateEnter.Invoke();
    40.                 break;
    41.             }
    42.         }
    43.     }
    44. // ....
    45. }
    To my delight, I discovered that I could add a behaviour to the top-level StateMachine and it would be automatically added to all States under that machine.

    However, I have recently started to use SubstateMachines. Only to find that:
    1. The behaviour scripts from the top-level SM are cloned/attached to states inside a SubstateMachine
    2. The behaviour scripts from the top-level SM are not cloned/attached to the SubstateMachine nodes
    3. Any behaviours added to the SubstateMachine nodes are cloned/attached to internal states

    Now, all I want to do is raise an event for entering the SubstateMachine (I'm using the Enter/Exit nodes), but I obviously do not want to raise duplicated enter/exit events for internal states.

    Please advise on a means of making this happen, or please consider this a feature/issue request.
     
    Threeyes likes this.