---
title: "Getting started with onegame"
url: https://recursiveai.org/onegame/docs/getting-started/
description: "Install the onegame command from NuGet, create a game, run, test and export it, then write an entity with synced state, a command and a view, and test it with a scenario."
version: "1.2.0"
---

# Getting started with onegame

Install the onegame command from NuGet, create a game, run, test and export it, then write an entity with synced state, a command and a view, and test it with a scenario.

onegame is a C# game engine with no editor. A game is plain C# plus text assets, and the `onegame` command builds, runs, tests, captures, replays and exports it from a terminal. Every command and every line of output on this page comes from one run of the 1.2.0 packages from NuGet, on Linux x64 with the .NET 10 SDK and no engine checkout.

## Install

You need the .NET 10 SDK. The `onegame` command is a .NET tool:

```shell
dotnet tool install -g RecursiveAI.OneGame.Cli --version 1.2.0
```

```text
You can invoke the tool using the following command: onegame
Tool 'recursiveai.onegame.cli' (version '1.2.0') was successfully installed.
```

- The tool is framework-dependent. If .NET lives in your home folder (the `dotnet-install` script puts it in `~/.dotnet`) rather than in a system location, set `DOTNET_ROOT` to that folder, or the tool stops with “You must install .NET to run this application.”
- `onegame help` lists every command. The exit codes are the same for all of them: 0 success, 1 a check failed, 2 bad command line, 3 the build failed, 4 the game or tool could not run, 5 the option needs a Debug build.
- The license is MIT or Apache-2.0, at your choice.

## Create a game

```shell
onegame new orchard
cd orchard
```

```text
created /tmp/onegame/orchard
  cd /tmp/onegame/orchard
  onegame build
  onegame run --headless --frames 60
  onegame test
```

The folder holds `orchard.csproj`, `Program.cs`, `Game.cs`, two scenarios (`scenarios/steer-ship.yaml` and `scenarios/ship-looks-right.yaml`, with its reference picture in `scenarios/refs/`), a `README.md`, `art/ship.png` and `fonts/` with Noto Sans Regular and its license. The game is a ship drawn from `art/ship.png` that you fly with WASD, the arrow keys or a gamepad’s left stick, and Space or the pad’s south button boosts. The project file is the whole build setup:

```xml
<Project Sdk="RecursiveAI.OneGame.Sdk/1.2.0">

  <PropertyGroup>
    <AssemblyName>Orchard</AssemblyName>
    <RootNamespace>Orchard</RootNamespace>
  </PropertyGroup>

</Project>
```

The `RecursiveAI.OneGame.Sdk` package sets the target framework and the build rules (warnings are errors, nullable on, implicit usings off), references the engine libraries at its own version, adds the development tools to Debug builds and carries the export targets. Everything restores from nuget.org. [Your first game](https://recursiveai.org/onegame/docs/first-game/) walks through the scaffold’s `Game.cs` line by line: the sprite, the input actions and the hot-reloaded picture.

## Run it

```shell
onegame run --headless --frames 60 --json
```

```text
{"ok":true,"game":"orchard","headless":true,"frames":60,"tick":60,"hash":"a367632d3cc24f14","width":1280,"height":720}
```

`run` builds the game and runs it. `--headless` needs no window or display, and `--frames 60` stops after 60 frames. `hash` is the hash of the whole world after the last tick: the same game, seed and inputs give the same hash every time. `onegame run` without `--headless` opens a window, and while it runs, an edit to `art/ship.png` shows in the next frame.

## Test it

```shell
onegame test
```

```text
PASS ship-looks-right (459 ms, tick 40, hash 32a7749f302a0994)
PASS steer-ship (4 ms, tick 22, hash eac71440f63f090d)
2 passed, 0 failed
```

`test` runs every file in `scenarios/` in a headless game, plus any C# test projects in the folder. `steer-ship` presses the `move` and `boost` actions and checks where the ship got to; `ship-looks-right` compares a drawn frame with its reference picture and checks that `art/ship.png` loaded. With `--json` the result is one object, with each step and expectation and the line it came from:

```text
{"ok":true,"scenarios":{"ok":true,"passed":2,"failed":0,"scenarios":[{"name":"ship-looks-right","source":"/tmp/onegame/orchard/scenarios/ship-looks-right.yaml","passed":true,"tick":40,"hash":"32a7749f302a0994","elapsedMs":444,"steps":[{"index":0,"kind":"action","line":5,"ok":true},{"index":1,"kind":"step","line":6,"ok":true}],"expectations":[{"index":0,"kind":"capture","line":8,"passed":true,"target":"refs/ship.png","actual":"0 px differ, max channel delta 1"},{"index":1,"kind":"asset","line":9,"passed":true,"target":"asset.art/ship.png"}]},{"name":"steer-ship","source":"/tmp/onegame/orchard/scenarios/steer-ship.yaml","passed":true,"tick":22,"hash":"eac71440f63f090d","elapsedMs":4,"steps":[{"index":0,"kind":"action","line":4,"ok":true},{"index":1,"kind":"step","line":5,"ok":true},{"index":2,"kind":"action","line":6,"ok":true},{"index":3,"kind":"step","line":7,"ok":true},{"index":4,"kind":"expect","line":8,"ok":true},{"index":5,"kind":"action","line":10,"ok":true},{"index":6,"kind":"action","line":11,"ok":true},{"index":7,"kind":"step","line":12,"ok":true}],"expectations":[{"index":4,"kind":"state","line":9,"passed":true,"target":"Ship.X","actual":"15"},{"index":0,"kind":"action","line":14,"passed":true,"target":"action","expected":"boost: held=true","actual":"boost: held=true"},{"index":1,"kind":"state","line":15,"passed":true,"target":"Ship.Boost","expected":"true","actual":"true"},{"index":2,"kind":"state","line":16,"passed":true,"target":"Ship.Y","actual":"30"}]}]},"unitTests":{"ran":false,"ok":true,"total":0,"passed":0,"failed":0,"skipped":0,"projects":[]}}
```

The capture scenario draws a frame, so it needs a Vulkan driver (a software one is fine). Here it ran on the GPU.

## Export it

```shell
onegame export --target linux
```

```text
exported orchard 1.0.0 for linux-x64 (nativeaot): 6 files, 7622 KiB
  folder:  /tmp/onegame/orchard/export/orchard-1.0.0-linux-x64
  archive: /tmp/onegame/orchard/export/orchard-1.0.0-linux-x64.tar.gz (3689 KiB)
  warning glibc_too_new: Orchard needs glibc 2.38, newer than 2.31 in Steam's Linux runtime (and so on the Steam Deck). Build on an older system, or inside the Steam Linux Runtime SDK: see docs/exporting.md.
```

The folder holds the NativeAOT program `Orchard`, `run.sh`, `art/ship.png`, `fonts/` and `THIRD-PARTY-NOTICES.txt`. The development tools are left out, so the exported program draws the picture it ships and doesn’t watch it for changes. It gives the same hash as `onegame run`:

```shell
export/orchard-1.0.0-linux-x64/run.sh --headless --frames 60 --json
```

```text
{"ok":true,"game":"orchard","headless":true,"frames":60,"tick":60,"hash":"a367632d3cc24f14","width":1280,"height":720}
```

- The warning matters only for Steam: the export was built on a system with a newer glibc than the Steam Linux runtime. See [Ship a game for Steam and the Steam Deck](https://recursiveai.org/onegame/cookbook/steam-and-steam-deck/).
- `--target win` and `--target osx` also work from the packages. NativeAOT needs a host of the same OS, so from Linux they make a self-contained build instead and say so in a warning. See [Export for Linux, Windows and macOS](https://recursiveai.org/onegame/cookbook/export-desktop/).
- For a NativeAOT build the repository’s docs list the platform’s C++ toolchain: `clang` and `zlib1g-dev` on Linux, the Visual Studio C++ tools on Windows.
- **Web, Android and iOS exports need an engine checkout at 1.2.0.** A game made from the packages gets `no_engine_root` from `--target web`. See [Export for the web, Android and iOS](https://recursiveai.org/onegame/cookbook/export-web-android-ios/).

## Write an entity

An entity is one class that holds synced state, commands, the simulation step and view code. Replace `Game.cs` with an apple tree: apples grow on their own, and players pick them. It draws with the UI alone; [Draw sprites, shapes and text](https://recursiveai.org/onegame/cookbook/draw-sprites-2d/) and [Input actions](https://recursiveai.org/onegame/cookbook/input-actions/) take the scaffold’s ship further.

```csharp
using OneGame.Core;
using OneGame.Host;
using OneGame.UI;

namespace Orchard;

/// <summary>An apple tree. Apples grow on their own; players pick them.</summary>
[Entity]
public partial class Tree : Entity
{
    [Synced] public partial int Apples { get; private set; }
    [Synced] public partial int Picked { get; private set; }
    [Synced] public partial int Growth { get; private set; }

    // A command: the UI and tests send it, the authority checks it and runs it.
    [Command]
    public bool Pick(int count)
    {
        if (count < 1 || count > Apples)
        {
            return false;   // refused: the state does not change
        }

        Apples -= count;
        Picked += count;
        return true;
    }

    // Every 30 ticks, zero to two new apples, drawn from a named random stream so a replay draws the same.
    protected override void OnTick()
    {
        if (++Growth < 30)
        {
            return;
        }

        Growth = 0;
        Apples += World!.Rng["growth"].NextInt(3);
    }

    [View]
    public string Label() => $"{Apples} on the tree, {Picked} picked";
}

public sealed class OrchardGame : GameDefinition
{
    public override string Name => "orchard";

    public override void RegisterEntities(EntityRegistry registry) => OneGame.Generated.Orchard.EntityTypes.Register(registry);

    public override void Setup(World world) => world.Add(new Tree());

    public override void DefineScreens(ScreenSet screens) =>
        screens.Add("main", game => Ui.Column(
            Ui.Label(() => TreeOf(game).Label()).Named("apples"),
            Ui.Button("Pick one", () => TreeOf(game).Send.Pick(1)).Named("pick").Padding(Length.Px(8)).Background(Color.Rgb(0x2F6B4F)))
            .Padding(Length.Px(24)).Gap(Length.Px(12)).Background(Color.Rgb(0x1E2A22)).TextColor(Color.White));

    private static Tree TreeOf(GameInstance game) => (Tree)game.World.Entities[0];
}
```

- **Synced state** is a `[Synced]` partial property. The source generator completes it with change tracking, a binary codec, deltas and snapshots. The setters are private, so only the entity’s own sim code writes them.
- **A command** is a `[Command]` method. Views and tests call it through `Send` (`TreeOf(game).Send.Pick(1)`), and the authority runs it on the next tick. Returning `false` refuses it. Commands are the only way a view changes the world.
- **The simulation step** is `OnTick`, run at a fixed rate. Randomness comes from named streams (`World.Rng["growth"]`) seeded from the world’s seed, so the draws are part of the state. Floats, clocks, `System.Random`, threads and I/O in sim code are compile errors. See [Deterministic simulation](https://recursiveai.org/onegame/cookbook/deterministic-simulation/).
- **A view** is a `[View]` member. It reads state and never writes it: assigning `Picked` inside `Label` is a compile error.
- **The screen** is built in `DefineScreens` with a C# builder. Lambdas such as `() => TreeOf(game).Label()` are bindings, read again every frame. `Named("pick")` gives an element the id that scenarios and tests use.

Build and run it twice with the default seed, then with seed 7:

```shell
onegame build
onegame run --headless --frames 300 --json
onegame run --headless --frames 300 --seed 7 --json
```

```text
build succeeded (Debug, 0 warnings, 0.79s)
{"ok":true,"game":"orchard","headless":true,"frames":300,"tick":300,"hash":"d0045829889cf126","width":1280,"height":720}
{"ok":true,"game":"orchard","headless":true,"frames":300,"tick":300,"hash":"d5ef04c0af6a538b","width":1280,"height":720}
```

A different seed grows a different tree; the same seed always grows the same one.

## Test it with a scenario

Delete `scenarios/steer-ship.yaml`, `scenarios/ship-looks-right.yaml` and `scenarios/refs/`, which test the ship, and add `scenarios/pick-apples.yaml`:

```yaml
name: pick-apples
description: Apples grow, picking takes them, and picking more than there are is refused.
seed: 7
steps:
  - expect:
      ui: { id: apples, text: "0 on the tree, 0 picked" }
  - click: pick              # nothing to pick yet: refused
  - step: 300
  - click: pick
  - step: 1
  - command: { entity: Tree, name: Pick, args: [99] }   # more than there are: refused
  - step: 1
expect:
  - state: { entity: Tree, property: Picked, equals: 1 }
  - state: { entity: Tree, property: Apples, equals: 9 }
  - ui: { id: apples, text: "9 on the tree, 1 picked" }
```

```shell
onegame test
```

```text
PASS pick-apples (39 ms, tick 302, hash 9a447aae324c764b)
1 passed, 0 failed
```

A scenario clicks elements by id, sends commands, steps frames and checks state and UI. `seed: 7` fixes the world’s random streams, so the nine apples left are the same on every run. A failing expectation prints the line, what was expected and what was found, and `onegame test` exits 1. [Automated game testing with scenarios](https://recursiveai.org/onegame/cookbook/automated-game-testing/) covers the rest of the format.

`onegame capture --size 720p --after 300` writes `captures/frame.png`, the screen after 300 ticks, so you can look at it.

## Packages by area

A game references only the SDK. It brings `Host`, `Core`, `UI` and `Audio` (and `Live` in Debug builds), and through them the rest of the engine. Every package is version 1.2.0, MIT or Apache-2.0.

| Package | What it is |
|---|---|
| `RecursiveAI.OneGame.Cli` | The `onegame` command, a .NET tool: create, build, run, test, capture, replay, export. |
| `RecursiveAI.OneGame.Sdk` | The MSBuild SDK for games: references, rules and export targets, so a game builds from nuget.org without an engine checkout. |
| `RecursiveAI.OneGame.Core` | The entity runtime: fixed-point numbers, random streams and the attributes that declare entities. It carries the source generators and analyzers. |
| `RecursiveAI.OneGame.Sync` | Nodes, scopes, saves, the command log, replay and drift hashes. |
| `RecursiveAI.OneGame.Net` | Reliable UDP and WebSocket transports, and a network conditioner for tests. |
| `RecursiveAI.OneGame.Server`, `.Server.Live` | The headless server host (fixed-step scopes, YAML configuration, the plugin API) and its live control. |
| `RecursiveAI.OneGame.Host` | The game host: window, input, fixed-step simulation, UI and render stage, windowed or headless. `Draw2D` for sprites, shapes and text from view code, and `Assets` for cached, hot-reloaded files. |
| `RecursiveAI.OneGame.Platform` | Windows, input, controllers, input actions and the app lifecycle per operating system. |
| `RecursiveAI.OneGame.UI`, `.UI.Kit` | The retained UI tree with CSS-model styling and flex layout, and the component kit: buttons, tables, lists, tabs, dialogs, text input. |
| `RecursiveAI.OneGame.Text`, `.Atlas` | Fonts, shaping and the glyph atlas; the SVG and image atlas for the 2D renderer. |
| `RecursiveAI.OneGame.Render2D` | Sprites, sprite sheets and clips, the 2D camera, tilemaps and text. |
| `RecursiveAI.OneGame.Render3D`, `.Gltf` | The physically based 3D renderer, and glTF 2.0 import that is safe on untrusted files. |
| `RecursiveAI.OneGame.Gpu`, `.Gpu.Vulkan`, `.Gpu.Metal`, `.Gpu.WebGpu`, `.Gpu.Backends` | The graphics layer and render graph, its three backends, and the package that picks one for the machine. |
| `RecursiveAI.OneGame.Audio` | Audio device output and the audio service. |
| `RecursiveAI.OneGame.Steam` | Steamworks bindings, Steam Input, and store-neutral achievements, stats, cloud saves and presence. |
| `RecursiveAI.OneGame.Purchases` | In-app purchases over StoreKit, Google Play Billing and a scriptable fake store. |
| `RecursiveAI.OneGame.Control`, `.Live` | The control layer the command line, tests and live control share; the development tools of a Debug build. |
| `RecursiveAI.OneGame.Testing` | The C# test API: in-process clusters, scenarios, recordings, captures and determinism checks. |

## What onegame doesn’t do

- **No editor.** That is the design: everything is code and text, driven from the terminal, tests and live control.
- **No physics engine.** Collision and movement are your own sim code. The 3D scene docs list physics under “not built.”
- **Not an ECS.** Entities are classes that hold their own state and logic. For hot loops over many rows there are `[HotData]` struct-of-arrays tables.
- **Web, Android and iOS exports need an engine checkout at 1.2.0**, as do Play Billing in Android builds and plugin servers. Desktop exports and dedicated servers work from the packages.

## Where next

Cookbook recipes, each run on Linux against the 1.2.0 packages:

- [Automated game testing with scenarios](https://recursiveai.org/onegame/cookbook/automated-game-testing/), [record and replay a game](https://recursiveai.org/onegame/cookbook/record-and-replay/), [screenshot tests](https://recursiveai.org/onegame/cookbook/screenshot-tests/)
- [Test a game from C#](https://recursiveai.org/onegame/cookbook/csharp-game-tests/), [start tests from fixtures and snapshots](https://recursiveai.org/onegame/cookbook/fixtures-and-snapshots/), [drive a running game over MCP](https://recursiveai.org/onegame/cookbook/live-control-mcp/)
- [Deterministic simulation](https://recursiveai.org/onegame/cookbook/deterministic-simulation/), [client/server sync with a dedicated server](https://recursiveai.org/onegame/cookbook/client-server-sync/), [client-side prediction](https://recursiveai.org/onegame/cookbook/client-side-prediction/)
- [Draw sprites, shapes and text](https://recursiveai.org/onegame/cookbook/draw-sprites-2d/), [input actions for keyboard, gamepad and tests](https://recursiveai.org/onegame/cookbook/input-actions/)
- [Build a game screen with the UI kit](https://recursiveai.org/onegame/cookbook/game-ui-screen/), [controller and keyboard navigation](https://recursiveai.org/onegame/cookbook/controller-navigation/), [check UI contrast](https://recursiveai.org/onegame/cookbook/ui-contrast/)
- [Export for Linux, Windows and macOS](https://recursiveai.org/onegame/cookbook/export-desktop/), [ship for Steam and the Steam Deck](https://recursiveai.org/onegame/cookbook/steam-and-steam-deck/), [export for the web, Android and iOS](https://recursiveai.org/onegame/cookbook/export-web-android-ios/)

The guides:

- [Your first game](https://recursiveai.org/onegame/docs/first-game/): the scaffold line by line
- [Driving a game](https://recursiveai.org/onegame/docs/driving-a-game/): scenarios, captures, replays, live control, servers and clusters
- [The UI guide](https://recursiveai.org/onegame/docs/ui-guide/): screens, styles, the component kit, focus and theming
- [Exporting](https://recursiveai.org/onegame/docs/exporting/) and [checking on devices](https://recursiveai.org/onegame/docs/devices/)
- [Changelog](https://recursiveai.org/onegame/docs/changelog/)

Coming from another engine: [onegame for MonoGame developers](https://recursiveai.org/onegame/articles/onegame-for-monogame-developers/) and [onegame for Unity developers](https://recursiveai.org/onegame/articles/onegame-for-unity-developers/).

Ready for more than the ship? Build a full game in [C# game development without an editor](https://recursiveai.org/onegame/articles/csharp-game-development-without-editor/).
