TheSingularityWorkshop.FSM_API 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package TheSingularityWorkshop.FSM_API --version 1.0.0
                    
NuGet\Install-Package TheSingularityWorkshop.FSM_API -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TheSingularityWorkshop.FSM_API" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TheSingularityWorkshop.FSM_API" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="TheSingularityWorkshop.FSM_API" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TheSingularityWorkshop.FSM_API --version 1.0.0
                    
#r "nuget: TheSingularityWorkshop.FSM_API, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TheSingularityWorkshop.FSM_API@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TheSingularityWorkshop.FSM_API&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=TheSingularityWorkshop.FSM_API&version=1.0.0
                    
Install as a Cake Tool

TheSingularityWorkshop.FSM.API

NuGet Version License A highly intuitive and fluent API for defining and managing Finite State Machines (FSMs) in C#. Designed for clarity, robustness, and ease of use, this library empowers developers and even non-coders to create complex FSM logic with a clean, self-documenting syntax.

🌟 Features

  • Fluent API: Define states, transitions, and FSM properties using a readable, method-chaining syntax.
  • Intuitive State Definition: Easily scaffold OnEnter, OnUpdate, and OnExit actions for each state.
  • Runtime Flexibility: Create, modify, and destroy FSM definitions at runtime.
  • Live Instance Management: Robust handling for modifying FSM blueprints while instances are active, ensuring proper state lifecycle (OnExit/OnEnter) calls during dynamic changes.
  • Processing Groups: Organize FSMs into custom update categories ("Update", "Physics", "AI", etc.) for granular control over their execution.
  • Any-State Transitions: Define global transitions that can occur from any active state.
  • Context-Driven Logic: All state actions and transition conditions operate on a user-defined IStateContext, allowing seamless integration with your game entities or application data.
  • Automatic Instance Processing: FSM instances can be automatically updated based on their defined ProcessRate within their ProcessingGroup.

📦 Installation

Install the NuGet package:

dotnet add package TheSingularityWorkshop.FSM.API

Or via NuGet Package Manager:

Install-Package TheSingularityWorkshop.FSM.API

🚀 Getting Started

1. Define Your State Context

Create a class that implements IStateContext. This object will hold the data specific to each FSM instance and will be passed to your state actions and transition conditions.
C#

public class PlayerContext : IStateContext
{
    public string PlayerName { get; set; } = "Hero";
    public bool IsMoving { get; set; } = false;
    public bool HasJumped { get; set; } = false;
    public string CurrentStateName { get; set; } = string.Empty; // Essential for FSM internal tracking
    // Add any other properties relevant to your player's state (e.g., Health, Inventory, etc.)
}

2. Define Your FSM Blueprint

Use FSM_API.Create.CreateFiniteStateMachine to begin building your FSM.
C#

using TheSingularityWorkshop.FSM_API;
using System;

public static class GameFSMs
{
    public static void DefinePlayerFSM()
    {
        FSM_API.Create.CreateFiniteStateMachine("PlayerFSM", processRate: 60, processingGroup: "GameLoop")
            // Define the "Idle" state and its behaviors
            .State("Idle",
                onEnter: (ctx) => Console.WriteLine($"{((PlayerContext)ctx).PlayerName} is now Idle."),
                onUpdate: (ctx) => {
                    // Logic to check for player input to move or jump
                    var playerCtx = (PlayerContext)ctx;
                    if (playerCtx.IsMoving) Console.WriteLine("Idle: Checking for movement...");
                    if (playerCtx.HasJumped) Console.WriteLine("Idle: Checking for jump...");
                },
                onExit: (ctx) => Console.WriteLine($"{((PlayerContext)ctx).PlayerName} stopped being Idle."))

            // Define the "Moving" state
            .State("Moving",
                onEnter: (ctx) => Console.WriteLine($"{((PlayerContext)ctx).PlayerName} started Moving."),
                onUpdate: (ctx) => {
                    // Handle continuous movement, check if player stops or jumps
                    var playerCtx = (PlayerContext)ctx;
                    Console.WriteLine($"{playerCtx.PlayerName} is still moving...");
                },
                onExit: (ctx) => Console.WriteLine($"{((PlayerContext)ctx).PlayerName} stopped Moving."))

            // Define the "Jumping" state
            .State("Jumping",
                onEnter: (ctx) => Console.WriteLine($"{((PlayerContext)ctx).PlayerName} is Jumping!"),
                onUpdate: (ctx) => {
                    // Apply jump physics, check for landing
                    var playerCtx = (PlayerContext)ctx;
                    Console.WriteLine($"{playerCtx.PlayerName} is in mid-air...");
                    // Example: Simulate landing after some time/logic
                    // if (some_landing_condition) playerCtx.HasJumped = false;
                },
                onExit: (ctx) => Console.WriteLine($"{((PlayerContext)ctx).PlayerName} landed."))

            // Set the starting state for any new FSM instance based on this blueprint
            .WithInitialState("Idle")

            // Define transitions based on conditions evaluated against the PlayerContext
            .Transition("Idle", "Moving", (ctx) => ((PlayerContext)ctx).IsMoving)
            .Transition("Moving", "Idle", (ctx) => !((PlayerContext)ctx).IsMoving)
            .Transition("Moving", "Jumping", (ctx) => ((PlayerContext)ctx).HasJumped)
            .Transition("Idle", "Jumping", (ctx) => ((PlayerContext)ctx).HasJumped)
            
            // An 'Any-State' transition: from *any* current state, transition to "Idle"
            // if the player has jumped AND is no longer moving (e.g., they landed and stopped)
            .Transition(FSM.AnyStateIdentifier, "Idle", (ctx) => ((PlayerContext)ctx).HasJumped && !((PlayerContext)ctx).IsMoving)
            
            // Finalize the definition and register it with the FSM system
            .BuildDefinition();
    }
}

3. Create and Manage FSM Instances (Handles)

Once defined, you can create multiple instances of your FSM.
C#

public static class GameManager
{
    private static FSMHandle playerOneHandle;
    private static FSMHandle playerTwoHandle;

    public static void Initialize()
    {
        GameFSMs.DefinePlayerFSM(); // Define the FSM blueprint once

        // Create FSM instances for individual players
        var playerOneContext = new PlayerContext { PlayerName = "PlayerOne" };
        playerOneHandle = FSM_API.Instantiate.GetHandleForContext("PlayerFSM", "PlayerOneID", playerOneContext, "GameLoop");

        var playerTwoContext = new PlayerContext { PlayerName = "PlayerTwo" };
        playerTwoHandle = FSM_API.Instantiate.GetHandleForContext("PlayerFSM", "PlayerTwoID", playerTwoContext, "GameLoop");

        Console.WriteLine($"PlayerOne is initially in state: {playerOneHandle.GetCurrentState()}");
        Console.WriteLine($"PlayerTwo is initially in state: {playerTwoHandle.GetCurrentState()}");
    }

    public static void GameLoopUpdate()
    {
        // Advance all FSMs in the "GameLoop" processing group
        FSM_API.Interaction.Update("GameLoop");

        // Example: Simulate input changes for PlayerOne
        if (DateTime.Now.Second % 5 == 0 && !((PlayerContext)playerOneHandle.Context).IsMoving)
        {
            ((PlayerContext)playerOneHandle.Context).IsMoving = true;
            Console.WriteLine("PlayerOne input: Start Moving!");
        }
        else if (DateTime.Now.Second % 5 != 0 && ((PlayerContext)playerOneHandle.Context).IsMoving)
        {
            ((PlayerContext)playerOneHandle.Context).IsMoving = false;
            Console.WriteLine("PlayerOne input: Stop Moving!");
        }

        if (DateTime.Now.Millisecond % 1000 < 50 && !((PlayerContext)playerOneHandle.Context).HasJumped) // Simulate quick jump
        {
             ((PlayerContext)playerOneHandle.Context).HasJumped = true;
             Console.WriteLine("PlayerOne input: Jump!");
        }
        else if (DateTime.Now.Millisecond % 1000 > 500 && ((PlayerContext)playerOneHandle.Context).HasJumped)
        {
            ((PlayerContext)playerOneHandle.Context).HasJumped = false; // Simulate landing
        }

        Console.WriteLine($"PlayerOne's current state: {playerOneHandle.GetCurrentState()}");
        Console.WriteLine($"PlayerTwo's current state: {playerTwoHandle.GetCurrentState()}");
        Console.WriteLine("---");
    }
}

4. Dynamic FSM Modification (Adding/Removing States)

The API supports changing FSM blueprints at runtime, even with active instances.

Adding a State

C#

// Example: Add a new "Death" state to PlayerFSM
FSM_API.Interaction.AddStateToFSM("PlayerFSM", "GameLoop",
    "Death",
    onEnter: (ctx) => Console.WriteLine($"{((PlayerContext)ctx).PlayerName} has died!"),
    onUpdate: (ctx) => { /* Death animation, game over logic */ },
    onExit: (ctx) => Console.WriteLine($"{((PlayerContext)ctx).PlayerName} is no longer dead (e.g., respawned)."));

// You would then add transitions to/from "Death" as needed
FSM_API.Interaction.ModifyFiniteStateMachine("PlayerFSM", "GameLoop")
    .Transition(FSM.AnyStateIdentifier, "Death", (ctx) => /* some death condition for the player */ false) // Example condition
    .BuildDefinition();

Removing a State

When removing a state that active instances might be in, they are automatically transitioned to a specified fallbackStateName (or the FSM's new initial state). OnExit is called for the removed state, and OnEnter for the fallback state.
C#

// Example: Remove the "Jumping" state. Any player currently "Jumping" will move to "Idle".
FSM_API.Interaction.RemoveStateFromFSM("PlayerFSM", "Jumping", "GameLoop", fallbackStateName: "Idle");

// If "Idle" was also being removed, and no other state would exist, you'd need to explicitly provide a valid fallback.
// FSM_API.Interaction.RemoveStateFromFSM("PlayerFSM", "Idle", "GameLoop", fallbackStateName: "SomeNewDefaultState");

📚 API Reference

    FSM_API.Create.CreateFiniteStateMachine(string fsmName, int processRate, string processingGroup): Starts building a new FSM.

    FSM_API.Interaction.ModifyFiniteStateMachine(string fsmName, string processingGroup): Gets a builder for an existing FSM.

    FSM_API.Interaction.DestroyFiniteStateMachine(string fsmName, string processingGroup): Removes an FSM blueprint and all its handles.

    FSM_API.Interaction.AddStateToFSM(string fsmName, string processingGroup, string stateName, Action<IStateContext> onEnter, Action<IStateContext> onUpdate, Action<IStateContext> onExit): Adds a new state to a live FSM definition.

    FSM_API.Interaction.RemoveStateFromFSM(string fsmName, string stateName, string processingGroup, string fallbackStateName = null): Removes a state, gracefully transitioning live instances.

    FSM_API.Instantiate.GetHandleForContext(string fsmName, string contextId, IStateContext context, string processingGroup): Creates or retrieves an FSMHandle for a given context.

    FSM_API.Interaction.Update(string processingGroup): Advances all FSMs in a specific processing group.

    IStateContext: Interface your context classes must implement, requiring string CurrentStateName { get; set; }.

🙏 Contributing

Contributions are welcome! Please feel free to open issues for bugs or feature requests, or submit pull requests.

📄 License

This project is licensed under the MIT License - see the License for details.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on TheSingularityWorkshop.FSM_API:

Package Downloads
TheSingularityWorkshop.FSM_COS

Platform-neutral computation composition kernel for resolving, loading, arbitrating, and assembling MicroBundle-based computation.

TheSingularityWorkshop.FSM_Bridge.Helix

A bridge layer to integrate the FSM_API with the Helix Toolkit for VR and 3D applications.

TheSingularityWorkshop.Renderer

Observer-aware rendering foundations built around Event Horizons and computational detail.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.13 11,389 12/2/2025
1.0.12 302 11/4/2025
1.0.11 344 9/22/2025
1.0.10 371 9/18/2025
1.0.9 496 8/31/2025
1.0.8 183 8/23/2025
1.0.7 171 8/23/2025
1.0.6 174 8/23/2025
1.0.5 211 7/27/2025
1.0.4 197 7/27/2025
1.0.3 187 7/27/2025
1.0.2 507 7/25/2025
1.0.0 615 7/22/2025