Add in-game developer console (MrGameEng.DevConsole)

Core gains a static Log (Debug/Info/Warning/Error + event); the engine
logs key events like scene switches. The console captures Log output
into a 2048-line ring buffer and executes registered commands with
input history (up/down), Tab prefix completion and scrolling
(PageUp/PageDown/End/wheel). Built-ins: help, clear, echo, timescale,
close, quit; games register their own (sample: stress/main/beep).

Console core is pure logic covered by headless tests; the Myra overlay
renders a single label rebuilt only when the Revision counter moves -
an idle or closed console costs nothing per frame. Toggled with the
backquote key; sample gameplay hotkeys are suppressed while open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-06-11 05:17:08 +03:00
co-authored by Claude Fable 5
parent e06f24a319
commit d498c70660
14 changed files with 788 additions and 3 deletions
@@ -12,6 +12,7 @@
<ProjectReference Include="..\..\src\MrGameEng.Audio\MrGameEng.Audio.csproj" />
<ProjectReference Include="..\..\src\MrGameEng.Assets\MrGameEng.Assets.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"
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
+12 -1
View File
@@ -12,10 +12,16 @@ namespace MrGameEng.Sample;
/// <summary>Управление игроком (WASD/стрелки) и прыжок-писк на пробел.</summary>
public sealed class PlayerControlSystem(
Entity player, ActionMap<SampleAction> actions, AudioManager audio, SoundEffect beep) : BaseSystem
Entity player, ActionMap<SampleAction> actions, AudioManager audio, SoundEffect beep,
MrGameEng.DevConsole.DevConsole console) : BaseSystem
{
protected override void OnUpdateGroup()
{
if (console.IsOpen)
{
return;
}
ref var transform = ref player.GetComponent<Transform2D>();
var move = new Vector2(
actions.GetAxis(SampleAction.MoveLeft, SampleAction.MoveRight),
@@ -131,6 +137,11 @@ public sealed class SceneHotkeysSystem(
{
protected override void OnUpdateGroup()
{
if (context.Services.Get<MrGameEng.DevConsole.DevConsole>().IsOpen)
{
return;
}
if (actions.IsPressed(SampleAction.Pause))
{
context.Clock.TimeScale = context.Clock.TimeScale > 0f ? 0f : 1f;
+20 -1
View File
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
using MrGameEng.Assets;
using MrGameEng.Audio;
using MrGameEng.Core;
using MrGameEng.DevConsole;
using MrGameEng.Graphics;
using MrGameEng.Input;
using MrGameEng.UI;
@@ -113,7 +114,25 @@ public sealed class MainScene : Scene
panel.Widgets.Add(switchButton);
desktop.Root = panel;
UpdateSystems.Add(new PlayerControlSystem(player, actions, audio, beep));
// Консоль разработчика (клавиша `) — последней, чтобы рисовалась поверх UI.
var console = this.UseDevConsole();
console.Register("stress", "switch to the stress scene", (_, _) =>
{
if (!Context.Scenes.IsTransitioning)
{
Context.Scenes.Switch(new StressScene(), Transition.Fade(0.8f));
}
});
console.Register("main", "switch to the main scene", (_, _) =>
{
if (!Context.Scenes.IsTransitioning)
{
Context.Scenes.Switch(new MainScene(), Transition.Fade(0.8f));
}
});
console.Register("beep", "play the beep sound", (_, _) => audio.Play(beep));
UpdateSystems.Add(new PlayerControlSystem(player, actions, audio, beep, console));
UpdateSystems.Add(new BounceSystem(WorldBounds));
UpdateSystems.Add(new CameraControlSystem(camera, player, input));
UpdateSystems.Add(new SceneHotkeysSystem(Context, actions, () => new StressScene(), Transition.Fade(0.8f)));
@@ -2,6 +2,7 @@ using Friflo.Engine.ECS;
using Microsoft.Xna.Framework;
using MrGameEng.Assets;
using MrGameEng.Core;
using MrGameEng.DevConsole;
using MrGameEng.Graphics;
using MrGameEng.Input;
using MrGameEng.UI;
@@ -71,6 +72,8 @@ public sealed class StressScene : Scene
panel.Widgets.Add(backButton);
desktop.Root = panel;
this.UseDevConsole(); // поверх UI
UpdateSystems.Add(new BounceSystem(WorldBounds));
UpdateSystems.Add(new StressCameraSystem(camera, input));
UpdateSystems.Add(new SceneHotkeysSystem(Context, actions, () => new MainScene(), Transition.Wipe(0.8f, Color.DarkSlateBlue)));