Add MrGameEng.Atlases: texture atlas builder, runtime loader and CLI tool
CI / build-test (push) Failing after 1m13s

AtlasBuilder packs a directory tree of loose images into atlas pages plus
JSON metadata (deterministic shelf packing, incremental rebuilds, orphan
cleanup); TextureAtlas loads them back handing out Texture2DRegions, so
sprites from one page batch into a single draw call. The asset handle
generator maps .atlas files to TextureAtlas and skips page images.
Demonstrated in the sample (Assets/Atlases + 'atlas' console command),
wrapped as tools/MrGameEng.AtlasTool for build scripts.

Documented dependency exception: Atlases depends on Graphics and Assets.
This commit is contained in:
Leonid Pershin
2026-06-11 08:18:30 +03:00
parent d6f19b9119
commit b318d1e795
21 changed files with 1209 additions and 1 deletions
@@ -0,0 +1,31 @@
{
"version": 1,
"name": "Textures",
"pageSize": 2048,
"padding": 2,
"pages": [
{
"file": "Textures.atlas.0.png",
"width": 256,
"height": 128
}
],
"regions": [
{
"key": "player",
"page": 0,
"x": 68,
"y": 2,
"w": 64,
"h": 32
},
{
"key": "shapes",
"page": 0,
"x": 2,
"y": 2,
"w": 64,
"h": 64
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -11,6 +11,7 @@
<ProjectReference Include="..\..\src\MrGameEng.Input\MrGameEng.Input.csproj" />
<ProjectReference Include="..\..\src\MrGameEng.Audio\MrGameEng.Audio.csproj" />
<ProjectReference Include="..\..\src\MrGameEng.Assets\MrGameEng.Assets.csproj" />
<ProjectReference Include="..\..\src\MrGameEng.Atlases\MrGameEng.Atlases.csproj" />
<ProjectReference Include="..\..\src\MrGameEng.UI\MrGameEng.UI.csproj" />
<ProjectReference Include="..\..\src\MrGameEng.DevConsole\MrGameEng.DevConsole.csproj" />
<ProjectReference Include="..\..\src\MrGameEng.Assets.Generator\MrGameEng.Assets.Generator.csproj"
@@ -1,6 +1,7 @@
using Friflo.Engine.ECS;
using Microsoft.Xna.Framework;
using MrGameEng.Assets;
using MrGameEng.Atlases;
using MrGameEng.Audio;
using MrGameEng.Core;
using MrGameEng.DevConsole;
@@ -36,6 +37,11 @@ public sealed class MainScene : Scene
var shapesTexture = assets.Load(GameAssets.Textures.Shapes);
var beep = assets.Load(GameAssets.Sounds.Beep);
// Текстурный атлас: Assets/Atlases собран утилитой MrGameEng.AtlasTool из Assets/Textures
// (см. README). Регионы адресуются исходным путём без расширения и батчатся в один draw call.
Context.UseTextureAtlases();
var atlas = assets.Load(GameAssets.Atlases.Textures);
var shapeRegions = new[]
{
new Texture2DRegion(shapesTexture, new Rectangle(0, 0, 32, 32)),
@@ -87,6 +93,15 @@ public sealed class MainScene : Scene
playerSprite,
new SpriteAnimator(blink));
// Пара спрайтов из атласа рядом со стартом игрока — вся пара рисуется одним draw call.
var atlasShowcase = new[] { ("player", -80f), ("shapes", 80f) };
foreach (var (key, offsetX) in atlasShowcase)
{
var sprite = new Sprite(atlas.GetRegion(key), SampleLayers.Actors);
sprite.CenterOrigin();
Store.CreateEntity(new Transform2D(new Vector2(offsetX, -120f)), sprite);
}
var camera = Store.CreateEntity(new Camera(Vector2.Zero, zoom: 1f, bounds: WorldBounds));
// HUD: золотой квадрат в углу на screen-space слое — не двигается с камерой.
@@ -131,6 +146,14 @@ public sealed class MainScene : Scene
}
});
console.Register("beep", "play the beep sound", (_, _) => audio.Play(beep));
console.Register("atlas", "list texture atlas regions", (c, _) =>
{
c.WriteLine($"atlas '{atlas.Name}': {atlas.Pages.Count} page(s), {atlas.Regions.Count} region(s)");
foreach (var (key, region) in atlas.Regions.OrderBy(r => r.Key, StringComparer.Ordinal))
{
c.WriteLine($" {key}: {region.Bounds.Width}x{region.Bounds.Height} at ({region.Bounds.X},{region.Bounds.Y})");
}
});
UpdateSystems.Add(new PlayerControlSystem(player, actions, audio, beep, console));
UpdateSystems.Add(new BounceSystem(WorldBounds));