Brine2D.Audio.ECS 0.7.0-beta

This is a prerelease version of Brine2D.Audio.ECS.
dotnet add package Brine2D.Audio.ECS --version 0.7.0-beta
                    
NuGet\Install-Package Brine2D.Audio.ECS -Version 0.7.0-beta
                    
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="Brine2D.Audio.ECS" Version="0.7.0-beta" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Brine2D.Audio.ECS" Version="0.7.0-beta" />
                    
Directory.Packages.props
<PackageReference Include="Brine2D.Audio.ECS" />
                    
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 Brine2D.Audio.ECS --version 0.7.0-beta
                    
#r "nuget: Brine2D.Audio.ECS, 0.7.0-beta"
                    
#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 Brine2D.Audio.ECS@0.7.0-beta
                    
#: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=Brine2D.Audio.ECS&version=0.7.0-beta&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Brine2D.Audio.ECS&version=0.7.0-beta&prerelease
                    
Install as a Cake Tool

Brine2D

The ASP.NET of game engines - A modern .NET 10 game engine built on SDL3 for creating 2D games with C#.

Brine2D brings the familiar patterns and developer experience of ASP.NET to game development. If you've built web apps with ASP.NET, you'll feel right at home building games with Brine2D.

Features

  • Entity Component System (ECS) - ASP.NET-style system pipelines with automatic ordering
  • Scene Management - Async loading, transitions, loading screens, and lifecycle hooks
  • Advanced Queries - Fluent API with spatial queries, filtering, sorting, and caching
  • Performance Monitoring - Built-in FPS counter, frame time graphs, and rendering statistics
  • Object Pooling - Zero-allocation systems using ArrayPool<T> and custom object pools
  • Sprite Batching - Automatic batching with layer sorting and frustum culling
  • Event System - Type-safe EventBus for decoupled component communication
  • Input System - Keyboard, mouse, gamepad with polling and events
  • Sprite Rendering - Hardware-accelerated with sprite sheets and animations
  • Animation System - Frame-based with multiple clips and events
  • Audio System - Sound effects and music via SDL3_mixer
  • Tilemap Support - Tiled (.tmj) integration with auto-collision
  • Collision Detection - AABB and circle colliders with spatial partitioning
  • Camera System - 2D camera with follow, zoom, rotation, and bounds
  • Particle System - Pooled particle effects with customizable emitters
  • UI Framework - Complete component library with tooltips, tabs, dialogs, and more
  • Configuration - JSON-based settings with hot reload support
  • Dependency Injection - ASP.NET Core-style DI container
  • Logging - Structured logging with Microsoft.Extensions.Logging
  • Multiple Backends - SDL3 GPU (modern, high-performance) and Legacy renderer (compatibility)

Why Brine2D?

ASP.NET Developers Will Feel at Home

// Looks familiar? That's the point!
var builder = GameApplication.CreateBuilder(args);

// Configure services just like ASP.NET
builder.Services.AddSDL3Input();
builder.Services.AddSDL3Audio();

builder.Services.AddSDL3Rendering(options =>
{
    options.WindowTitle = "My Game";
    options.WindowWidth = 1280;
    options.WindowHeight = 720;
    
    // Choose your backend
    options.Backend = GraphicsBackend.GPU;              // Modern SDL3 GPU API (recommended)
    // options.Backend = GraphicsBackend.LegacyRenderer; // SDL_Renderer API (fallback)
    
    options.PreferredGPUDriver = "vulkan"; // or "d3d12", "metal"
    options.VSync = true;
});

// Configure ECS systems like middleware
builder.Services.ConfigureSystemPipelines(pipelines =>
{
    pipelines.AddSystem<PlayerControllerSystem>();
    pipelines.AddSystem<AISystem>();
    pipelines.AddSystem<VelocitySystem>();
    pipelines.AddSystem<PhysicsSystem>();
    pipelines.AddSystem<SpriteRenderingSystem>();
});

// Register your scenes like controllers
builder.Services.AddScene<GameScene>();

var game = builder.Build();
await game.RunAsync<GameScene>();

Key Similarities to ASP.NET

ASP.NET Brine2D
WebApplicationBuilder GameApplicationBuilder
Controllers Scenes
Middleware ECS System Pipelines
app.UseAuthentication() pipelines.AddSystem<T>()
Automatic execution Lifecycle hooks
appsettings.json gamesettings.json
Dependency Injection Dependency Injection
ILogger<T> ILogger<T>
Configuration binding Configuration binding

Quick Start

Installation

Using NuGet (Recommended)

Create a new .NET 10 console project and add Brine2D:

dotnet new console -n MyGame
cd MyGame
dotnet add package Brine2D.Desktop

That's it! Brine2D.Desktop includes everything you need to start building games.

Package Options

For most users, install the meta-package:

dotnet add package Brine2D.Desktop

Advanced: Install only what you need:

# Core abstractions
dotnet add package Brine2D.Core
dotnet add package Brine2D.Engine
dotnet add package Brine2D.ECS

# Choose your implementations
dotnet add package Brine2D.Rendering.SDL
dotnet add package Brine2D.Input.SDL
dotnet add package Brine2D.Audio.SDL

# ECS bridges (optional)
dotnet add package Brine2D.Rendering.ECS
dotnet add package Brine2D.Input.ECS
dotnet add package Brine2D.Audio.ECS

Your First Game

Create Program.cs:

using Brine2D.Core;
using Brine2D.Hosting;
using Brine2D.Input;
using Brine2D.Input.SDL;
using Brine2D.Rendering;
using Brine2D.Rendering.SDL;
using Microsoft.Extensions.Logging;

// Create the game application builder
var builder = GameApplication.CreateBuilder(args);

// Configure SDL3 rendering
builder.Services.AddSDL3Rendering(options =>
{
    options.WindowTitle = "My First Brine2D Game";
    options.WindowWidth = 1280;
    options.WindowHeight = 720;
    options.VSync = true;
});

// Add SDL3 input
builder.Services.AddSDL3Input();

// Register your scene
builder.Services.AddScene<GameScene>();

// Build and run
var game = builder.Build();
await game.RunAsync<GameScene>();

// Define your game scene
public class GameScene : Scene
{
    private readonly IRenderer _renderer;
    private readonly IInputService _input;
    private readonly IGameContext _gameContext;

    public GameScene(
        IRenderer renderer,
        IInputService input,
        IGameContext gameContext,
        ILogger<GameScene> logger) : base(logger)
    {
        _renderer = renderer;
        _input = input;
        _gameContext = gameContext;
    }

    protected override void OnRender(GameTime gameTime)
    {
        // Systems and rendering happen automatically!
        _renderer.DrawText("Hello, Brine2D!", 100, 100, Color.White);
    }

    protected override void OnUpdate(GameTime gameTime)
    {
        // Systems run automatically via lifecycle hooks!
        if (_input.IsKeyPressed(Keys.Escape))
        {
            _gameContext.RequestExit();
        }
    }
}

Run your game:

dotnet run

Beta Release Notice

⚠️ This is a beta release (0.7.0-beta)

What works:

  • Entity Component System (ECS)
  • System pipelines with automatic ordering
  • Advanced query system with fluent API
  • Performance monitoring and profiling
  • Object pooling (ArrayPool, custom pools)
  • Sprite batching with frustum culling
  • Scene transitions and loading screens
  • Lifecycle hooks with opt-out for power users
  • EventBus for component communication
  • Prefabs and serialization
  • Transform hierarchy (parent/child)
  • Utility components (Timer, Lifetime, Tween)
  • GPU rendering (SDL3 GPU API with Vulkan/D3D12/Metal)
  • Legacy rendering (SDL_Renderer API for compatibility)
  • ✅ Sprites, primitives, text, lines
  • ✅ Input system (keyboard, mouse, gamepad)
  • ✅ Audio system
  • ✅ Animation system
  • ✅ Collision detection with physics response
  • ✅ Tilemap support
  • ✅ UI framework (complete component library)
  • ✅ Camera system with follow behavior
  • ✅ Particle system with pooling

What's coming next:

  • 🔄 Texture atlasing for advanced batching
  • 🔄 Post-processing effects
  • 🔄 Multi-threaded ECS systems
  • 🔄 Enhanced particle system (rotation, trails)

Expect breaking changes before 1.0!


Performance & Optimization

Brine2D is built for performance with zero-allocation hot paths and efficient memory management.

Performance Monitoring

Built-in performance overlay with real-time statistics:

// Enable performance monitoring
builder.Services.AddPerformanceMonitoring(options =>
{
    options.EnableOverlay = true;
    options.ShowFPS = true;
    options.ShowFrameTime = true;
    options.ShowMemory = true;
});

Features:

  • FPS counter with min/max/average tracking
  • Frame time graph (60-frame history)
  • Memory usage monitoring
  • Rendering statistics (sprites, draw calls, batches)
  • Per-system profiling

Hotkeys (in scenes):

  • F1 - Toggle performance overlay visibility
  • F3 - Toggle detailed stats (includes frame time graph and memory)

Zero-Allocation Systems

Brine2D uses ArrayPool<T> and custom object pools to minimize GC pressure:

// Entity updates use ArrayPool for safe iteration
protected internal virtual void OnUpdate(GameTime gameTime)
{
    var array = ArrayPool<Component>.Shared.Rent(count);
    try
    {
        _components.CopyTo(array, 0);
        // Process components without allocation
    }
    finally
    {
        ArrayPool<Component>.Shared.Return(array, clearArray: true);
    }
}

// Particle system uses object pooling
private readonly ObjectPool<Particle> _particlePool;

// Get from pool instead of 'new'
var particle = _particlePool.Get();
// ... use particle ...
_particlePool.Return(particle);

Sprite Batching

Automatic batching with layer sorting and texture grouping:

// SpriteRenderingSystem automatically batches sprites
// - Groups by texture to minimize draw calls
// - Sorts by layer for correct rendering order
// - Frustum culling for off-screen sprites

var sprite = entity.AddComponent<SpriteComponent>();
sprite.TexturePath = "assets/player.png";
sprite.Layer = 10; // Higher layers render on top
sprite.Tint = Color.White;

// Check batching stats
var (renderedCount, drawCalls) = spriteSystem.GetBatchStats();
Logger.LogInfo($"Rendered {renderedCount} sprites in {drawCalls} draw calls");

Scene Management

Brine2D now includes powerful scene management with transitions and loading screens.

Scene Transitions

using Brine2D.Engine;
using Brine2D.Engine.Transitions;

// Load scene with fade transition
await _sceneManager.LoadSceneAsync<GameScene>(
    new FadeTransition(duration: 0.5f, color: Color.Black)
);

Loading Screens

public class CustomLoadingScreen : LoadingScene
{
    protected override void OnRender(GameTime gameTime)
    {
        _renderer.DrawText($"Loading... {Progress:P0}", 500, 300, Color.White);
    }
}

// Use loading screen during scene load
await _sceneManager.LoadSceneAsync<GameScene>(
    loadingScreen: new CustomLoadingScreen(),
    transition: new FadeTransition(0.5f, Color.Black)
);

Automatic System Execution

Systems run automatically via lifecycle hooks - no manual calls needed!

public class GameScene : Scene
{
    // No need to inject UpdatePipeline or RenderPipeline!
    
    protected override void OnUpdate(GameTime gameTime)
    {
        // Your scene-specific logic
        CheckWinCondition();
        
        // ECS systems run automatically!
    }
    
    protected override void OnRender(GameTime gameTime)
    {
        // Frame management automatic!
        // ECS rendering happens automatically!
        
        // Just draw your UI/debug info
        _renderer.DrawText($"Score: {_score}", 10, 10, Color.White);
    }
}

Manual Control (Power Users)

Need fine-grained control? Opt-out of automatic behavior:

public class ManualControlScene : Scene
{
    public override bool EnableLifecycleHooks => false; // Disable automatic execution
    
    protected override void OnUpdate(GameTime gameTime)
    {
        // You control when systems run
        _updatePipeline.Execute(gameTime);
        _world.Update(gameTime);
    }
}

See the Lifecycle Hooks Guide for advanced usage.


Advanced Entity Queries

Build complex queries with a fluent API:

using Brine2D.ECS.Query;

// Find low-health enemies near the player
var weakEnemies = _world.Query()
    .With<EnemyComponent>()
    .With<HealthComponent>()
    .With<TransformComponent>()
    .Without<DeadComponent>()
    .WithTag("Boss")
    .Where(e => 
    {
        var health = e.GetComponent<HealthComponent>();
        var transform = e.GetComponent<TransformComponent>();
        var distance = Vector2.Distance(transform.Position, playerPosition);
        
        return health.CurrentHealth < 50 && distance < 200f;
    })
    .Execute();

Spatial Queries

Query entities within a radius or bounds:

// Find all entities within 200 units of player
var nearbyEntities = _world.Query()
    .WithinRadius(playerPosition, 200f)
    .With<EnemyComponent>()
    .Execute();

// Find entities within screen bounds
var visibleEntities = _world.Query()
    .WithinBounds(new Rectangle(0, 0, 1280, 720))
    .Execute();

Sorting and Pagination

// Get 5 nearest enemies
var nearestEnemies = _world.Query()
    .With<EnemyComponent>()
    .OrderBy(e => Vector2.Distance(
        e.GetComponent<TransformComponent>().Position, 
        playerPosition))
    .Take(5)
    .Execute();

// Get second page of results
var page2 = _world.Query()
    .With<ItemComponent>()
    .Skip(10)
    .Take(10)
    .Execute();

Cached Queries for Performance

// Create cached query (updates automatically)
var movingEntities = _world.CreateCachedQuery<TransformComponent, VelocityComponent>();

// Use in systems (no allocation!)
public void Update(GameTime gameTime)
{
    foreach (var entity in movingEntities.Execute())
    {
        var transform = entity.GetComponent<TransformComponent>();
        var velocity = entity.GetComponent<VelocityComponent>();
        
        transform.Position += velocity.Velocity * deltaTime;
    }
}

Entity Component System (ECS)

Brine2D's ECS framework with ASP.NET-style system pipelines.

Creating Entities

using Brine2D.ECS;
using Brine2D.ECS.Components;
using System.Numerics;

// Create an entity
var player = _world.CreateEntity("Player");
player.Tags.Add("Player");

// Add components
var transform = player.AddComponent<TransformComponent>();
transform.Position = new Vector2(400, 300);

var velocity = player.AddComponent<VelocityComponent>();
velocity.MaxSpeed = 200f;

var sprite = player.AddComponent<SpriteComponent>();
sprite.TexturePath = "assets/player.png";

Using Prefabs (Reusable Templates)

using Brine2D.ECS;

// Create a prefab
var enemyPrefab = new EntityPrefab("Enemy");
enemyPrefab.Tags.Add("Enemy");

enemyPrefab.AddComponent<TransformComponent>();
enemyPrefab.AddComponent<SpriteComponent>(s => 
{
    s.TexturePath = "assets/enemy.png";
    s.Tint = new Color(255, 100, 100);
});
enemyPrefab.AddComponent<VelocityComponent>(v => v.MaxSpeed = 150f);
enemyPrefab.AddComponent<AIControllerComponent>(ai => 
{
    ai.Behavior = AIBehavior.Chase;
    ai.TargetTag = "Player";
});

// Register and instantiate
_prefabLibrary.Register(enemyPrefab);
var enemy = enemyPrefab.Instantiate(_world, new Vector2(500, 300));

Configuring System Pipelines (ASP.NET-style!)

using Brine2D.ECS.Systems;
using Brine2D.Rendering.ECS;
using Brine2D.Input.ECS;
using Brine2D.Audio.ECS;

// Configure like ASP.NET middleware!
builder.Services.ConfigureSystemPipelines(pipelines =>
{
    // Update systems (run every frame, automatically!)
    pipelines.AddSystem<PlayerControllerSystem>();  // Order: 10 (input)
    pipelines.AddSystem<AISystem>();                // Order: 50 (AI)
    pipelines.AddSystem<VelocitySystem>();          // Order: 100 (movement)
    pipelines.AddSystem<PhysicsSystem>();           // Order: 200 (collision)
    pipelines.AddSystem<AudioSystem>();             // Order: 300 (audio)
    pipelines.AddSystem<CameraSystem>();            // Order: 400 (camera)
    
    // Render systems (run during render phase, automatically!)
    pipelines.AddSystem<SpriteRenderingSystem>();   // Order: 0 (sprites)
    pipelines.AddSystem<ParticleSystem>();          // Update + Render
    pipelines.AddSystem<DebugRenderer>();           // Order: 1000 (debug overlay)
});

Camera System

Automatic camera following with smooth movement and constraints:

// Make camera follow player
var cameraFollow = player.AddComponent<CameraFollowComponent>();
cameraFollow.CameraName = "Main";
cameraFollow.Smoothing = 5f; // Higher = slower follow
cameraFollow.Offset = new Vector2(0, -50); // Camera offset from target
cameraFollow.Deadzone = new Vector2(50, 30); // Don't move if within deadzone
cameraFollow.FollowX = true;
cameraFollow.FollowY = true;
cameraFollow.Priority = 10; // Higher priority targets take precedence

Particle System

Pooled particle effects with customizable emitters:

// Create particle emitter
var emitter = entity.AddComponent<ParticleEmitterComponent>();
emitter.IsEmitting = true;
emitter.EmissionRate = 50f; // Particles per second
emitter.MaxParticles = 200;
emitter.ParticleLifetime = 2f;

// Configure appearance
emitter.StartColor = new Color(255, 200, 0, 255);
emitter.EndColor = new Color(255, 50, 0, 0); // Fade to transparent
emitter.StartSize = 8f;
emitter.EndSize = 2f;

// Configure physics
emitter.InitialVelocity = new Vector2(0, -50);
emitter.VelocitySpread = 30f; // Random angle variance
emitter.Gravity = new Vector2(0, 100);
emitter.SpawnRadius = 10f; // Random spawn area

// Get stats
Logger.LogInfo($"Active particles: {emitter.ParticleCount}");

Save/Load System

using Brine2D.ECS.Serialization;

// Save game state
await _serializer.SaveWorldAsync(_world, "saves/game.json");

// Load game state
await _serializer.LoadAndRestoreWorldAsync(_world, "saves/game.json");

Utility Components

// Timer - Countdown with events
var timer = entity.AddComponent<TimerComponent>();
timer.Duration = 3f;
timer.OnComplete += () => Logger.LogInfo("Timer finished!");

// Lifetime - Auto-destroy after time
var lifetime = projectile.AddComponent<LifetimeComponent>();
lifetime.Lifetime = 5f;

// Tween - Simple animations
var tween = entity.AddComponent<TweenComponent>();
tween.Type = TweenType.Position;
tween.StartPosition = new Vector2(0, 0);
tween.EndPosition = new Vector2(100, 100);
tween.Duration = 1f;
tween.Easing = EasingType.EaseInOutQuad;

Event System

Decouple components with the type-safe EventBus:

using Brine2D.SDL.Common;
using Brine2D.SDL.Common.Events;

// Subscribe to events
_eventBus.Subscribe<PlayerDeathEvent>(OnPlayerDeath);
_eventBus.Subscribe<WindowResizedEvent>(OnWindowResized);

// Publish events
_eventBus.Publish(new PlayerDeathEvent { PlayerName = "Player1" });

// Unsubscribe
_eventBus.Unsubscribe<PlayerDeathEvent>(OnPlayerDeath);

Built-in Events:

  • WindowResizedEvent - Window size changes
  • WindowMinimizedEvent, WindowMaximizedEvent
  • WindowFocusGainedEvent, WindowFocusLostEvent
  • Custom events - Create your own event types

Transform Hierarchy (Parent/Child)

// Create weapon as child of player
var weapon = _world.CreateEntity("Sword");
weapon.AddComponent<TransformComponent>();
weapon.AddComponent<SpriteComponent>();

// Attach weapon to player (transforms follow parent)
weapon.SetParent(player);

// When player moves/rotates, weapon follows automatically!

Examples

Loading and Drawing Sprites

using Brine2D.Core;
using Brine2D.Rendering;

public class SpriteScene : Scene
{
    private readonly IRenderer _renderer;
    private readonly ITextureLoader _textureLoader;

    private ITexture? _playerTexture;

    protected override async Task OnLoadAsync(CancellationToken cancellationToken)
    {
        // Load with nearest neighbor filtering for pixel art
        _playerTexture = await _textureLoader.LoadTextureAsync
        (
            "assets/player.png",
            TextureScaleMode.Nearest,
            cancellationToken
        );
    }

    protected override void OnRender(GameTime gameTime)
    {
        if (_playerTexture != null)
        {
            _renderer.DrawTexture(_playerTexture, 100, 100);
        }
    }
}

Sprite Animation

using Brine2D.Core;
using Brine2D.Core.Animation;
using Brine2D.Rendering;

public class AnimatedScene : Scene
{
    private SpriteAnimator? _animator;
    private ITexture? _spriteSheet;

    protected override async Task OnLoadAsync(CancellationToken cancellationToken)
    {
        _spriteSheet = await _textureLoader.LoadTextureAsync
        (
            "assets/character.png",
            TextureScaleMode.Nearest,
            cancellationToken
        );

        _animator = new SpriteAnimator();

        // Create walk animation from sprite sheet
        var walkAnim = AnimationClip.FromSpriteSheet
        (
            "walk",
            32,
            32,
            8,
            8
        );

        _animator.AddAnimation(walkAnim);
        _animator.Play("walk");
    }

    protected override void OnRender(GameTime gameTime)
    {
        if (_spriteSheet != null && _animator?.CurrentFrame != null)
        {
            var frame = _animator.CurrentFrame;
            var rect = frame.SourceRect;

            _renderer.DrawTexture
            (
                _spriteSheet,
                rect.X, rect.Y, rect.Width, rect.Height,
                100, 100, 64, 64
            );
        }
    }

    protected override void OnUpdate(GameTime gameTime)
    {
        _animator?.Update((float)gameTime.DeltaTime);
    }
}

Playing Audio

using Brine2D.Audio;
using Brine2D.Core;
using Brine2D.Input;

public class AudioScene : Scene
{
    private readonly IAudioService _audio;

    private IMusic? _bgMusic;
    private ISoundEffect? _jumpSound;

    protected override async Task OnLoadAsync(CancellationToken cancellationToken)
    {
        _jumpSound = await _audio.LoadSoundAsync("assets/jump.wav", cancellationToken);
        _bgMusic = await _audio.LoadMusicAsync("assets/music.mp3", cancellationToken);

        _audio.PlayMusic(_bgMusic);
    }

    protected override void OnUpdate(GameTime gameTime)
    {
        if (_input.IsKeyPressed(Keys.Space) && _jumpSound != null)
        {
            _audio.PlaySound(_jumpSound);
        }
    }
}

Input Handling

using Brine2D.Core;
using Brine2D.Input;

protected override void OnUpdate(GameTime gameTime)
{
    // Keyboard
    if (_input.IsKeyDown(Keys.W))
    {
        /* Move up */
    }

    if (_input.IsKeyPressed(Keys.Space))
    {
        /* Jump */
    }

    // Mouse
    var mousePos = _input.MousePosition;

    if (_input.IsMouseButtonPressed(MouseButton.Left))
    {
        /* Click */
    }

    // Gamepad
    if (_input.IsGamepadConnected())
    {
        var leftStick = _input.GetGamepadLeftStick();

        if (_input.IsGamepadButtonPressed(GamepadButton.A))
        {
            /* Jump */
        }
    }
}

Configuration

Create a gamesettings.json file in your project:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Brine2D": "Debug"
    }
  },
  "Rendering": {
    "WindowTitle": "My Game",
    "WindowWidth": 1280,
    "WindowHeight": 720,
    "VSync": true,
    "Fullscreen": false,
    "Backend": "GPU",
    "PreferredGPUDriver": "vulkan"
  },
  "Performance": {
    "EnableOverlay": true,
    "ShowFPS": true,
    "ShowFrameTime": true,
    "ShowMemory": true
  }
}

Backend Options:

  • "GPU" - Modern SDL3 GPU API (Vulkan, D3D12, Metal)
  • "LegacyRenderer" - SDL_Renderer API (older hardware, compatibility)

GPU Drivers:

  • "vulkan" - Windows, Linux (recommended)
  • "d3d12" - Windows (native DirectX)
  • "metal" - macOS (native Metal)

Architecture

Brine2D follows a modular architecture with clear separation of concerns:

Core Packages

  • Brine2D.Core - Core abstractions, animation, collision, tilemap, pooling
  • Brine2D.Engine - Game loop, scene management, transitions
  • Brine2D.Hosting - ASP.NET-style application hosting
  • Brine2D.ECS - Entity Component System

Abstraction Layers

  • Brine2D.Rendering - Rendering abstractions (IRenderer, ITexture, ICamera)
  • Brine2D.Input - Input abstractions (IInputService, keyboard, mouse, gamepad)
  • Brine2D.Audio - Audio abstractions (IAudioService, music, sound effects)

SDL3 Implementations

  • Brine2D.Rendering.SDL - SDL3 GPU + Legacy renderer implementation
  • Brine2D.Input.SDL - SDL3 input implementation
  • Brine2D.Audio.SDL - SDL3_mixer audio implementation

ECS Bridges

  • Brine2D.Rendering.ECS - Sprite rendering, particles, camera systems
  • Brine2D.Input.ECS - Player controller system
  • Brine2D.Audio.ECS - Audio playback system

Extensions

  • Brine2D.UI - UI framework (buttons, inputs, dialogs, tabs, scroll views)

Meta-Package

  • Brine2D.Desktop - All-in-one package (recommended for most users)

Requirements

  • .NET 10 SDK
  • SDL3 (included via SDL3-CS NuGet package)
  • SDL3_image (for texture loading)
  • SDL3_mixer (for audio playback)

Platform Support

Platform Status Notes
Windows ✅ Supported Tested on Windows 10/11
Linux ⚠️ Untested Should work via SDL3
macOS ⚠️ Untested Should work via SDL3

SDL3 provides cross-platform support, but we've only tested on Windows so far. Community testing on other platforms is welcome!

Building from Source

If you want to build from source or contribute:

git clone https://github.com/CrazyPickleStudios/Brine2D.git
cd Brine2D
dotnet build

Then reference the projects directly in your game:

<ItemGroup>
  <ProjectReference Include="..\Brine2D\src\Brine2D.Desktop\Brine2D.Desktop.csproj" />
</ItemGroup>

Samples

Check out the samples/ directory for complete working examples:

FeatureDemos

Interactive demo menu showcasing all major features:

  • Query System Demo - Advanced entity queries with spatial filtering, sorting, and pagination
  • Particle System Demo - Pooled particle effects with fire, explosions, smoke, and trails
  • Collision Demo - AABB and circle colliders with physics response
  • Scene Transitions Demo - Fade transitions and custom loading screens
  • UI Components Demo - Complete UI framework showcase
  • Manual Control Demo - Power user lifecycle hook examples
  • Performance Benchmark - Sprite batching stress test with 10,000+ sprites

Run the demos:

cd samples/FeatureDemos
dotnet run

Performance hotkeys (in any demo scene):

  • F3 - Toggle performance overlay
  • F4 - Toggle frame time graph
  • F5 - Toggle memory statistics

Community & Support

Roadmap

0.5.0-betaRELEASED

  • ✅ Advanced ECS queries and filters
  • ✅ Scene transitions and loading screens
  • ✅ Lifecycle hooks with opt-out
  • ✅ Performance monitoring and profiling
  • ✅ Object pooling and sprite batching
  • ✅ Camera follow and particle systems
  • ✅ Complete UI framework

0.6.0-betaRELEASED

  • ✅ Bug fixes and stability improvements
  • ✅ Font atlas system for improved text rendering
  • ✅ Enhanced SDL3 integration

0.7.0-betaRELEASED (Current)

  • ✅ SDL3 GPU renderer (Vulkan, D3D12, Metal)
  • ✅ EventBus integration for decoupled communication
  • ✅ Window event handling (resize, minimize, etc.)
  • ✅ Improved renderer architecture
  • ✅ Both GPU and Legacy renderer backends stable

0.8.0-beta (Next Release)

  • Texture atlasing for advanced batching
  • Post-processing effects (bloom, blur, etc.)
  • Enhanced particle system (textures, rotation, trails)
  • Spatial audio
  • Multi-threaded ECS systems
  • Comprehensive documentation

1.0.0 (Stable Release)

  • Stable, production-ready API
  • Complete documentation and tutorials
  • Full platform testing (Windows, Linux, macOS)
  • Advanced ECS optimizations
  • Comprehensive sample games
  • Migration guides from beta

See the full roadmap.

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE file for details

Credits

Built with:

  • SDL3 - Simple DirectMedia Layer
  • SDL3-CS - C# bindings for SDL3

Made with ❤️ by CrazyPickle Studios

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Brine2D.Audio.ECS:

Package Downloads
Brine2D.Desktop

Modern .NET 10 game engine with ASP.NET-style API - Desktop meta-package. Includes all default implementations for building 2D games.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.7.0-beta 31 1/14/2026
0.6.0-beta 47 1/9/2026
0.5.0-beta 47 1/7/2026
0.4.0-alpha 46 1/5/2026