OnExit Node in State Machines

Hello, dear Unity Developers and dear Community!

I have read several topics related to State Machine in the Bahvior Graph and noticed that the current possible implementation of the State Machine in the Bahvior Graph is limited.

First of all, I understand that in Behavior Trees it’s better to stick to the Tree model instead of State Machine because when it’s more than 5 states, the transitions between state if difficult to handle. But in my project I use combined approach: I use Trees as decision makers of what state should be enabled right now and at the same time I use State Machine for better visual representation.

Also, I use Subgraphs for each state to hide its complexity and reuse the states for other enemies.
Here is how my state machine looks like:

It works well, but it has a huge limitation: it’s possible to simulate OnStart for each state, but it’s impossible to simulate OnExit for them without increasing the complexity of the graph.

Let’s imagine that I have a Player Detector that detects players around the enemy. It’s a performance-consuming operation and I enable it only when the enemy is in Patrol state.
So, as soon as I entered the Patrolling Subgraph, I enable that detector. But when I exit the Patrol state, I need to disable the detector somehow.
As you know, there is an OnStart event in each Graph, but there is no OnEnd/OnExit event.

As a result, I have to disable the detector somewhere else. For instance, I can disable it in each state at the start, but that’s not right because other states must not know about the Player Detector at all (if we follow good architecture and code design).

Except for the bad architecture, we’ll have a complexity issue: every time we need to enable/disable something for states, we must duplicate it in other states which increases complexity of the Graph.

And it’s not only about the detector. The detector is just an example. We can also enable/disable bool parameters of the Animator (e.g. IsPatrolling should be enabled in the Patrol state and disabled when not in the Patrol state).

So, this is why OnEnd/OnExit built-in node would be a cool thing. Is it possible to implement such a node from Unity’s side? I think yes, because they have such thing as Interruptions. If a branch is interrupted, code of Unity developers calls the OnEnd() method in actions that are updating right now. But I’m not sure about the nodes that do not update at runtime: there is a chance that the OnEnd node will not be called if a branch was not interrupted and was just switched. Anyway, I hope Unity devs in this discussion will tell me if it’s possible.

What workarounds I found so far:

  1. Duplicating of the enable/disable actions in other states (the approach that we discussed above). It’s a bad decision - it violates architecture/design and increases complexity.
  2. Split each Subgraph state into OnEnter and OnExit Subgraphs (e.g. Subgraph_Idle_OnEnter, Subgraph_Idle_OnExit), compare CurrentState with PreviousState, and duplicate the Switch on the picture above so that we call OnExit for the PreviousState when CurrentState is changed and then OnEnter for the CurrentState. It’s a bad decision - it increases complexity.
  3. Create OnExit Event and wait for the event to be triggered in each Subgraph. This sounds like a solution, but unfortunately, it increases complexity outside of the Bahvior Graphs. The events that developers provide us are global events based on ScriptableObjects. A long time ago on the Unity YouTube channel developers proposed us EDA architecture using ScriptableObjects. So, the same approach is used here in Behavior Graphs. If you know what is this, you already noticed the problem: the ScriptableObject events should be duplicated for each case in order to avoid collisions between different kind of enemies and even different instances of enemies. But creating different events for different creatures is time-consuming and can confuse other developers. I would like to re-use the same Behavior Tree for different enemies and do not want to create 100+ events.

Dear Unity Developers and dear Community, could you please help me with my problem? I don’t know how to make the OnExit for the State Machine and not increase the complexity of the Graph at the same time.
What approach would you use if you were me?

Platform: Windows 10
Behavior Version: 1.0.7
Unity Version: 6000.0.25f1

Thank you very much for your time!

Hey @unity_30D89FB79A72844A3914 ! I fully agree and we’ve talked about it in the team and want to better support state machines!

For now, here’s an idea:

Instead of using Restart If for the state change, use an Event set to restart on state change and send both the new and the old state. Then you can have a switch to handle your exit before going on to your loop to handle the current state.

Hello! Thank you for such a quick reply!

Are you talking about something like this:

It’s not a working code, I just quickly tried to represent what you just suggested, in order to understand if I got the idea.

Questions:

  1. So, all the Subgraphs running simultaneously, but they just waiting for their time (they wait until the event triggers), correct?
  2. In each state Subgraph we pass the Current and Previous states and implement logic of OnEnter and OnExit, correct?
  3. Are the graphs I provided can be simplified or this is exactly what you meant?
  4. The OnStateChangedChannel: is this channel will have its own instance for every Behavior Agent, or will it spread across all the agents that use this channel?

Thank you!

Hey @unity_30D89FB79A72844A3914 ,

I tried to copy your state to show what I mean, I hope this helps. Basically you’ll remove the Restart If and use a Start On Event with Restart mode on (see the inspector). Then you can go through the cleanup phase for the old state and move to the new state. I also added a setup member to the enum to allow identifying you just started.

1 Like

Thank you for your code @ShaneeNishry !
The code really works and implements OnExit, OnEnter calls.

I re-made your approach a bit to fit the Subgraph-As-A-State approach.

As I said, Behavior Tree is a decision maker in my game in the main graph and since it contains sensitive info you will just believe me that it exists and it calls the Send State Changed when the State is really changed.

Then I have the states represented as subgraphs in the main graph:

Each State (Subgraph) contains a listener for the event from the main graph that splits code into OnExit and OnEnter parts:

Thank you again @ShaneeNishry , this is the best workaround so far!

1 Like

Heeey! I’m glad it helped. Good luck with the game! Let us know when you got something to show :slight_smile: