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¶
- Verify existing functionality works
- Test new features (audio, atlasing, particles)
- 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
AudioSystemto 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:
- Texture Atlasing - Runtime sprite packing
- Spatial Audio - 2D positional audio
- Particles - Updated with new features
- Samples - New demo scenes for atlasing and audio
Known Issues¶
- Texture atlas max size - Limited to 2048x2048 on some hardware
-
Solution: Use multiple atlases or reduce texture count
-
Spatial audio updates every frame - May impact performance with 100+ sources
-
Solution: Disable spatial audio for distant/off-screen sources
-
Particle trails with very short lifetimes - May flicker
- Solution: Increase
ParticleLifetimeto 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:
- Report bugs: GitHub Issues
- Feature requests: GitHub Discussions
- Show your games: Share what you're building!
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 systemAtlasBuilder- Intelligent bin packing algorithmAudioListenerComponent- Spatial audio listener (ECS)AudioSourceComponent- Spatial audio source (ECS)AudioSystem- Updates spatial audio every frame- Particle textures -
TexturePathproperty on emitters - Particle rotation -
StartRotation,EndRotation,RotationSpeed - Particle trails -
TrailEnabled,TrailLength,TrailColor - Blend modes -
BlendModeenum (None, AlphaBlend, Additive) - 7 emitter shapes - Point, Circle, Ring, Box, Cone, Line, Burst
- SDL3_mixer callbacks -
OnTrackStoppedevent with proper cleanup - SpriteBatcher rotation - Now respects rotation transforms
Changed¶
ParticleEmitterComponent- New properties for textures, rotation, trailsSpriteRenderingSystem- Uses texture atlases when availableSDL3AudioService- Registers native callbacks for track lifecycleSpriteBatcher- 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