OnStateMachineExit is not being fired on sub state machines whenever a transition out of “Any State” is fired. It only works when the sub state machine exits through the exit node.
I need a method that gets fired on exit ALWAYS, no matter how I’m exiting.
I’ve been digging through the forums, reddit, and done a ton of experiments, including trying out this fix: https://github.com/Baste-RainGames/AnimatorFix
This is a well covered problem that is talked about here and other posts but apparently still has no fix:
Nothing works.
I really need this for my animation system as I’ve designed a huge amount of animation and systems using StateMachineBehaviors already. But I need to be able to clean up / reset certain things when I’m exiting a sub-state machine. Right now there is no reliable way to do that.
Not only would it be great to have a method like OnStateMachineExit that actually fires whenever exiting, but also to have a true “exit” state that ALWAYS gets fired on exit that I could drop behaviours on that I can use to cleanup/reset stuff.
To achieve the “true exit” you could try adding script directly on the BaseLayer of macanim.
When you are in the animator layer go to the base layer (exit all the sub behaviours) and tap on an empty space. Then you can add code directly on the base layer (or any layer) like this. If you open the created class there is a comment which says:
Now, I did not test it, but it should be called when any state in the machine exits. You can get info about the state that currenty ended from stateInfo.
Yeah, the functionality of OnStateMachineExit should still be changed to fire when the state machine exists. The current behavior is useless, so while it would be a “breaking change”, nothing of value would be broken.
Baste, awesome, yeah it would be amazing if Unity fixed it.
In the mean time, might be cool to try CloudKids technique of adding a behaviour at the top level state machine, instead of adding it to the GameObject with the animator on it. That might also resolve your having to select the object every time you update it.
The callback should be renamed to OnEntryNode/OnExitNode, the callback is badly named I agree but still the callback is useful to select what is gonna be the next state when you enter a Statemachine.
the nice thing about this callback is that it get called while the statemachine is been evaluated and just before we evaluate all transitions so you can set parameter that will have a direct impact on which transition gonna get triggered.
Maybe it not useful for you, but it’s for some other users.
As @CloudKid mentionned the only way to get a reliable StateMachine.OnStateExit/OnStateEnter is to add a behaviour directly on the statemachine itself.
For the issue with interrupted transition not calling OnStateExit a fix was submited to trunk this week, if everything goes well I will backport this fix to 5.5.
You would achieve exactly the same thing with a real OnStateMachineEnter, as long as AnimatorTransitionInfo gets an .machineTransition similar to .anyState. I’m not arguing that a callback that fires when you enter a state machine is not a good thing, I’m arguing that only calling it half the time you enter a state machine makes the system incredibly unwieldy.
What does a OnEntryNode/OnExitNode achieve that a MachineEntry/Exit callback doesn’t also achieve?
I agree with Baste (and many others who have complained about this)
So why not rename the current function to OnEnterNode/OnExitNode
and also make an actual reliable OnStateMachineEnter/Exit function that gets fired always.
I don’t want to have this at the top level either, I want to be able to put it on the sub state machine so I can clean up or reset things when the sub state machine is exited for any reason in any wY (not just through exit)
It the timing of callback, the OnEntryNode/OnExitNode is called while we do evaluate the state machine just before evaluating transitions conditions, all other statemachine behaviour callback are processed after the statemachine evaluation.
So an OnEntryNode/OnExitNode callback can change the flow of evaluation of the current frame, all other callback can only influence the next frame.
The real problem is not to argue against OnStateMachineEnter callback, we all agree that it would be a great addition.
The problem is how we could change the system without breaking any project already using OnStateMachineEnter the way it was designed. We could say we don’t care and change the behaviour but tomorow we will get a shit tons of users with regression.
So it only a matter of how to implement this without breaking project compatibility, the easiest solution is too choose another name for the callback. OnStateMachineStateEnter().
I did bring this to the animation meeting this morning and we all agree that it need to be done it only a matter of how which I need to sort out with our scripting team
Thanks for the response! It’s great to see that this is making progress.
I also totally understand the problem with the evaluation order. I had forgotten that whole aspect about the callback.
On that note, the callback should then be called OnBeforeEntryNode or just BeforeEntryNode or something like that. The ordering of these things are very important, and should be a part of the name so users notice it. OnEntryNode isn’t a problem in itself - all examples will probably use the fact that it resolves early, so users will understand that.
The problem is that if that method is named “OnEntry” and is evaluated before the machine, users will intuitively assume that the “On…Enter” methods also gets evaluated before the machine. If Before is a part of the EntryNode’s callback name, the difference will be a part of the naming.
Ok now that I’m actually trying to implement the technique of putting a StateMachineBehaviour at the very top of the entire state machine, I have no idea how to actually detect which sub-state machine I’m in. I’d like to just be able to, in the OnStateExit check which sub-state machine I’m exiting from by name.
How would you go about making a script that automatically does that? It’s possible to do it from a menu button or a shortcut or a button or whatever, but is it possible to make a thing that automatically notices that an AnimatorController has been updated and sets the correct tags?
I have been able to have a MonoBehaviour’s editor modify an Animator’s controller, by using reflection to get at the controller, but I can’t see how you’d automate the entire thing?
Maybe from the SMB’s editor, but can you get from a SMB to the animator that instance is attached to at editor time in any reasonable way?
Like you said with a menu or button, it’s not ideal but as a workaround it does work.
We do have an internal message that we send when a controller is modified, we could expose it and that would allow you to do all kind of automatic setup when needed from a script
Then I could do things like ensuring that an AnimatorController that’s meant to be an AI state machine only has zero-length transitions, and store metadata on StateMachineBehaviours.
I’ve got some prefabs that break if I change the AnimatorController that’s attached to the prefab unless I select the prefab and let a script’s behaviour parse the changes to that AnimatorController. This would allow me to store the necessary metadata about the controller directly IN the controller instead of on the prefab.
Look like we already have an internal System.Action callback on the AnimatorController class
public sealed partial class AnimatorController : RuntimeAnimatorController
{
internal System.Action OnAnimatorControllerDirty;
…
}
with reflection you could find it and setup your own callback
all our tools are hooked on this callback already so be careful and use += or -= when adding your own callback otherwise animation tools will stop responding.
@Baste let me know if you try it and it does work, I’m curious.
There is alot of user operation that will trigger this callback, so i’m wondering if you could run into performance issue if the process that you run when you get this callback is heavy.
In the meantime, as far as I can see, the only way to get at the animator controller of an animator is through reflection. I can get it through SerializedObject.FindProperty, but that seems to fail at times. Here’s my code:
private bool TryFindAnimatorController(out AnimatorController controller) {
var anim = GetComponent<Animator>();
if (anim == null) {
controller = null;
return false;
}
var serializedObject = new SerializedObject(anim);
var serializedProperty = serializedObject.FindProperty("m_Controller");
var objectReferenceValue = serializedProperty.objectReferenceValue;
if (objectReferenceValue == null) {
controller = null;
return true;
}
controller = (AnimatorController) objectReferenceValue;
return false;
}
This manages to find the controller from OnBeforeSerialize, but if I run it in edit-mode Awake ([ExecuteInEditMode] MonoBehaviour), it fails.
In that vein, it would be really nice to have this method: