Submachine States

Layering FSMs the easy way

A third alternative to the state classes has been introduced in v1.5 of GC FSM. Similarly to Local States, the intent is to avoid creating separate implementation classes, with the main difference that the new state runs a sub-FSM defined in the same object as the state node.  In order to create an FSM Submachine State, right-click on the background of the event graph and select “FSM Submachine State” from the GC FSM category. You can immediately edit the name of the state.

This node is essentially equivalent to a regular State that calls a Launch FSM in its OnEnter handler. However, since the sub-FSM is defined in the same object, its implementation has direct access to the object internals, without the need to pass through the Context variable.

Please notice that is a subtle, but important, difference between a Submachine State and a Local State that launches an FSM: in the case of a Submachine State the new FSM is actually a sub-FSM that will be interrupted if the Submachine State is exited, while we saw earlier that handlers of a Local State launch FSMs at the same hierarchy level as the original FSM.

You can rename the sub-FSM at any moment and the Submachine Node will be automatically updated. If you delete the sub-FSM, the node will be invalidated.

The same sub-FSM can be referenced in multiple Submachine State nodes. However, launching an FSM while it is already running will fail and may produce unexpected results. Don’t do that!

Once an FSM Submachine State has been assigned an FSM, double clicking on the node will bring you to the sub-FSM node.

You can switch a state node from regular state node to local state node at any time, by right-clicking on the node an select the “Convert to Regular/Local State” command in the context menu

How to Create a Submachine State node
A Submachine State node

Submachine states with history (new in v1.7)

Since v1.7 of GC FSM, Submachine States can keep their history. This can be activated by selecting “Non-replicated, keep history” as the value on the “Replication” pin. This settings changes the behaviour of the Submachine State as follows:

  1. The first time the Submachine State is entered it behaves as a regular Submachine State and the corresponding FSM is launched as usual;
  2. Every time the Submachine State is exited, the state that was active in the FSM is saved;
  3. When the Submachine State is re-entered, the FSM is launched but the state that was last active is restored instead of re-starting from the beginning

This behaviour is achieved by internally storing a snapshot whenever the state is exited and restoring it when it is re-entered. Since snapshots won’t work on replicated states, this means a Submachine State with History can’t be replicated.

Submachine States with History can be used to obtain a result similar to the UML Deep History Pseudostate of an UML Submachine State.

The internal snapshot is stored in the FSM that includes the Submachine State. If that FSM is stopped, the internal snapshot will be destroyed and the Submachine State will be lost.

A Submachine State history will be properly saved and restored in the parent FSM snapshots.

A Submachine State with History

How to implement “Any State” transitions with Submachine States

Other FSM design tools introduce the concept of “Any State”, that is a pseudo state that handles events regardless of the actual state the FSM is running. That approach is powerful, but promotes poorly structured FSMs that eventually become a burden to maintain and evolve. You can usually achieve the same result by organizing your FSMs in layers and Submachine State help you keep the different layers in the same object.

In the following example we see a a very simple FSM using the “Any State” facility. It has normal three states: Idle, Move and Shoot.

An example FSM using "Any State"

The “normal” execution flow occures between the Idle and Move states. However, the presence of the “Any State” introduces an “exceptional” flow which is triggered by the user pressing the fire button: in that case the FSM exits whatever state (Idle or Move) it’s in and makes a transition to the Shoot state. When the user eventually stops firing, the FSM gets back to Idle and resumes the normal flow.

Notice that this approach doesn’t visually render the presence of the two distinct flows, especially since the one with the highest priority (the “shoot”) is not represented if it weren’t for the “Any State”.

This is how you could implement the same scenarion with a two-layer FSM in GC FSM. There’s a main FSM handling the higher priority shoot/no-shoot transition and a sub-FSM dealing with the regular movement.

A two-layer FSM implementing the same behaviour