03. Getting Started with FSM_API in Pure C# (From our Documentation)

03. Getting Started with FSM_API in Pure C# (From our Documentation)

posted 7 min read

Previously we published a deep dive included with our documentation in our FSM_API nuget package for our truly unique feature, runtime recomposition/definition of our FSMs. In this article we are sharing our document on Getting started in pure c#. We showed you the power, now let us show you how easy it is to start using our 45kb API. Embrace the pure digital power!

Without further rambling:

03. Getting Started with FSM_API in Pure C#

Use FSM_API with pure C# classes — completely decoupled from Unity — for backend logic, simulations, AI, networking, or any state-based system.

This guide shows how to use FSM_API in non-Unity environments (console, services, backends, robotics, etc.) with clean, minimal dependencies.

Install FSM_API into your c# project using Nuget Package

Within your preferred IDE find FSM_API within the NuGet Package Manager and click install.

Install FSM_API into your project using .NET CLI:

dotnet add package TheSingularityWorkshop.FSM_API

Integrate the API into your project

Integrating the API with any c# application is foremost establishing the heartbeat or cadence of when your FSMs operate.

Example c#:

FSM_API.Interaction.Update("YourProcessingGroupName");//Updates all FSMs registered to this processing group.

consider the simplest integration possible, a console application as shown below:

Example c#:

using TheSingularityWorkshop.FSM_API;

//Define the application context
SimpleDemoContext appContext = new SimpleDemoContext();

//now we enter a loop which will run indefinitely until the handle becomes invalid.
do
{
    //We make this update call which will then update the app fsm.  The app's
    FSM_API.Interaction.Update("Main");
} while (appContext.IsValid);

To help with this understanding we will describe an example below along with additional samples which are available on our samples repo.

Samples Repo

What This Example Will Demonstrate

We’ll simulate a basic character health lifecycle using FSM_API — all in pure C#. This example is useful for understanding how FSM_API integrates into any standard C# application, offering the same benefits as in Unity.

This example illustrates:

  • How to define and link FSMs to standard C# classes as their context.
  • How a game character transitions through health-related states (Healthy, Damaged, Critically Injured, Dead).
  • How FSM_API makes these transitions declarative, clean, and maintainable.
  • How to manually control the "ticking" of FSMs without relying on a game engine's built-in loop.
  • How FSMs can be revived and continue operating even after simulated "death."

Simulation Flow

  1. The player starts in the Healthy state.
  2. Over several ticks, they take damage, triggering transitions to:
    • Damaged
    • Critically Injured
    • Dead
  3. After death, a revive command is issued.
  4. Health is restored.
  5. The FSM automatically transitions back to Healthy.

All output is printed to the console to simulate animation, audio, and event feedback — ideal for unit tests or backend game systems.


✅ Step 1: Define Your Pure C# Context

Create a standard C# class to represent the "thing" your FSM will manage — like a character, connection, pipeline, or subsystem. This class will hold the data and methods that your FSM's states will interact with.

It must implement the IStateContext interface. Recall from the 01. Core Concepts: Your Guide to FSM_API that IStateContext requires Name and IsValid properties.

Example: GameCharacterContext.cs

Example c#:

using System;
using TheSingularityWorkshop.FSM_API;

public class GameCharacterContext : IStateContext
{
    private float _currentHealth = 100f;
    private int _currentAmmo = 10;
    private float _moveSpeed = 5f;
    private bool _isActive = true;
    private bool _isReviveCommandIssued = false;

    public float CurrentHealth => _currentHealth;
    public int CurrentAmmo => _currentAmmo;
    public float MoveSpeed => _moveSpeed;

    // IStateContext Properties
    public string Name { get; set; }
    public bool IsValid => _isActive; // FSM_API uses this to know if the context is still relevant

    public GameCharacterContext(string name)
    {
        Name = name;
        Console.WriteLine($"{Name} context created.");
    }

    // Methods that FSM states will call to interact with this context
    public void TakeDamage(float amount)
    {
        _currentHealth -= amount;
        Console.WriteLine($"{Name} took {amount} damage. Health: {CurrentHealth}");
    }

    public void SetActive(bool active)
    {
        _isActive = active;
        Console.WriteLine($"{Name} active status set to {_isActive}.");
    }

    public void RestoreHealth(float amount)
    {
        _currentHealth += amount;
        if (_currentHealth > 100f) _currentHealth = 100f;
        Console.WriteLine($"{Name} health restored to {CurrentHealth}.");
    }

    public void SimulateMovement()
    {
        Console.WriteLine($"{Name} is moving at speed {MoveSpeed}.");
    }

    public void PlaySound(string soundName)
    {
        Console.WriteLine($"{Name} playing sound: {soundName}");
    }

    // Methods for transition conditions
    public void IssueReviveCommand() => _isReviveCommandIssued = true;
    public void ClearReviveCommand() => _isReviveCommandIssued = false;
    public bool IsReviveCommandPending => _isReviveCommandIssued;
}

✅ Step 2: Define and Create the FSM

With your context ready, you can now define your FSM using FSM_API.CreateFiniteStateMachine() and its fluent builder pattern. Then, create an instance of that FSM, linking it to your GameCharacterContext.

Example: PureCSharpGameLoop.cs


<img src="Visuals/Character_Health_FSM.png" alt="A flowchart of the Character Health FSM showing states and transitions." height="200" style="display: block;">


A flowchart of the Character Health FSM showing states and transitions.

This class will set up our FSM definition and manage the FSM instance within a simulated game loop.

Example c#:

using System;
using TheSingularityWorkshop.FSM_API;

public class PureCSharpGameLoop
{
    private FSMHandle characterHealthFSM;
    private GameCharacterContext playerCharacter;

    public void SetupGame()
    {
        playerCharacter = new GameCharacterContext("Hero");

        // Define the FSM blueprint if it doesn't already exist.
        // The FSM definition is reusable across multiple instances.
        if (!FSM_API.Exists("CharacterHealthFSM"))
        {
            FSM_API.CreateFiniteStateMachine("CharacterHealthFSM", processRate: 1, processingGroup: "GameLogic")
                .State("Healthy",
                    onEnter: ctx => ((GameCharacterContext)ctx).PlaySound("HappyTune"),
                    onUpdate: ctx => 
                        Console.WriteLine($"{ctx.Name} is Healthy. Health: {((GameCharacterContext)ctx).CurrentHealth}"),
                    onExit: ctx => ((GameCharacterContext)ctx).PlaySound("SadTune"))
                .State("Damaged",
                    onEnter: ctx => ((GameCharacterContext)ctx).PlaySound("OuchSound"),
                    onUpdate: ctx => 
                        Console.WriteLine($"{ctx.Name} is Damaged. Health: {((GameCharacterContext)ctx).CurrentHealth}"))
                .State("CriticallyInjured",
                    onEnter: ctx => ((GameCharacterContext)ctx).PlaySound("WarningAlarm"),
                    onUpdate: ctx => Console.WriteLine($"{ctx.Name} is Critically Injured!"))
                .State("Dead",
                    onEnter: ctx => ((GameCharacterContext)ctx).PlaySound("DeathRattle"),
                    onUpdate: ctx => Console.WriteLine($"{ctx.Name} is Dead."),
                    onExit: ctx => ((GameCharacterContext)ctx).ClearReviveCommand())
                .WithInitialState("Healthy")
                .Transition("Healthy", "Damaged", ctx => 
                    ((GameCharacterContext)ctx).CurrentHealth <= 75 && ((GameCharacterContext)ctx).CurrentHealth > 25)
                .Transition("Damaged", "Healthy", ctx => 
                    ((GameCharacterContext)ctx).CurrentHealth > 75)
                .Transition("Damaged", "CriticallyInjured", ctx => 
                    ((GameCharacterContext)ctx).CurrentHealth <= 25 && ((GameCharacterContext)ctx).CurrentHealth > 0)
                .Transition("CriticallyInjured", "Damaged", ctx => 
                    ((GameCharacterContext)ctx).CurrentHealth > 25 && ((GameCharacterContext)ctx).CurrentHealth <= 75)
                .Transition("CriticallyInjured", "Dead", ctx => 
                    ((GameCharacterContext)ctx).CurrentHealth <= 0)
                .Transition("Dead", "Healthy", ctx => 
                    ((GameCharacterContext)ctx).IsReviveCommandPending && ((GameCharacterContext)ctx).CurrentHealth > 0)
                .BuildDefinition();
        }

        // Create a live FSM instance, associating it with our playerCharacter context.
        characterHealthFSM = FSM_API.CreateInstance("CharacterHealthFSM", playerCharacter);
        Console.WriteLine($"FSM for {playerCharacter.Name} initialized to state: {characterHealthFSM.CurrentState}");
    }

    // Call this method from your main application loop to update the FSMs
    public void UpdateGameLogic() => FSM_API.Update("GameLogic");

    // Helper methods for accessing character and FSM state
    public GameCharacterContext GetPlayerCharacter() => playerCharacter;
    public string GetCharacterFSMState() => characterHealthFSM.CurrentState;
}

✅ Step 3: Manual Update Loop

Since you don’t have an engine's Update() or FixedUpdate() loop, you'll explicitly "tick" the FSMs yourself from your main application loop or a dedicated manager. You'll call FSM_API.Update() for the processing group you defined.

Example: Program.cs (Main Application Entry Point)


<img src="Visuals/Simulation_Timeline.png" alt="A timeline of the 20-tick simulation showing key events and state changes." height="200" style="display: block;">


A timeline of the 20-tick simulation showing key events and state changes.

Example c#:

using System;
using System.Threading; // For Thread.Sleep

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("--- Starting Pure C# FSM Demo ---");

        var game = new PureCSharpGameLoop();
        game.SetupGame();
        var player = game.GetPlayerCharacter();

        for (int i = 0; i < 20; i++) // Simulate 20 game ticks
        {
            Console.WriteLine($"\n--- Tick {i + 1} ---");

            // Simulate events that affect the character's health
            if (i == 2) player.TakeDamage(30);   // Health: 100 -> 70 (Healthy -> Damaged)
            if (i == 5) player.TakeDamage(20);   // Health: 70 -> 50 (Still Damaged)
            if (i == 8) player.TakeDamage(30);   // Health: 50 -> 20 (Damaged -> Critically Injured)
            if (i == 10) player.TakeDamage(20);  // Health: 20 -> 0 (Critically Injured -> Dead)
            if (i == 12) player.IssueReviveCommand(); // Issue revive command
            if (i == 13) player.RestoreHealth(100);    // Restore health, FSM should transition Dead -> Healthy

            // Advance the FSMs in the "GameLogic" processing group
            game.UpdateGameLogic();

            Console.WriteLine($"Current FSM State: {game.GetCharacterFSMState()}");
            Thread.Sleep(500); // Optional: pause for half a second to observe output
        }

        Console.WriteLine("\n--- FSM Demo Finished ---");
    }
}

Running the Example

To run this pure C# FSM example:

  1. Create a Console App Project in your preferred IDE (e.g., Visual Studio, VS Code with C# extension).
  2. Add FSM_API Reference:
    • If FSM_API is available on NuGet, add it via the NuGet Package Manager.
    • Otherwise, manually add a reference to the TheSingularityWorkshop.FSM_API.dll file.
  3. Add Code Files:
    • Create GameCharacterContext.cs and paste the code from Step 1.
    • Create PureCSharpGameLoop.cs and paste the code from Step 2.
    • Replace the content of Program.cs with the code from Step 3.
  4. Run the App and observe the FSM transitions and console output, which will show your character moving through different health states.

✅ Summary

  • FSM_API works seamlessly outside of Unity and other game engines, providing a powerful FSM solution for any pure C# application.
  • You define your application's data models or entities as classes that implement the IStateContext interface.
  • FSM definition and instantiation logic remain consistent with Unity usage, using FSM_API.CreateFiniteStateMachine() and FSM_API.CreateInstance().
  • You gain explicit control over when FSMs process their logic by calling FSM_API.Update("YourGroupName") from your application's main loop or scheduler.
  • This makes FSM_API an ideal choice for server-side logic, complex simulations, AI systems, backend services, or custom game engines where you need robust and decoupled state management.


Support Us

If you find this project useful, you can support its development through PayPal.

Donate via PayPal


Brought to you by

The Singularity Workshop – Tools for the curious, the bold, and the systemically inclined.

The image depicts a futuristic, sci-fi landscape dominated by glowing circuitry. A large, stylized Patreon logo hovers in the dark, starry sky above a radiant, eye-like sphere. This sphere pulses with vibrant, multicolored light, casting a brilliant glow onto the digital terrain below. The ground is a vast circuit board, with rivers of glowing orange and blue energy flowing along intricate pathways. Small, button-like structures on the surface emit soft light. The overall scene feels alive and dynamic, suggesting a powerful, almost magical, connection between technology and consciousness, with the Patreon logo at the center of it all.
Because state shouldn’t be a mess.


1 Comment

1 vote
0

More Posts

10. FSM_API for Non-Coders: A Big Picture Overview (From our Documentation)

The Singularity Workshop - Nov 15, 2025

My First Flow with Kestra.io

Amara Graham - Feb 6

06_FSM_Modifier Deep Dive: Modifying Your FSMs at Runtime (From our Documentation)

The Singularity Workshop - Nov 14, 2025

Getting Started with esbuild in Angular

Sunny - Jan 9

Optimization Log: How One Line of Code Unlocked 33,000 Agents

The Singularity Workshop - Dec 8, 2025
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

10 comments
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!