Asset - Scripts [STATE MACHINES] xState an advanced StateMachine system (FREE)

xDGameStudios

GameMaker Staff
GameMaker Dev.
[LINKS]

xState (marketplace)

xState (itch.io)

[DEMO IMAGES]
state.gif

Enemies walk using a two states "idle" and "wander" (repeatedly). Mouse clicking will change near-by enemies to "suspicious" state (for a couple of frames). If another click occurs during "suspicious" state the enemy will go into "alert" state and move towards the click. Pressing "F" on keyboard will make enemies move to a "freeze" state (if they are not on a "suspicious" or "alert" state) - the "freeze" state belongs to another StateMachine so we are actually swapping behaviors.

Below is an image of how state machines are defined:

Example2.png


[INFORMATION]
This is a context-less and fluent like implementation of a finite state machine system asset for GMS2.3+


[SETUP]
1) The state machine can (actually should) be defined inside a script file (GLOBAL) following the code example:

GML:
stateMachine = new StateMachine();
stateMachine.configure("stateA")
    .onEntry(function() { show_debug_message("Entered State A"); })

// NOTE: stateMachine is in GLOBAL scope
2) In order to use it we create a context for the state machine inside the INSTANCE we want to use the state machine:

GML:
fsmContext = global.stateMachine.build(id, "stateA");
3) Finally during step event the state machine context needs to be update:

GML:
fsmContext.update(delta_time);
[USAGE]
This library uses a fluent-like way of creating state machines for so when defining states using the 'configure' method we are actually creating a StateConfiguration instance that allows chaining methods to add logic to our state, the available methods are:
  • onEntry(func) : sets a function to be called when entering the state, params: (fsmContext, transitionData, eventArgs)
  • onUpdate(func) : sets a function to be called when updating the state, params: (fsmContext, deltaTime)
  • onExit(func) : sets a function to be called when exiting the state, params: (fsmContext, transitionData)
  • onActivate(func) : sets a function to be called when activating (manual call), params: (fsmContext)
  • onDeactivate(func) : sets a function to be called when deactivating (manual call), params: (fsmContext)
  • substateOf(stateId) : marks the state as a substate of another state (superstate logic will be called before his own)
  • permit(eventId, targetStateId) : this sets a transition trigger that will change the state to a target state.
  • permitIf(eventId, targetStateId, guardFunc) : same as above but there will be a guard condition.
  • permitDynamic(eventId, selectorFunc) : same as 'permit' but the target is dynamically determined.
  • permitDynamicIf(eventId, selectorFunc, guardFunc) : same as above but there will be a guard condition.
  • permitReentry(eventId) : same has 'permit' the target state is the same state.
  • permitReentryIf(eventId, guardFunc) : same as above but there will be a guard condition.
Example:
GML:
enemyFSM = new StateMachine();

enemyFSM.configure("alive")
    .onEntry(function() { .... })
    .permit("fire", "dead")

enemyFSM.configure("dead")
    .onEntry(function(_fsmContext, _transitionData, _eventArgs) { .... })
Now that we know how the states are configured we need to understand how StateMachineContext works.

When you define a StateMachine (FSM) it doesn't belong to any particular instance and this makes this system extremely powerful since you can share the same FSM with all the instances with the same behavior (meaning that thousands of enemies will only need ONE state machine).

Also thanks to this context system the code defined in the 'onEntry', 'onUpdate', 'onExit', 'onActivate', 'onDeactivate' and also guard condition will be executed from the scope of the instance running the state machine. The StateMachineContext allows the following controls:

GML:
fsmContext = global.stateMachine.build(id, "initialState");

fsmContext.fire(eventName) // fires the event with that name and triggers the transitions (ON NEXT STEP)

fsmContext.moveTo(stateId) // force-transition to the target state (IMMEDIATELY)

fsmContext.isInState(stateId) // checks if we are in the given state (takes into account substates)

fsmContext.getStateId() // returns the id of the current state

fsmContext.activate(stateId) // activates the given state

fsmContext.deactivate(stateId) // deactivates the given state

fsmContext.switchMachine(stateMachine, initialStateId) // replaces the state machine being used

fsmContext.currentState // this is a (READONLY) property that references the current state instance
One of the functions I would like to give special highlight is the switchMachine with this function you are able to switch between state machines on-the-fly for advanced and complex behaviors. States can also be defined as classes inherited from StateRepresentation constructor function (there is an example inside the demo of this under ADVANCED USAGE)

[NOTES]
The demo project serves as an example for the State Machine System being used all the demo specific code is inside the Demo folder of the asset pack.

[COMPATIBILITY]
The asset is fully compatible with 2.3+ and is purely written in GML making it compatible with all exports available.
 
Last edited:
Top