> For the complete documentation index, see [llms.txt](/llms.txt)


# C# State Machine Definition Tutorial

A tutorial that builds a small game in the Unity Editor, edits C# rules, and operates them from the game screen



On this page, you build **a sample `MyGame` that increases a number with button presses and finishes when it reaches 9**. There are three rules.

- The number `Progress` starts at `0`.
- Choosing `Small` increases it by `1`, and choosing `Large` increases it by `3`.
- It finishes when `Progress` reaches `9`. Even if an operation would exceed `9`, the value is capped at `9`.

In steps 1 and 2, you run the template in the Editor preview; in step 3, you change the rules; and in step 4, you operate it from a Unity scene. In step 5, you add verification by the server.

## 1. Create the Template

You work in a Unity project with the GS2 SDK for Unity, GS2-CSharp-SDK, and a version of LocalStateMachineKit that includes the C# generation feature. The generation feature supports Unity 2021.2 or later.

1. From the Unity menu, select **GS2 > Local State Machine > Generate from C#**. The **GS2 C# Authoring** window opens.
2. Enter `MyGame` in **Name** under **New definition**.
3. Press **Create and preview**.
4. When generation and compilation finish, the **GS2 Local Preview** window opens.

**In this step, you don't need to select a Project or expand or change Generation settings.** The creation and output locations are set automatically. You don't need to log in or configure a server yet.

If `MyGame` already exists, don't recreate it; select `GameRules/MyGame/project.json` from the **…** of **Existing definition > Project** and press **Generate and preview**.

## 2. Operate It in the Preview

**GS2 Local Preview** has **View** at the top and **Operations** at the bottom.

- **View > State > Progress**: The number that operations increase. It starts at `0`.
- **Operations**: Has a `choice` selector and an **Advance** button.

Change `choice` to **Small** and press **Advance**. Progress becomes `1`, and `Accepted: Advance` is displayed. **Advance here is the operation button that moves the game forward.**

| Value chosen in choice | Result of pressing Advance |
| --- | --- |
| `Small` | Progress increases by 1 |
| `Large` | Progress increases by 3, but does not exceed 9 |
| `None` | Progress does not change, and `Ignored: InvalidChoice (1001)` is displayed |

If you press **Reset** and then run `Large` three times, Progress changes `0 → 3 → 6 → 9`. When it reaches 9, `9` is also shown in **Result > Progress**, and the Advance button can no longer be pressed. To try again, press **Reset**.

You can use this preview without pressing Unity's Play button.

## 3. Change the Amount Small Adds from 1 to 2

Go back to the **GS2 C# Authoring** window and press **Open rules**. The rules folder appears in Finder / Explorer; open `MyGame.cs` in a text editor or IDE.

The file is at `GameRules/MyGame/Rules/MyGame.cs` directly under the Unity project. Because it is outside `Assets`, it does not appear in Unity's Project window.

The template contains the following rules (line breaks added for readability).

```csharp
using System;
using Gs2.LocalStateMachine.Authoring;
using Step = Gs2.LocalStateMachine.Authoring.Step<
    GameRules.MyGameState, GameRules.MyGameResult, GameRules.MyGameError>;

namespace GameRules;

public enum MyGameError { InvalidChoice = 1001 }
public enum MyGameChoice { None, Small, Large }
public record MyGameState(int Progress);
public record MyGameResult(int Progress);

public static class MyGame
{
    public static Step Start() =>
        Step.WaitFor(nameof(Advance), new MyGameState(0));

    public static Step Advance(MyGameState state, MyGameChoice choice)
    {
        if (choice != MyGameChoice.Small && choice != MyGameChoice.Large)
            return Step.Ignore(MyGameError.InvalidChoice);

        var amount = 1;
        if (choice == MyGameChoice.Large)
            amount = 3;

        var next = state with { Progress = Math.Min(9, state.Progress + amount) };
        if (next.Progress == 9)
            return Step.End(new MyGameResult(next.Progress), next);

        return Step.WaitFor(nameof(Advance), next);
    }
}
```

It processes in the following order.

1. `Start` creates a state with Progress 0 and waits for `Advance` input.
2. `Advance` receives the current state `state` and the `choice` the player selected.
3. If it is `None`, it returns `Ignore` and does not accept the input.
4. If the choice is valid, it creates the updated state with `state with { ... }`.
5. When it reaches 9, it finishes with `End`; otherwise, it waits for the next `Advance`.

`MyGameState` is the in-progress state, and `MyGameResult` is the result you receive on finishing. `using Step = ...` is shorthand for specifying these two types together with the type for reasons to ignore input.

**Change `var amount = 1;` to `var amount = 2;` and save.** Return to Unity and press **GS2 C# Authoring > Generate and preview**. The preview starts over, and this time `Small` changes Progress `0 → 2`.

Generate and preview generates **GSL** and a **Session** from the definition. GSL is the file that runs the rules, and the Session is a C# class for calling operations from Unity. From now on, you can try changes with "edit and save the rules → Generate and preview". Don't edit the generated GSL or Session directly.

## 4. Call It from the Unity Game Screen

Next, operate the same `MyGame` from buttons in a scene.

The C# that writes the game rules and the C# that writes the Unity screen have different roles.

| What you write | Role |
| --- | --- |
| Game definition | Conditions for accepting operations, state changes, and finish conditions |
| Unity screen | Calls operations from buttons and displays the current state |

Here, you write the Unity screen side.

1. Create **MyGamePreview.cs** under `Assets` and save the following content.
2. Create an empty GameObject in the scene and attach this script.
3. Press Unity's **Play** button.
4. Press **Small** / **Large** in the Game view and check how Progress changes. Since you changed the rules in the previous step, pressing Small increases Progress by 2.

```csharp
#if UNITY_EDITOR && !GS2_DISABLE_LOCAL_STATE_MACHINE_KIT
using System.IO;
using Generated.MyGame;
using Gs2.Unity.Gs2StateMachine.Local;
using UnityEngine;

public sealed class MyGamePreview : MonoBehaviour
{
    private MyGameSession game;

    private void Start()
    {
        var path = Path.Combine(Application.dataPath, "Generated/MyGame/machine.gsl");
        game = MyGameSession.FromExecutor(
            LocalStateMachineExecutor.CreatePreview(File.ReadAllText(path), "MyGame"));
    }

    private void OnGUI()
    {
        if (game == null) return;
        GUILayout.Label("Progress: " + game.View.State.Progress);

        using (new UnityEditor.EditorGUI.DisabledScope(!game.View.Actions.Advance))
        {
            if (GUILayout.Button("Small")) game.Advance(MyGameChoice.Small);
            if (GUILayout.Button("Large")) game.Advance(MyGameChoice.Large);
        }

        if (game.View.Result != null)
            GUILayout.Label("Finished: " + game.View.Result.Progress);
    }
}
#endif
```

`game.Advance(...)` calls the operation, and `game.View.State` is the current state used for display. The runtime passes the `state` argument of the definition's `Advance`, so the screen side passes only `choice`.

`game.View.Actions.Advance` indicates "whether Advance can be called now". In this example, it becomes `false` after finishing, so the buttons are disabled. If there are game-specific conditions such as insufficient materials, check them inside the operation function and return `Ignore`.

`View.State` is a read-only copy. Call operation functions instead of rewriting the state directly. If you want to be notified only on changes, you can add the following line right after creating the Session in `Start`.

```csharp
game.ViewChanged += view => Debug.Log("Progress: " + view.State.Progress);
```

You have now tried editing the definition, regenerating, and operating from the game screen. This `MyGamePreview` is code for checking inside the Editor. To build it into your app, proceed to the following connection steps.

## 5. Use Server Verification

Even when you connect to the server, the `game.Advance(...)` and `game.View.State` you call on the screen side stay the same. What changes is how the Session is created and how the operation history is sent.

First, prepare the GS2 side.

1. Prepare a GS2-StateMachine namespace and enable speculative execution.
2. Read `Assets/Generated/MyGame/machine.gsl` and specify it as the `payload` of the master registration. `mainStateMachineName` is `MyGame`. See the [master registration description]().
3. From your game's start logic, start the state machine for the target user. Use the [start API](#startstatemachinebyuserid) or the [start action](#gs2statemachinestartstatemachinebyuserid).

The following is a **UniTask example** for use in an app that is already initialized and logged in. `gs2` and `gameSession` are the initialized SDK connection and login information, `namespaceName` is the namespace name, and `statusName` is the name of the status you started in step 3.

```csharp
var game = await Generated.MyGame.MyGameSession.OpenAsync(
    gs2, gameSession, namespaceName, statusName);

var outcome = game.Advance(Generated.MyGame.MyGameChoice.Small);
if (outcome.Ignored)
    Debug.Log("Input was not accepted: " + outcome.Code);

await game.ConfirmAsync(gs2, gameSession);
```

`OpenAsync` connects to the status you started in step 3. If you don't use UniTask, use `OpenFuture` / `ConfirmFuture`.

`ConfirmAsync` sends the operation history so far to the server and waits for the verification result. You can call it not only at the end but also in the middle of the game. The server runs the same rules and checks the results. Write important rules such as damage calculations and reward conditions in the game definition as well.

**The Editor preview has no history to send to the server.** Instead of playing the preview partway and then connecting, open the state started on the server with `OpenAsync`.

## Look Up the Definition Specifications

- [Add rewards](#rewards): An example of specifying rewards with SDK request types.
- [Switch accepted operations](#step): When to use `WaitFor` / `Stay` / `Next` / `End`.
- [Use random numbers](#random): How to use random numbers that the server can reproduce.
- [C# Definition Reference](): Declaration rules, data types, and the subset of C# you can use.
- [StateMachine Speculative Execution Session API](): APIs for operations, state reference, and server verification.
- [Generation Settings and Registration](): project.json, SDK references, and handling generated files.

## If Things Don't Go as Described

| Situation | What to check |
| --- | --- |
| Create and generate is shown instead of Create and preview | Remove `GS2_DISABLE_LOCAL_STATE_MACHINE_KIT` and recompile. You don't need to remove it if you use only the generation feature |
| You cannot create with the same name | Select the existing `project.json` and press Generate and preview |
| Pressing Open rules does not open a code editor | It is a button that opens the folder; open `MyGame.cs` inside it in your editor |
| Behavior does not change after editing | Save the rules and press Generate and preview. Check the Console for generation errors |
| You cannot operate after reaching 9 | The game has finished. In the preview window, press Reset; in a scene, stop Play and start again |

If generation fails, the previously successful output is kept. To avoid checking behavior with stale output while errors remain, resolve the errors in the Console before regenerating.



