Quick Start
Get a Brine2D game running in 5 minutes. This guide takes you from zero to a window with a moving sprite.
Prerequisites
Before starting:
.NET 10 SDK installed
IDE ready (Visual Studio 2022+, VS Code, or Rider)
5 minutes
New to Brine2D? Perfect! This guide assumes no prior knowledge.
Already have a project? Skip to Add to Existing Project.
Step 1: Create Project
Open your terminal and create a new console application:
What this does:
- Creates a new .NET 10 console application
- Names it MyFirstGame
- Changes to the project directory
Step 2: Install Brine2D
Add the Brine2D package:
What this does: - Installs Brine2D (core engine, rendering, input, audio - everything you need)
Verify installation:
You should see:
Step 3: Create Your First Scene
Replace the contents of Program.cs with:
using Brine2D.Core;
using Brine2D.Engine;
using Brine2D.Hosting;
using Brine2D.Input;
using System.Numerics;
var builder = GameApplication.CreateBuilder(args);
builder.Configure(options =>
{
options.Window.Title = "My First Game";
options.Window.Width = 800;
options.Window.Height = 600;
});
builder.AddScene<GameScene>();
await using var game = builder.Build();
await game.RunAsync<GameScene>();
public class GameScene : Scene
{
private readonly IGameContext _gameContext;
private Vector2 _playerPosition = new(400, 300);
private readonly float _speed = 200f;
public GameScene(IGameContext gameContext)
{
_gameContext = gameContext;
}
protected override void OnUpdate(GameTime gameTime)
{
if (Input.IsKeyPressed(Key.Escape))
{
_gameContext.RequestExit();
}
var deltaTime = (float)gameTime.DeltaTime;
if (Input.IsKeyDown(Key.W)) _playerPosition.Y -= _speed * deltaTime;
if (Input.IsKeyDown(Key.S)) _playerPosition.Y += _speed * deltaTime;
if (Input.IsKeyDown(Key.A)) _playerPosition.X -= _speed * deltaTime;
if (Input.IsKeyDown(Key.D)) _playerPosition.X += _speed * deltaTime;
}
protected override void OnRender(GameTime gameTime)
{
Renderer.DrawRectangleFilled(
_playerPosition.X - 25,
_playerPosition.Y - 25,
50, 50,
Color.Blue);
Renderer.DrawText("WASD: Move | ESC: Quit", 10, 10, Color.White);
}
}
Notice the pattern:
- Constructor only has YOUR dependencies (IGameContext)
- Logger, World, Renderer, Input, Audio, Game are available automatically as framework properties
- No need to pass framework services through the constructor!
Step 4: Run Your Game
Start your game:
You should see: - A window titled "My First Game" - A blue square in the center - Instructions at the top - The square moves with WASD keys - Escape quits the game
Success! You've created your first Brine2D game.
Understanding the Code
Let's break down what each part does:
Application Setup
var builder = GameApplication.CreateBuilder(args);
builder.Configure(options =>
{
options.Window.Title = "My First Game";
options.Window.Width = 800;
options.Window.Height = 600;
});
builder.AddScene<GameScene>();
await using var game = builder.Build();
await game.RunAsync<GameScene>();
What this does: 1. Creates a game application builder (like ASP.NET Core) 2. Configures window and engine options 3. Registers your game scene 4. Builds and runs the game
Pattern: This is dependency injection - Brine2D uses ASP.NET Core patterns.
Scene Class
public class GameScene : Scene
{
private readonly IGameContext _gameContext;
public GameScene(IGameContext gameContext)
{
_gameContext = gameContext;
}
protected override void OnRender(GameTime gameTime)
{
Logger.LogDebug("Rendering frame");
var player = World.CreateEntity("Player");
Renderer.DrawText("Hello", 10, 10, Color.White);
}
}
What's important:
- Constructor: Only YOUR services (IGameContext, custom services, etc.)
- Framework properties: Logger, World, Renderer, Input, Audio, Game set automatically by the framework
- Clean: No need to inject ILogger<T>, IEntityWorld, IRenderer, IInputContext - they're all properties
Framework Properties
These are available in all lifecycle methods (after constructor):
| Property | Type | Purpose |
|---|---|---|
Logger |
ILogger |
Logging for this scene (typed automatically) |
World |
IEntityWorld |
Entity world, scoped per scene — disposed automatically on scene unload |
Renderer |
IRenderer |
Drawing + render state (clear color, camera, etc.) |
Input |
IInputContext |
Keyboard, mouse, and gamepad input |
Audio |
IAudioPlayer |
Play sounds and music |
Game |
IGameContext |
Game context (request exit, game time) |
You never inject these - they're set by SceneManager before any lifecycle methods run.
Update Loop
protected override void OnUpdate(GameTime gameTime)
{
if (Input.IsKeyPressed(Key.Escape))
{
_gameContext.RequestExit();
}
var deltaTime = (float)gameTime.DeltaTime;
if (Input.IsKeyDown(Key.W)) _playerPosition.Y -= _speed * deltaTime;
if (Input.IsKeyDown(Key.S)) _playerPosition.Y += _speed * deltaTime;
if (Input.IsKeyDown(Key.A)) _playerPosition.X -= _speed * deltaTime;
if (Input.IsKeyDown(Key.D)) _playerPosition.X += _speed * deltaTime;
}
What this does:
- Called every frame (~60 times per second)
- Handles input via the Input framework property
- Updates game state (position, physics, AI)
- Uses deltaTime for frame-rate independent movement
Render Loop
protected override void OnRender(GameTime gameTime)
{
Renderer.DrawRectangleFilled(
_playerPosition.X - 25,
_playerPosition.Y - 25,
50, 50,
Color.Blue);
Renderer.DrawText("WASD: Move | ESC: Quit", 10, 10, Color.White);
}
What this does:
- Called every frame after update
- Draws game objects (sprites, shapes, text) via Renderer property
- Frame management (clear, begin, end) is automatic
Add to Existing Project
Already have a .NET 10 project? Add Brine2D:
Then add the startup code to your Program.cs:
using Brine2D.Hosting;
var builder = GameApplication.CreateBuilder(args);
builder.Configure(options =>
{
options.Window.Title = "My Game";
options.Window.Width = 1280;
options.Window.Height = 720;
});
builder.AddScene<GameScene>();
await using var game = builder.Build();
await game.RunAsync<GameScene>();
Common Mistakes
-
Don't access framework properties in the constructor
-
Don't inject framework services via constructor
-
Don't manually clear World on unload
-
Don't poll input in OnRender
-
Don't load assets in OnUpdate
Summary
What you learned:
| Concept | Description |
|---|---|
| GameApplication | Entry point, similar to ASP.NET Core |
| Scene | Container for game logic (update + render) |
| Framework Properties | Logger, World, Renderer, Input, Audio, Game - set automatically |
| Scoped World | Each scene has isolated EntityWorld - automatic cleanup! |
| Dependency Injection | YOUR services injected via constructor |
| Game Loop | Update (logic) → Render (drawing) |
| deltaTime | Frame-rate independent movement |
Key patterns:
// 1. Setup
var builder = GameApplication.CreateBuilder(args);
builder.Configure(options =>
{
options.Window.Title = "My Game";
options.Window.Width = 1280;
options.Window.Height = 720;
});
builder.AddScene<GameScene>();
// 2. Scene
public class GameScene : Scene
{
public GameScene(IGameContext gameContext) { }
protected override void OnUpdate(GameTime gameTime)
{
Logger.LogDebug("Updating");
var player = World.GetEntityByName("Player");
}
protected override void OnRender(GameTime gameTime)
{
Renderer.DrawText("Hello", 10, 10, Color.White);
}
}
// 3. Run
await using var game = builder.Build();
await game.RunAsync<GameScene>();
Next Steps
Now that you have a working game, explore more features:
- Your First Game - Build a complete game with sprites, audio, and collision
- Project Structure - Organize your code
- Configuration - Configure game settings
- Input Guide - Master keyboard, mouse, and gamepad input
- Rendering Guide - Work with sprites and textures
Quick Reference
// Minimal Program.cs
using Brine2D.Hosting;
var builder = GameApplication.CreateBuilder(args);
builder.Configure(options =>
{
options.Window.Title = "My Game";
options.Window.Width = 800;
options.Window.Height = 600;
});
builder.AddScene<GameScene>();
await using var game = builder.Build();
await game.RunAsync<GameScene>();
// Minimal Scene
using Brine2D.Core;
using Brine2D.Engine;
using Brine2D.Input;
public class GameScene : Scene
{
private readonly IGameContext _gameContext;
public GameScene(IGameContext gameContext)
{
_gameContext = gameContext;
}
protected override void OnUpdate(GameTime gameTime)
{
if (Input.IsKeyPressed(Key.Escape))
{
_gameContext.RequestExit();
}
}
protected override void OnRender(GameTime gameTime)
{
Renderer.DrawText("Hello, Brine2D!", 10, 10, Color.White);
}
}
Ready to build your first complete game? Head to Your First Game!