All Projects → UnterrainerInformatik → FiniteStateMachine

UnterrainerInformatik / FiniteStateMachine

Licence: Unlicense license
This project is a finite state machine designed to be used in games.

Programming Languages

C#
18002 projects

Projects that are alternatives of or similar to FiniteStateMachine

Fluent State Machine
Fluent API for creating state machines in C#
Stars: ✭ 195 (+333.33%)
Mutual labels:  fsm, state-machine, finite-state-machine
Statecharts.github.io
There is no state but what we make. Feel free to pitch in.
Stars: ✭ 265 (+488.89%)
Mutual labels:  fsm, state-machine, finite-state-machine
simple-state-machine
A simple Java state machine for Spring Boot projects
Stars: ✭ 25 (-44.44%)
Mutual labels:  fsm, state-machine, finite-state-machine
Nanostate
🚦- Small Finite State Machines
Stars: ✭ 151 (+235.56%)
Mutual labels:  fsm, state-machine, finite-state-machine
Jstate
Advanced state machines in Java.
Stars: ✭ 84 (+86.67%)
Mutual labels:  fsm, state-machine, finite-state-machine
pastafarian
A tiny event-based finite state machine
Stars: ✭ 20 (-55.56%)
Mutual labels:  fsm, state-machine, finite-state-machine
statemachine-go
🚦 Declarative Finite-State Machines in Go
Stars: ✭ 47 (+4.44%)
Mutual labels:  fsm, state-machine, finite-state-machine
UnityHFSM
A simple yet powerful class based hierarchical finite state machine for Unity3D
Stars: ✭ 243 (+440%)
Mutual labels:  fsm, state-machine, finite-state-machine
Stately.js
Stately.js is a JavaScript based finite-state machine (FSM) engine for Node.js and the browser.
Stars: ✭ 785 (+1644.44%)
Mutual labels:  fsm, state-machine, transition
Stateless4j
Lightweight Java State Machine
Stars: ✭ 658 (+1362.22%)
Mutual labels:  fsm, transition, finite-state-machine
use-state-machine
Use Finite State Machines with React Hooks
Stars: ✭ 28 (-37.78%)
Mutual labels:  fsm, state-machine, finite-state-machine
Afsm
C++14 Finite State Machine library
Stars: ✭ 113 (+151.11%)
Mutual labels:  fsm, state-machine, finite-state-machine
xstate
State machines and statecharts for the modern web.
Stars: ✭ 21,286 (+47202.22%)
Mutual labels:  fsm, state-machine, finite-state-machine
Django Fsm
Django friendly finite state machine support
Stars: ✭ 1,898 (+4117.78%)
Mutual labels:  fsm, state-machine, finite-state-machine
Hal
🔴 A non-deterministic finite-state machine for Android & JVM that won't let you down
Stars: ✭ 63 (+40%)
Mutual labels:  state-machine, machine, finite-state-machine
Fsm As Promised
A finite state machine library using ES6 promises
Stars: ✭ 446 (+891.11%)
Mutual labels:  fsm, state-machine, finite-state-machine
Finity
A finite state machine library for Node.js and the browser with a friendly configuration DSL.
Stars: ✭ 88 (+95.56%)
Mutual labels:  fsm, state-machine, finite-state-machine
stateless
Finite State Machine porting from Stateless C#
Stars: ✭ 25 (-44.44%)
Mutual labels:  fsm, state-machine, finite-state-machine
Statemachine
A feature-rich, yet simple finite state machine (FSM) implementation in C
Stars: ✭ 168 (+273.33%)
Mutual labels:  fsm, state-machine
Rosmaro
Visual automata-based programming in functional JavaScript
Stars: ✭ 176 (+291.11%)
Mutual labels:  fsm, state-machine

license Twitter Follow

Project Package
StateMachine NuGet NuGet
MonoGameStateMachine (deprecated)
Please use the normal StateMachine instead
NuGet NuGet
Use the normal StateMachine and pass a TimeSpan on Update.
Java-Version: JavaStateMachine Olards Java Version on GitHub

Deprecation of MonoGameStateMachine

We simply didn't want to maintain two libraries of this size and the non-MG users wanted the functionality of the MonoGame version as well. So we compromised and ported the MG version back to the core-version replacing the GameTime reference, which really was the only thing we used from MG, which felt like a waste, with a TimeSpan everyone may use. Use it like this:

machine.Update(TimeSpan.FromMilliseconds(gameTime.ElapsedGameTime.TotalMilliseconds));

Finite-State-Machine

This project provides a Finite-State-Machine (FSM) as a portable class library (PCL) designed to be used in games.

Furthermore it implements even a Stack-Based-FSM (SBFSM). So you may tell it to 'continue with the last state before the active one'.

You describe your FSM using a nice and well documented DSL (Domain Specific Language).

If you're looking for a Java version of this project, check out Olards Java Version on GitHub.

If you like this repo, please don't forget to star it. Thank you.

IconStateMachine

Is the generic implementation in the form of a PCL (portable code library). It references no other library (no dependencies).

Nice if you want to use it outside of MonoGame.

Description

This replaces the code we usually had for keyboard-input (run-left-right-duck-jump), clicked buttons on the GUI (idle-over-down-refreshing), tower-states (idle-aiming-firing-reloading) or for the connection procedure when setting up peer2peer connections in our games (more complex; example further down).

The idea is to generate a single FSM for every 'layer' of input that your engine allows. In our example it's a multi-button GUI. Some of the buttons will stay pressed and the GUI will enter a 'selection-grid-mode' until the left button is pressed again. Some of them just do immediate actions and become immediately released afterwards. Some of both of those have a refresh-time and stay disabled for that period. In this example the state of the GUI ('selection-grid-mode' or not) would be such a machine.

We place those machines in a single class where they could 'talk with each other' by reading their respective states. That way it is possible to construct and react on compound states.

Example

State-Machine-Image jumping-diving

Fsm<State, Trigger>.Builder(State.STANDING)
  .State(State.DUCKING)
    .TransitionTo(State.STANDING).On(Trigger.UP)
  .State(State.STANDING)
    .TransitionTo(State.DUCKING).On(Trigger.DOWN)
    .TransitionTo(State.JUMPING).On(Trigger.UP)
  .State(State.JUMPING)
    .TransitionTo(State.DIVING).On(Trigger.DOWN)
    .State(State.DIVING)
  .Build();

A nice and more complex example for such a machine is the setup of a multiplayer game. It would be like the following:

Server:
  • Send the 'load level' signal to other players
  • Load level
  • Display 'waiting for other players' message-box
  • Wait for all other players to finish loading
  • Send the level-data to other players and wait for acknowledgement from each of them
  • Remove the 'waiting for other players' message-box
  • Send 'start' signal to other players
  • Start the game

Test-drive

Time to take it for a test-drive.

The motivation for this project came from a nice article I found here which comes with some examples. We tried to solve the proposed problems with our new project.

By the way: This seems to be a great book, so try to support the author in any way possible for you.

He's making a point using a FSM that looks like this:

  • ducking --(release down)--> standing
  • standing --(press down)--> ducking
  • standing --(press B)--> jumping
  • jumping --(press down)--> diving

So the file GameProgrammingPatterns1.cs in the test-folder contains that machine.

Usage

This is a short paragraph that is about an example what configuring a state machine actually looks like.

State-Machine-Image walking_running

State-Machine-Image standing_ducking

private enum VState { DUCKING, STANDING, JUMPING, DESCENDING, DIVING };
private enum VTrigger { DOWN_RELEASED, DOWN_PRESSED, UP_PRESSED, SPACE_PRESSED };

private enum HState { STANDING, RUNNING_LEFT, RUNNING_RIGHT, WALKING_LEFT,
                     WALKING_RIGHT, WALKING_DELAY_LEFT, WALKING_DELAY_RIGHT};
private enum HTrigger { LEFT_PRESSED, LEFT_RELEASED, RIGHT_PRESSED, RIGHT_RELEASED, SPACE_PRESSED };

private Fsm<HState, VTrigger> verticalMachine;
private Fsm<VState, HTrigger> horizontalMachine;

private Keys[] lastKeysPressed;
private Hero hero;

public void main() {
  horizontalMachine = Fsm<HState, HTrigger>.Builder(STANDING)
    .State(STANDING)
      .TransitionTo(WALKING_LEFT).On(LEFT_PRESSED)
      .TransitionTo(WALKING_RIGHT).On(RIGHT_PRESSED)
      .OnEnter(e => {
        ConsoleOut();
        hero.HAnimation = HAnimation.STANDING;
        hero.delayTimer.StopAndReset();
      })
    .State(WALKING_LEFT)
      .TransitionTo(WALKING_DELAY_LEFT).On(LEFT_RELEASED)
      .OnEnter(e => {
        ConsoleOut();
        hero.HAnimation = HAnimation.WALK_LEFT;
        hero.delayTimer.StopAndReset();
      })
    .State(WALKING_RIGHT)
      .TransitionTo(WALKING_DELAY_RIGHT).On(RIGHT_RELEASED)
      .OnEnter(e => {
        ConsoleOut();
        hero.HAnimation = HAnimation.WALK_RIGHT;
        hero.delayTimer.StopAndReset();
      })
    .State(WALKING_DELAY_LEFT)
      .TransitionTo(WALKING_RIGHT).On(RIGHT_PRESSED)
      .TransitionTo(RUNNING_LEFT).On(LEFT_PRESSED)
      .OnEnter(e => {
        hero.delayTimer.Start();
      })
      .Update(a => {
        hero.delayTimer.Update(a.ElapsedTimeSpan);
        if(hero.delayTimer) {
          horizontalMachine.JumpTo(STANDING);
        }
      })
    .State(WALKING_DELAY_RIGHT)
      .TransitionTo(WALKING_LEFT).On(LEFT_PRESSED)
      .TransitionTo(RUNNING_RIGHT).On(RIGHT_PRESSED)
      .OnEnter(e => {
        hero.delayTimer.Start();
      })
      .Update(a => {
        hero.delayTimer.Update(a.ElapsedTimeSpan);
        if(hero.delayTimer) {
          horizontalMachine.JumpTo(STANDING);
        }
      })
    .State(RUNNING_LEFT)
      .TransitionTo(STANDING).On(LEFT_RELEASED)
      .OnEnter(e => {
        ConsoleOut();
        hero.HAnimation = HAnimation.RUNNING_LEFT;
        hero.delayTimer.StopAndReset();
      })
    .State(RUNNING_RIGHT)
      .TransitionTo(STANDING).On(RIGHT_RELEASED)
      .OnEnter(e => {
        ConsoleOut();
        hero.HAnimation = HAnimation.RUNNING_RIGHT;
        hero.delayTimer.StopAndReset();
      })
    .GlobalTransitionTo(STANDING).On(SPACE_PRESSED)
    .Build();
  
  verticalMachine = Fsm<VState, VTrigger>.Builder(STANDING)
    .State(STANDING)
      .TransitionTo(DUCKING).On(DOWN_PRESSED)
      .TransitionTo(JUMPING).On(UP_PRESSED)
      .OnEnter(e => {
        ConsoleOut();
        hero.VAnimation = VAnimation.IDLE;
      })
      .OnExit(Console.Out.WriteLine($"From [{e.From}] with [{e.Input}] to [{e.To}]"))
    .State(DUCKING)
      .TransitionTo(STANDING).On(DOWN_RELEASED)
      .OnEnter(e => {
        ConsoleOut();
        hero.VAnimation = VAnimation.DUCKING;
      })
      .OnExit(ConsoleOut)
    .State(JUMPING)
      .TransitionTo(DIVING).On(DOWN_PRESSED)
      .OnEnter(e => {
        ConsoleOut();
        hero.VAnimation = VAnimation.JUMPING;
      })
      .OnExit(ConsoleOut)
      .Update(a => {
        hero.height += a.ElapsedTimeSpan.TotalSeconds * 100F;
        if(hero.height >= 200F)
          verticalMachine.TransitionTo(DESCENDING);
      })
    .State(DESCENDING)
      .TransitionTo(DIVING).On(DOWN_PRESSED)
      .OnEnter(e => {
        ConsoleOut();
        hero.VAnimation = VAnimation.DESCENDING;
      })
      .OnExit(ConsoleOut)
      .Update(a => {
        hero.height -= a.ElapsedTimeSpan.TotalSeconds * 100F;
        if(hero.height <= 0F) {
          hero.height = 0F;
          verticalMachine.TransitionTo(STANDING);
        }
      })
    .State(DIVING)
      .TransitionTo(DESCENDING).On(DOWN_RELEASED)
      .OnEnter(e => {
        ConsoleOut();
        hero.VAnimation = VAnimation.DIVING;
      })
      .OnExit(ConsoleOut)
      .Update(a => {
        hero.height -= a.ElapsedTimeSpan.TotalSeconds * 150F;
        if(hero.height <= 0F) {
          hero.height = 0F;
          verticalMachine.TransitionTo(STANDING);
        }
      })
    .GlobalTransitionTo(STANDING).On(SPACE_PRESSED)
    .Build();
}

protected override void Update(GameTime gameTime) {
  if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
      Keyboard.GetState().IsKeyDown(Keys.Escape))
    Exit();
  
  var s = Keyboard.GetState();
  if (s.IsKeyDown(Keys.Up))
    verticalMachine.Trigger(UP_PRESSED);
  if (s.IsKeyDown(Keys.Down))
    verticalMachine.Trigger(DOWN_PRESSED);
  if (!s.IsKeyDown(Keys.Down) && lastKeysPressed.Contains(Keys.Down))
    verticalMachine.Trigger(DOWN_RELEASED);
  
  if (s.IsKeyDown(Keys.Left))
    horizontalMachine.Trigger(LEFT_PRESSED);
  if (s.IsKeyDown(Keys.Right))
    horizontalMachine.Trigger(RIGHT_PRESSED);
  if (!s.IsKeyDown(Keys.Right) && lastKeysPressed.Contains(Keys.Right))
    horizontalMachine.Trigger(RIGHT_RELEASED);
  if (!s.IsKeyDown(Keys.Left) && lastKeysPressed.Contains(Keys.Left))
    horizontalMachine.Trigger(LEFT_RELEASED);
  
  lastKeysPressed = s.GetPressedKeys();
  
  // Update the machines themselves.
  verticalMachine.Update(TimeSpan.FromMilliseconds(
	gameTime.ElapsedGameTime.TotalMilliseconds));
  horizontalMachine.Update(TimeSpan.FromMilliseconds(
	gameTime.ElapsedGameTime.TotalMilliseconds));
}

private void ConsoleOut(TransitioningValueArgs<string> e) {
  Console.Out.WriteLine($"From [{e.From}] with [{e.Input}] to [{e.To}]");
}

Another example with a spell-button that has a refresh-time:

State-Machine-Image button

private enum State { IDLE, OVER, PRESSED, REFRESHING };
private enum Trigger { MOUSE_CLICKED, MOUSE_RELEASED, MOUSE_OVER, MOUSE_LEAVE };

private Dictionary<Button, Fsm<State, Trigger>> buttonMachines = new
  Dictionary<Button, Fsm<State, Trigger>>();

private void CreateMachineFor(Button button)
  buttonMachines.Add(button, Fsm.Builder<State, Trigger>(IDLE)
    .State(IDLE)
      .TransitionTo(OVER).On(MOUSE_OVER)
      .OnEnter(e => {
        button.State = ButtonState.IDLE;
      })
    .State(OVER)
      .TransitionTo(IDLE).On(MOUSE_LEAVE)
      .TransitionTo(PRESSED).On(MOUSE_CLICKED)
      .OnEnter(e => {
        button.State = ButtonState.OVER;
      })
    .State(PRESSED)
      .TransitionTo(IDLE).On(MOUSE_LEAVE).If(button.Kind == Kind.FLIPBACK)
      .TransitionTo(REFRESHING).On(MOUSE_RELEASED)
      .OnEnter(e => {
        button.State = ButtonState.DOWN;
      })
    .State(REFRESHING)
      .OnEnter(e => {
        hero.doSpell(button.DoAssociatedSpell());
        button.RefreshTimer.Start();
        button.State = ButtonState.REFRESHING;
      })
      .Update(a => {
        if(button.RefreshTimer.Value <= 0F) {
          button.RefreshTimer.StopAndReset();
          machine.JumpTo(IDLE);
        }
      })
    .Build();
}

public void main() {
  Button b1 = new Button("name1", "someText", ...);
  Button b2 = new Button("name2", "someOtherText", ...);
  
  CreateMachineFor(b1);
  CreateMachineFor(b2);
  ...
}

There also is the After feature ported from the MG version. Use it like that:

Fsm<State, Trigger>.Builder(State.STANDING)
  .State(State.DUCKING)
    .TransitionTo(State.STANDING).After(TimeSpan.fromMilliseconds(500))
...

This functionality is achieved by updating the After conditions before evaluating the Update function - Be advised that this happens directly before the Update call with the TimeSpan you've specified in the call to Update. If the After function triggers, the call to Update will be omitted.

Inspired by:

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].