Skip to main content

State transitions

In order to define relations between states you need to create transitions. In a nutshell, a transition specifies a possible state change from an origin state to a target state when a specific action is triggered. As many transitions as needed can be defined.

Simple transitions

Simple transitions only define the origin state, target state and the action that triggers the transition. You can add a transition using the AddTransition method from the defined state, indicating the action and the target state. Example:

var state = mb.AddState("StateA");
state.AddTransition("switch", "StateB");

The target state is StateB and the action is switch.

Lets see a working example:

This example shows us a transition from StateA to StateB. It only happens when the action toB is triggered.

Let's see another example:

This example shows us two transitions from StateA:

  • To StateB when the action toB is triggered.
  • To StateC when the action toC is triggered.

We can see that the target state depends on the action that is triggered.

Conditional transitions

Conditional transitions work as simple transitions but they only work when a provided method returns true. This is useful when you want to disable transitions based on programmatic behavior. One popular case is disabling transitions depending on the context value.

You can add a condition to a simple transition by using the When method from the transition instance. Example:

var stateA = mb.AddState("StateA");
var transitionToB = stateA.AddTransition("toB", "StateB");

transitionToB.When((info) => {
// When we return `true` the transition is enabled. Otherwise it is disabled.
return true;
});

Let's use the day counter example. The machine should stop transitioning to night once the days count reaches 10. This example uses the machine context, read the context documentation to understand it better:

var machineBlueprint = StateMachine<string, string, int>.Factory((mb) => {
// Define states
var day = mb.AddState("DAY");
var night = mb.AddState("NIGHT");

// Define transitions
var dayTransition = day.AddTransition("skip", "NIGHT");
night.AddTransition("skip", "DAY");

// Define conditions
dayTransition.When((info) => {
return info.Machine.Context < 10; // Allows the transition only when days count (stored in the context) is less than 10
});

// Define events
day.OnEnter((info) => {
var machine = info.Machine;
machine.MutateContext((context) => context + 1); // Increment the days count
});
}, "DAY", 0); // Initial state is 'DAY' and initial context is '0'

Multiple conditions

One transition can have multiple conditions. You can add more than one by simply invoking the When method again. For example, let's add another condition to the past example:

// ...

// Define conditions

// Same condition as the previous example
dayTransition.When((info) => {
return info.Machine.Context < 10;
});

// New condition that will also be evaluated
dayTransition.When((info) => {
return false;
});

// ...

Even thought the first condition will sometimes return true the second one will prevent the transition from changing the state. This is because the machine engine treats both conditions as a single AND condition (condition1 AND condition2), so if one of them is false the transition is disabled.

Condition dependant transitions

Conditional transitions allow you to create condition dependant transitions. This happens when a transition needs a condition to obtain a proper working machine. For example, when a state has different two transitions to two different states with the same action. See the diagram:

We can see that StateA has two transitions (to StateB and StateC) with the action change. This is a valid state machine, but it might lead to unexpected behaviors as the machine has no priority on which state it has to choose. Here is where conditional transitions come in.

To solve this you only need to specify a condition to the state that has to be conditionally evaluated before triggering the transition. This way the state machine will prioritize transitioning to the conditional transition. If the condition fails it tries to transition with the other transitions. Let's update the example.

With the previous modification you prioritized StateC over StateB when transitioning from StateA with the action change. If StateC transition condition fails it will transition to StateB.

Keep in mind

If more than one transition has a condition you cannot known which transition will be picked first. You only known conditional transitions are evaluated before non conditional transitions.

Transitions from any state

Sometimes you might want to define an action that triggers a transition to a specific state from any state. This can be done by using the special any state. Take the following example:

When using the AnyState() you are given an anonymous state (it behaves like a state, but it has no state code). You can define more than one transition and add conditions.

Important

Transitions which origin is AnyState have maximum priority. If other transitions sharing the same action code are defined, they will be evaluated after the transitions from AnyState. It does not matter if AnyState transitions have no conditions attached.