Add MrGameEng.UI module integrating Myra

scene.UseUI() creates a per-scene Myra Desktop and registers
UiRenderSystem last in the draw phase (UI on top, window pixels).
GameHost now exposes itself as a Game service (Myra needs the instance;
useful for games too). Myra was picked over Gum/ImGui for the
FontStashSharp ecosystem fit, pipeline-free assets and maturity; its
internal SpriteBatch use is a documented exception to the engine rule.

Sample: on-screen HUD replaces window-title-only stats — live FPS label,
music volume slider and scene-switch buttons on both scenes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-06-11 04:51:12 +03:00
co-authored by Claude Fable 5
parent a3e6d3bb0a
commit af319d1276
12 changed files with 159 additions and 8 deletions
+1
View File
@@ -47,6 +47,7 @@ public class GameHost : Game
Window.AllowUserResizing = _options.AllowResizing;
Context.AttachGraphicsDevice(GraphicsDevice);
Context.Services.Add(Window);
Context.Services.Add<Game>(this);
base.Initialize();
Context.Scenes.Switch(_initialScene);
}
+15
View File
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Myra" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MrGameEng.Core\MrGameEng.Core.csproj" />
</ItemGroup>
</Project>
+49
View File
@@ -0,0 +1,49 @@
using Friflo.Engine.ECS.Systems;
using Microsoft.Xna.Framework;
using Myra;
using Myra.Graphics2D.UI;
using MrGameEng.Core;
namespace MrGameEng.UI;
/// <summary>
/// Draw system rendering the scene's Myra <see cref="Desktop"/>. Must run after the scene's
/// world rendering (register UI last in the draw phase) — the UI is drawn on top in window
/// pixels and also processes mouse/keyboard interaction during render.
/// </summary>
public sealed class UiRenderSystem : BaseSystem
{
private readonly Desktop _desktop;
/// <summary>Creates the system for <paramref name="desktop"/>.</summary>
public UiRenderSystem(Desktop desktop) => _desktop = desktop;
/// <inheritdoc />
protected override void OnUpdateGroup() => _desktop.Render();
}
/// <summary>Wires the UI module (Myra) into a <see cref="Scene"/>.</summary>
public static class SceneUiExtensions
{
private static bool _environmentInitialized;
/// <summary>
/// Creates a Myra <see cref="Desktop"/> for this scene and registers
/// <see cref="UiRenderSystem"/> in the draw phase. Call from <c>OnLoad</c>
/// <b>after</b> <c>UseRenderer2D()</c> so the UI draws on top of the world.
/// Build the UI by assigning <see cref="Desktop.Root"/>.
/// </summary>
public static Desktop UseUI(this Scene scene)
{
// Геттер MyraEnvironment.Game бросает исключение, пока Game не задан — проверять через ??= нельзя.
if (!_environmentInitialized)
{
MyraEnvironment.Game = scene.Context.Services.Get<Game>();
_environmentInitialized = true;
}
var desktop = new Desktop();
scene.DrawSystems.Add(new UiRenderSystem(desktop));
return desktop;
}
}