Hey guys, I’ve been working on a project which has involved a lot of StateMachineBehaviours and requires a lot of linking to things in the scene. As such I have made an extension to StateMachineBehaviour which helps with that. The you create a normal MonoBehaviour which has all your scene references and in it’s Start function it calls Initialise. Each of the StateMachineBehaviours you create should inherit from SceneLinkedSMB and use the MonoBehaviour you created as the generic type. So for example:
public class MyMonoBehaviour : MonoBehaviour
{
public Rigidbody someSceneRigidbody;
public Animator animator;
void Start ()
{
SceneLinkedSMB<MyMonoBehaviour>.Initialise (animator, this);
}
}
Then your StateMachineBehaviours would look like:
public class MySceneLinkedSMB : SceneLinkedSMB<MyMonoBehaviour>
{
public override void OnSLStatePostEnter (Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
m_MonoBehaviour.someSceneRigidbody.isKinematic = true;
}
}
The SceneLinkedSMB also contains much more granular function calls so you can easily control exactly when things happen. The script itself contains an explanation of each of its functions.
using UnityEngine;
using UnityEngine.Animations;
public class SceneLinkedSMB<TMonoBehaviour> : SealedSMB
where TMonoBehaviour : MonoBehaviour
{
protected TMonoBehaviour m_MonoBehaviour;
bool m_FirstFrameHappened;
bool m_LastFrameHappened;
public static void Initialise (Animator animator, TMonoBehaviour monoBehaviour)
{
SceneLinkedSMB<TMonoBehaviour>[] sceneLinkedSMBs = animator.GetBehaviours<SceneLinkedSMB<TMonoBehaviour>>();
for (int i = 0; i < sceneLinkedSMBs.Length; i++)
{
sceneLinkedSMBs[i].InternalInitialise(animator, monoBehaviour);
}
}
protected void InternalInitialise (Animator animator, TMonoBehaviour monoBehaviour)
{
m_MonoBehaviour = monoBehaviour;
OnStart (animator);
}
public sealed override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
{
m_FirstFrameHappened = false;
OnSLStateEnter(animator, stateInfo, layerIndex);
OnSLStateEnter (animator, stateInfo, layerIndex, controller);
}
public sealed override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
{
if (animator.IsInTransition(layerIndex) && animator.GetNextAnimatorStateInfo(layerIndex).fullPathHash == stateInfo.fullPathHash)
{
OnSLTransitionToStateUpdate(animator, stateInfo, layerIndex);
OnSLTransitionToStateUpdate(animator, stateInfo, layerIndex, controller);
}
if (!animator.IsInTransition(layerIndex) && !m_FirstFrameHappened)
{
m_FirstFrameHappened = true;
OnSLStatePostEnter(animator, stateInfo, layerIndex);
OnSLStatePostEnter(animator, stateInfo, layerIndex, controller);
}
if (!animator.IsInTransition(layerIndex))
{
OnSLStateNoTransitionUpdate(animator, stateInfo, layerIndex);
OnSLStateNoTransitionUpdate(animator, stateInfo, layerIndex, controller);
}
if (animator.IsInTransition(layerIndex) && !m_LastFrameHappened && m_FirstFrameHappened)
{
m_LastFrameHappened = true;
OnSLStatePreExit(animator, stateInfo, layerIndex);
OnSLStatePreExit(animator, stateInfo, layerIndex, controller);
}
if (animator.IsInTransition(layerIndex) && animator.GetCurrentAnimatorStateInfo(layerIndex).fullPathHash == stateInfo.fullPathHash)
{
OnSLTransitionFromStateUpdate(animator, stateInfo, layerIndex);
OnSLTransitionFromStateUpdate(animator, stateInfo, layerIndex, controller);
}
}
public sealed override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller)
{
m_LastFrameHappened = false;
OnSLStateExit(animator, stateInfo, layerIndex);
OnSLStateExit(animator, stateInfo, layerIndex, controller);
}
/// <summary>
/// Called by a MonoBehaviour in the scene during its Start function.
/// </summary>
public virtual void OnStart(Animator animator) { }
/// <summary>
/// Called before Updates when execution of the state first starts (on transition to the state).
/// </summary>
public virtual void OnSLStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
/// <summary>
/// Called after OnSLStateEnter every frame during transition to the state.
/// </summary>
public virtual void OnSLTransitionToStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
/// <summary>
/// Called on the first frame after the transition to the state has finished.
/// </summary>
public virtual void OnSLStatePostEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
/// <summary>
/// Called every frame when the state is not being transitioned to or from.
/// </summary>
public virtual void OnSLStateNoTransitionUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
/// <summary>
/// Called on the first frame after the transition from the state has started. Note that if the transition has a duration of less than a frame, this will not be called.
/// </summary>
public virtual void OnSLStatePreExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
/// <summary>
/// Called after OnSLStatePreExit every frame during transition to the state.
/// </summary>
public virtual void OnSLTransitionFromStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
/// <summary>
/// Called after Updates when execution of the state first finshes (after transition from the state).
/// </summary>
public virtual void OnSLStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
/// <summary>
/// Called before Updates when execution of the state first starts (on transition to the state).
/// </summary>
public virtual void OnSLStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }
/// <summary>
/// Called after OnSLStateEnter every frame during transition to the state.
/// </summary>
public virtual void OnSLTransitionToStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }
/// <summary>
/// Called on the first frame after the transition to the state has finished.
/// </summary>
public virtual void OnSLStatePostEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }
/// <summary>
/// Called every frame when the state is not being transitioned to or from.
/// </summary>
public virtual void OnSLStateNoTransitionUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }
/// <summary>
/// Called on the first frame after the transition from the state has started. Note that if the transition has a duration of less than a frame, this will not be called.
/// </summary>
public virtual void OnSLStatePreExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }
/// <summary>
/// Called after OnSLStatePreExit every frame during transition to the state.
/// </summary>
public virtual void OnSLTransitionFromStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }
/// <summary>
/// Called after Updates when execution of the state first finshes (after transition from the state).
/// </summary>
public virtual void OnSLStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) { }
}
public abstract class SealedSMB : StateMachineBehaviour
{
public sealed override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
public sealed override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
public sealed override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) { }
}
I hope you find this useful.