Click or drag to resize

SimpleStateMachineT Class

Simple state machine that allows tracking of the states and checking the basic transition conditions.
Inheritance Hierarchy
SystemObject
  KSPDev.ProcessingUtilsSimpleStateMachineT

Namespace:  KSPDev.ProcessingUtils
Assembly:  KSPDev_Utils.2.5 (in KSPDev_Utils.2.5.dll) Version: 2.5 for KSP v1
Syntax
C#
public sealed class SimpleStateMachine<T>
where T : struct, new(), IConvertible
Request Example View Source

Type Parameters

T
The enum to use as the state constants.

The SimpleStateMachineT type exposes the following members.

Constructors
Properties
  NameDescription
Public propertyCode examplecurrentState
Current state of the machine.
Public propertyCode exampleisStrict
Tells if all the transitions must be explicitly declared.
Public propertymachineIsInactive
Tells if the machine was shutdown.
Top
Methods
  NameDescription
Public methodCode exampleAddStateHandlers
Adds a state change event.
Public methodCode exampleCheckCanSwitchTo
Verifies if the machine can move into the desired state.
Public methodRemoveHandlers
Removes a state change event handler.
Public methodResetTransitionConstraint
Clears the transitions for the source state if any.
Public methodCode exampleSetTransitionConstraint
Defines a state and the allowed target states for it.
Top
Events
  NameDescription
Public eventCode exampleonAfterTransition
Event that fires when the state machine has changed its state.
Public eventCode exampleonBeforeTransition
Event that fires before the state machine has changed its state.
Top
Remarks
If a module has more that two modes (which can be controlled by a simple boolean) it makes sense to define each mode as a state, and introduce a definite state transition diagram. Once it's done, a state machine can be setup by defining which transitions are allowed. At this point the module will be able to just react on the state change events instead of checking multiple conditions.
Examples
/// <summary>A class that demonstrates a simple state module with three states.</summary>
/// <remarks>
/// There are the following rules for the state changes:
/// <list type="bullet">
/// <item>The state <c>One</c> can be transitioned into both <c>Two</c> and <c>Three</c>.</item>
/// <item>The states <c>Two</c> and <c>Three</c> can only return back to <c>One</c>.</item>
/// <item>In states <c>Two</c> and <c>Three</c> different menu options are available.</item>
/// <item>In state <c>One</c> no menu options are available.</item>
/// </list>
/// </remarks>
class SimpleStateMachine1 : PartModule {
  public enum State {
    One,
    Two,
    Three
  }

  [KSPField(isPersistant = true)]
  public State persistedState = State.One;  // ALWAYS provide a default value!

  SimpleStateMachine<State> stateMachine;

  [KSPEvent(guiName = "State: TWO")]
  public void StateTwoMenuAction() {
    Debug.Log("StateTwoMenuAction()");
  }

  [KSPEvent(guiName = "State: THREE")]
  public void StateThreeMenuAction() {
    Debug.Log("StateThreeMenuAction()");
  }

  public override void OnAwake() {
    stateMachine = new SimpleStateMachine<State>(strict: true);
    // State ONE can be transitioned into both TWO and THREE.
    stateMachine.SetTransitionConstraint(
        State.One,
        new[] {State.Two, State.Three});
    // State TWO can only get back to ONE.
    stateMachine.SetTransitionConstraint(
        State.Two,
        new[] {State.One});
    // State THREE can only get back to ONE.
    stateMachine.SetTransitionConstraint(
        State.Three,
        new[] {State.One});
    // No menus available in state ONE.
    stateMachine.AddStateHandlers(
        State.One,
        enterHandler: oldState => {
          Events["StateTwoMenuAction"].active = false;
          Events["StateThreeMenuAction"].active = false;
        },
        leaveHandler: newState => Debug.LogFormat("Move from ONE to {0}", newState));
    // Only TWO-menu is available in the state TWO.
    stateMachine.AddStateHandlers(
        State.Two,
        enterHandler: oldState => {
          Events["StateTwoMenuAction"].active = true;
          Events["StateThreeMenuAction"].active = false;
        });
    // Only THREE-menu is available in the state THREE.
    stateMachine.AddStateHandlers(
        State.Three,
        enterHandler: oldState => {
          Events["StateTwoMenuAction"].active = false;
          Events["StateThreeMenuAction"].active = true;
        });
  }

  public override void OnStart(PartModule.StartState state) {
    stateMachine.currentState = persistedState;  // Restore state from the save file.
  }

  void OnDestory() {
    // Usually, this isn't needed. But if code needs to do a cleanup job it makes sense to wrap
    // it into a handler, and stop the machine in the Unity destructor.
    stateMachine.currentState = null;
  }

  public override void OnUpdate() {
    if (Input.GetKeyDown("1")) {
      // This transition will always succceed. 
      stateMachine.currentState = State.One;
    }
    if (Input.GetKeyDown("2")) {
      // This transition will only succceed if current state is MyState.One. 
      stateMachine.currentState = State.Two;
    }
    if (Input.GetKeyDown("3")) {
      // This transition will only succceed if current state is MyState.One. 
      stateMachine.currentState = State.Three;
    }
  }
}
See Also