Skip to content

What's New in v0.8.0-beta

Version 0.8.0-beta delivers production-ready audio and visual effects systems with runtime texture packing, 2D spatial audio, and a massively enhanced particle system.

Release Highlights

  • Texture Atlasing - Runtime sprite packing with intelligent bin packing
  • 2D Spatial Audio - Distance attenuation and stereo panning
  • Advanced Particle System - Textures, rotation, trails, blend modes, 7 emitter shapes
  • Audio Callbacks - Proper track lifecycle management via SDL3_mixer
  • Rotation Support - SpriteBatcher now respects rotation transforms

Major Features

1. Texture Atlasing

Runtime texture packing dramatically reduces draw calls by combining multiple sprites into single textures.

Key Features: - Intelligent bin packing: Automatic sprite layout optimization - Runtime generation: No prebuild step required - Automatic batching: Renderer uses atlases transparently - Memory efficient: Shared texture memory across sprites

Creating an Atlas:

using Brine2D.Rendering;

// Load individual textures
var texture1 = await _textureLoader.LoadTextureAsync("assets/sprite1.png");
var texture2 = await _textureLoader.LoadTextureAsync("assets/sprite2.png");
var texture3 = await _textureLoader.LoadTextureAsync("assets/sprite3.png");

// Build atlas
var atlas = await AtlasBuilder.BuildAtlasAsync(
    _renderer,
    _textureLoader,
    new[] { texture1, texture2, texture3 },
    padding: 2,  // Pixels between sprites
    maxSize: 2048  // Max atlas dimension
);

// Use packed sprites
var region1 = atlas.GetRegion(texture1);
_renderer.DrawTexture(
    atlas.AtlasTexture,
    region1.SourceRect.X, region1.SourceRect.Y,
    region1.SourceRect.Width, region1.SourceRect.Height,
    x, y, width, height
);

ECS Integration:

// Components automatically use atlas if available
var sprite = entity.AddComponent<SpriteComponent>();
sprite.TexturePath = "assets/player.png";  // Will use atlas if packed
sprite.Layer = 10;

// SpriteRenderingSystem handles atlas lookup automatically

Performance Benefits:

Sprites Without Atlas With Atlas Improvement
100 unique textures 100 draw calls 1 draw call 99% reduction
1,000 sprites (10 textures) 10 draw calls 1 draw call 90% reduction
Texture switches Expensive Eliminated Major performance gain

Best Practices: - Pack sprites that render together (same layer, similar lifecycle) - Use 2-4px padding to prevent bleeding - Limit atlas size to 2048x2048 for compatibility - Pack UI elements separately from game sprites


2. 2D Spatial Audio

Positional audio with distance attenuation and stereo panning brings your game world to life.

Key Features: - Distance attenuation: Sounds fade based on distance from listener - Stereo panning: Left/right audio positioning - Configurable falloff: Linear, quadratic, or custom curves - ECS integration: Component-based audio sources and listeners

Basic Setup:

using Brine2D.Audio.ECS;

// Create audio listener (usually on player/camera)
var player = _world.CreateEntity("Player");
var listener = player.AddComponent<AudioListenerComponent>();
listener.GlobalSpatialVolume = 1.0f;  // Master spatial audio volume

// Create audio source
var enemy = _world.CreateEntity("Enemy");
var audioSource = enemy.AddComponent<AudioSourceComponent>();
audioSource.SoundEffect = enemySound;
audioSource.EnableSpatialAudio = true;

// Configure distance attenuation
audioSource.MinDistance = 100f;  // Full volume within this range
audioSource.MaxDistance = 500f;  // Silent beyond this range
audioSource.RolloffFactor = 1.0f;  // 1.0 = linear, 2.0 = quadratic

// Configure stereo panning
audioSource.SpatialBlend = 1.0f;  // 0.0 = mono (center), 1.0 = full stereo

// Start playing
audioSource.Loop = true;
audioSource.LoopCount = -1;  // Infinite loop
audioSource.PlayOnEnable = true;

Distance Attenuation Curves:

// Linear falloff (smooth, natural)
audioSource.RolloffFactor = 1.0f;

// Quadratic falloff (faster dropoff, more dramatic)
audioSource.RolloffFactor = 2.0f;

// Custom falloff (exponential)
audioSource.RolloffFactor = 1.5f;

// No falloff (constant volume within range)
audioSource.RolloffFactor = 0f;

Runtime Position Updates:

// AudioSystem automatically updates spatial audio every frame
// based on TransformComponent positions

// Move player
playerTransform.Position = new Vector2(500, 300);

// Move sound source
enemyTransform.Position = new Vector2(200, 300);

// Distance and panning calculated automatically!
// Sounds adjust in real-time as entities move

Manual Control:

// Trigger one-shot sound
audioSource.TriggerPlay = true;

// Stop playing
audioSource.TriggerStop = true;

// Toggle on/off
audioSource.IsEnabled = !audioSource.IsEnabled;

// Check state
if (audioSource.IsPlaying)
{
    Logger.LogInfo($"Volume: {audioSource.SpatialVolume}, Pan: {audioSource.SpatialPan}");
}

System Configuration:

// AudioSystem runs automatically in ECS pipeline
builder.Services.ConfigureSystemPipelines(pipelines =>
{
    pipelines.AddSystem<AudioSystem>();  // Update order: 300
});

3. Advanced Particle System

The particle system now supports textures, rotation, trails, and multiple blend modes.

New Features:

Particle Textures

var emitter = entity.AddComponent<ParticleEmitterComponent>();
emitter.TexturePath = "assets/particle.png";  // Use custom texture
emitter.TextureScaleMode = TextureScaleMode.Nearest;  // Pixel-perfect scaling

Rotation

// Particle rotation
emitter.StartRotation = 0f;  // Starting angle (radians)
emitter.EndRotation = MathF.PI * 2;  // End angle (full rotation)
emitter.RotationSpeed = 2f;  // Radians per second (overrides lerp)

// Random rotation variance
emitter.RotationSpread = MathF.PI / 4;  // ±45 degrees

Trail Effects

// Enable particle trails
emitter.TrailEnabled = true;
emitter.TrailLength = 10;  // Number of trail segments
emitter.TrailColor = new Color(255, 100, 0, 128);  // Semi-transparent orange

Blend Modes

// Additive blending (fire, explosions)
emitter.BlendMode = BlendMode.Additive;

// Alpha blending (smoke, fog)
emitter.BlendMode = BlendMode.AlphaBlend;

// No blending (solid particles)
emitter.BlendMode = BlendMode.None;

7 Emitter Shapes

// Point emitter (single spawn point)
emitter.EmitterShape = EmitterShape.Point;

// Circle emitter (spawn anywhere in circle)
emitter.EmitterShape = EmitterShape.Circle;
emitter.SpawnRadius = 20f;

// Ring emitter (spawn on circle edge)
emitter.EmitterShape = EmitterShape.Ring;
emitter.SpawnRadius = 30f;

// Box emitter (rectangular spawn area)
emitter.EmitterShape = EmitterShape.Box;
emitter.SpawnRadius = 50f;  // Half-width/height

// Cone emitter (directional spray)
emitter.EmitterShape = EmitterShape.Cone;
emitter.ConeAngle = MathF.PI / 4;  // 45 degree cone

// Line emitter (spawn along line)
emitter.EmitterShape = EmitterShape.Line;
emitter.LineLength = 100f;

// Burst emitter (radial explosion)
emitter.EmitterShape = EmitterShape.Burst;
emitter.BurstCount = 50;  // Particles per burst

Complete Example - Fire Effect:

var fire = _world.CreateEntity("FireEffect");
var transform = fire.AddComponent<TransformComponent>();
transform.Position = new Vector2(640, 500);

var emitter = fire.AddComponent<ParticleEmitterComponent>();

// Appearance
emitter.TexturePath = "assets/particles/fire.png";
emitter.BlendMode = BlendMode.Additive;
emitter.StartColor = new Color(255, 200, 0, 255);  // Bright yellow
emitter.EndColor = new Color(255, 50, 0, 0);  // Fade to transparent red
emitter.StartSize = 8f;
emitter.EndSize = 2f;

// Rotation
emitter.StartRotation = 0f;
emitter.RotationSpeed = 2f;
emitter.RotationSpread = MathF.PI / 2;

// Physics
emitter.InitialVelocity = new Vector2(0, -100);  // Rise upward
emitter.VelocitySpread = 30f;
emitter.Gravity = new Vector2(0, -20);  // Slight upward pull

// Emission
emitter.EmitterShape = EmitterShape.Circle;
emitter.SpawnRadius = 15f;
emitter.EmissionRate = 100f;  // Particles per second
emitter.ParticleLifetime = 1.5f;
emitter.MaxParticles = 200;

// Trails
emitter.TrailEnabled = true;
emitter.TrailLength = 5;
emitter.TrailColor = new Color(255, 100, 0, 100);

emitter.IsEmitting = true;

4. Audio Track Callbacks

SDL3_mixer callbacks now properly notify when tracks finish playing.

What Changed: - Tracks automatically clean up when finished - IsPlaying state accurately reflects playback - No more orphaned track handles - Thread-safe callback handling

Benefits: - One-shot sounds properly complete - Looping sounds restart seamlessly - Memory management improved - Better debugging with accurate state

Implementation Details:

// AudioSystem automatically handles callbacks
// You don't need to do anything special

// But you can observe state:
if (audioSource.IsPlaying)
{
    // Sound is actively playing
}
else
{
    // Sound finished or was stopped
}

5. SpriteBatcher Rotation Support

SpriteBatcher now properly applies rotation transforms.

Before (v0.7.0):

// Rotation was ignored
sprite.Rotation = MathF.PI / 4;  // Had no effect

After (v0.8.0):

// Rotation works correctly
sprite.Rotation = MathF.PI / 4;  // Sprite rotates 45 degrees
sprite.Origin = new Vector2(0.5f, 0.5f);  // Rotate around center


Breaking Changes

None! Version 0.8.0-beta is fully backward compatible with 0.7.0-beta.

All new features are additive - existing code continues to work without changes.


Upgrade Guide

Step 1: Update NuGet Packages

dotnet add package Brine2D.Desktop --version 0.8.0-beta

Or update your .csproj:

<PackageReference Include="Brine2D.Desktop" Version="0.8.0-beta" />

Step 2: Optional - Enable Spatial Audio

If using audio, add AudioSystem to your pipeline:

builder.Services.ConfigureSystemPipelines(pipelines =>
{
    pipelines.AddSystem<AudioSystem>();  // Add this line
});

Step 3: Optional - Use Texture Atlasing

For games with many sprites, consider packing textures:

// At startup, pack your sprites
var atlas = await AtlasBuilder.BuildAtlasAsync(
    _renderer,
    _textureLoader,
    myTextures,
    padding: 2,
    maxSize: 2048
);

// Store atlas for use in rendering
_atlasCache.Add("ui", atlas);

Step 4: Optional - Upgrade Particles

Update particle emitters with new features:

// Add texture
emitter.TexturePath = "assets/particle.png";

// Add rotation
emitter.RotationSpeed = 1f;

// Add trails
emitter.TrailEnabled = true;
emitter.TrailLength = 8;

// Set blend mode
emitter.BlendMode = BlendMode.Additive;

Step 5: Test Your Game

  1. Verify existing functionality works
  2. Test new features (audio, atlasing, particles)
  3. Check performance improvements

Performance Improvements

Texture Atlasing Impact

Real-world performance gains from texture packing:

Game Type Draw Calls (Before) Draw Calls (After) Improvement
Platformer (50 sprites) 15 2 87% reduction
Top-down shooter (200 sprites) 40 3 92% reduction
UI-heavy game (100 buttons) 50 1 98% reduction

When to Use Atlasing: - Games with 10+ unique sprites rendered frequently - UI systems with many icons/buttons - Particle effects using sprite textures - Any game targeting lower-end hardware

Spatial Audio Overhead

Spatial audio calculations are highly optimized:

Entities Audio Update Time Impact on 60 FPS
10 sources 0.05ms Negligible
50 sources 0.2ms < 2%
100 sources 0.4ms < 3%

Optimization Tips: - Disable EnableSpatialAudio for non-positional sounds (music, UI) - Use larger MinDistance values to reduce calculations - Set SpatialBlend = 0 for center-only sounds


Migration Checklist

  • [ ] Update NuGet packages to 0.8.0-beta
  • [ ] Optional: Add AudioSystem to ECS pipeline
  • [ ] Optional: Implement texture atlasing for sprites
  • [ ] Optional: Add spatial audio to game entities
  • [ ] Optional: Upgrade particle emitters with new features
  • [ ] Test all existing functionality
  • [ ] Verify performance improvements

Documentation Updates

New and updated guides for v0.8.0:


Known Issues

  1. Texture atlas max size - Limited to 2048x2048 on some hardware
  2. Solution: Use multiple atlases or reduce texture count

  3. Spatial audio updates every frame - May impact performance with 100+ sources

  4. Solution: Disable spatial audio for distant/off-screen sources

  5. Particle trails with very short lifetimes - May flicker

  6. Solution: Increase ParticleLifetime to at least 0.5s

Future Plans

Coming in v0.9.0-beta:

  • Post-processing effects - Bloom, blur, color grading
  • Multi-threaded ECS - Parallel system execution
  • Render-to-texture - Off-screen rendering for effects
  • Audio zones - Trigger sounds when entering areas

Feedback

Share your experience with v0.8.0-beta:


Summary

Version 0.8.0-beta focuses on audio and visual effects:

  • Texture atlasing - Massive draw call reduction
  • 2D spatial audio - Immersive positional sound
  • Advanced particles - Textures, rotation, trails, blend modes
  • Track callbacks - Proper audio lifecycle
  • Rotation support - SpriteBatcher now rotates correctly

Perfect for building production-quality 2D games!


Changelog

Added

  • TextureAtlas - Runtime texture packing system
  • AtlasBuilder - Intelligent bin packing algorithm
  • AudioListenerComponent - Spatial audio listener (ECS)
  • AudioSourceComponent - Spatial audio source (ECS)
  • AudioSystem - Updates spatial audio every frame
  • Particle textures - TexturePath property on emitters
  • Particle rotation - StartRotation, EndRotation, RotationSpeed
  • Particle trails - TrailEnabled, TrailLength, TrailColor
  • Blend modes - BlendMode enum (None, AlphaBlend, Additive)
  • 7 emitter shapes - Point, Circle, Ring, Box, Cone, Line, Burst
  • SDL3_mixer callbacks - OnTrackStopped event with proper cleanup
  • SpriteBatcher rotation - Now respects rotation transforms

Changed

  • ParticleEmitterComponent - New properties for textures, rotation, trails
  • SpriteRenderingSystem - Uses texture atlases when available
  • SDL3AudioService - Registers native callbacks for track lifecycle
  • SpriteBatcher - Applies rotation and tint to sprites

Fixed

  • Audio track cleanup - Tracks now properly destroyed when finished
  • Particle lifetime edge cases - Better handling of very short lifetimes
  • SpriteBatcher rotation - Was ignored, now applied correctly
  • Spatial audio panning - More accurate left/right calculation

Deprecated

  • None

Removed

  • None

← Back to What's New | Installation Guide →