Ga naar inhoud

Debuggen

Doelstelling

Tijdens het ontwerpen van verschillende features is het belangrijk om goede debug tools te hebben. Anders is het moeilijk om snel en gemakkelijk dingen uit te proberen om de beste oplossing te vinden.

Toegepaste oplossing

Dear ImGui

alle voorbeeld windows van Dear ImGui in ons spel over alles heen

Om variabelen te testen en zichtbaar te maken op een mooie manier heb ik Dear ImGui toegevoegd. Het is een gui library geschreven in C++ maar met behulp van wat extra libraries kon het gemakkelijk toegevoegd worden aan Monogame.

Ook heb ik het op een zulke manier toegevoegd zodat overal, niet alleen in de draw thread, nieuwe elementen gemaakt kunnen worden. Wat het gebruik ervan veel makkelijker maakt.

public class ImGuiSystem : BaseSystem
{
    protected ImGUIRenderer ImGUIRenderer { get; set; }

    public override void Initialize(App app, SystemHandler handler)
    {
        base.Initialize(app, handler);

        ImGUIRenderer = new ImGUIRenderer(app)
            .Initialize();
    }

    public override void LoadContent()
    {
        ImGUIRenderer.RebuildFontAtlas();

        var stylePtr = ImGui.GetStyle();
        stylePtr.WindowRounding = 1f;

Hier en in de method hieronder word ervoor gezorgd dat het altijd mogelijk is om nieuwe elementen te maken. Dit word gedaan door de begin en einde om te draaien. Het enigste nadeel was dat er extra stappen nodig waren voor systemen om dit als laatste te doen in de frame, maar niet pas in de volgende frame. Hierdoor word er ook voor gezorgd dat het altijd boven alles word getekend.

        ImGUIRenderer.BeginLayout(new GameTime(TimeSpan.Zero, TimeSpan.Zero));
    }

    public override void DrawCleanup(GameTime gameTime, SpriteBatch spriteBatch)
    {
        ImGUIRenderer.EndLayout();

        ImGUIRenderer.BeginLayout(gameTime);
    }
}

Gizmos

Maar getallen zijn niet de enigste soort data waarop geïtereerd moet worden. Vandaar heb ik ook een gizmo systeem gemaakt, waarmee snel en simpel verschillende vormen getekend kunnen worden.

Ook de gizmo systeem kan altijd overal gebruikt worden, niet alleen in de draw thread. En word het altijd boven alles getekend, zodat het goed te zien is. Uitgaande dat de gekozen kleur dan ook goed zichtbaar is.

public class GizmoSystem : BaseSystem
{
    protected AssetManager assets;
    protected Queue<GizmoBase> gizmosToDraw = new();

    public override void Initialize(App app, SystemHandler handler)
    {
        base.Initialize(app, handler);
        assets = handler.GetSystem<AssetManager>();
    }

    public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
        var pixel = assets.GetPixel();

        foreach (var gizmo in gizmosToDraw)
            gizmo.Draw(spriteBatch, pixel);

        gizmosToDraw.Clear();
    }


    public GizmoSystem Add(GizmoBase gizmo)
    {
        gizmosToDraw.Enqueue(gizmo);
        return this;
    }

    public GizmoSystem Add(ICollection<GizmoBase> gizmos)
    {
        gizmosToDraw.EnqueueMultiple(gizmos);
        return this;
    }


    public const float GizmoSize = 1.5f;

Voor de gizmos heb ik een simpele class structuur gebruikt. In de vorm van records zodat de fields/properties niet handmatig hoeven worden aangemaakt. Hier is geen interface gebruikt omdat dan het kleur en grootte gedeelte van elke gizmo niet vereist kan worden.

    public abstract record GizmoBase(Color? color = null, float size = GizmoSize)
    {
        public abstract void Draw(SpriteBatch spriteBatch, Texture2D pixel);
    }

    public GizmoSystem AddPoint(Vector2 point, Color? color = null, float size = GizmoSize)
        => Add(new PointGizmo(point, color, size));
    public record PointGizmo(Vector2 point, Color? color = null, float size = GizmoSize) : GizmoBase(color, size)
    {
        public override void Draw(SpriteBatch spriteBatch, Texture2D pixel)
        {
            spriteBatch.BetterDraw(pixel, point, new(size*2), color: color);
            spriteBatch.BetterDraw(pixel, point, new(size*2), color: color, rotation: MathF.PI * .25f);
        }
    }

Alleen het probleem was met deze classen die deel zijn van het systeem dat het niet zo snel te schrijven is als ik wilde. Dus heb ik voor elke soort gizmo een helper method toegevoegd dat het korter en makkelijker maakt.

    public GizmoSystem AddLine(Line line, Color? color = null, float size = GizmoSize)
        => Add(new LineGizmo(line, color, size));
    public record LineGizmo(Line line, Color? color = null, float size = GizmoSize) : GizmoBase(color, size)
    {
        public override void Draw(SpriteBatch spriteBatch, Texture2D pixel)
        {
            var (theta, radius) = MathExtras.CartesianToPolar((line.a - line.b).ToNumerics()).ToXna();

            spriteBatch.BetterDraw(
                pixel,
                line.a,
                new(radius, size),
                MathF.PI - theta,
                color,
                new(0, .5f)
            );
        }
    }

    public GizmoSystem AddRect(Rectangle rect, Color? color = null, float size = GizmoSize)
        => Add(new RectGizmo(rect, color, size));
    public record RectGizmo(Rectangle rect, Color? color = null, float size = GizmoSize) : GizmoBase(color, size)
    {
        public override void Draw(SpriteBatch spriteBatch, Texture2D pixel)
        {
            var (x, y, w, h) = rect;

            spriteBatch.BetterDraw(pixel, new(x, y), new(w, size), color: color, origin: new(0, .5f)); // top
            spriteBatch.BetterDraw(pixel, new(x, y), new(size, h), color: color, origin: new(.5f, 0)); // left
            spriteBatch.BetterDraw(pixel, new(x, y+h), new(w, size), color: color, origin: new(0, .5f)); // bottom
            spriteBatch.BetterDraw(pixel, new(x+w, y), new(size, h), color: color, origin: new(.5f, 0)); // right
        }
    }

    public GizmoSystem AddCircle(Circle circle, int resolution = 16, Color? color = null, float size = GizmoSize)
        => Add(new CircleGizmo(circle, resolution, color, size));
    public record CircleGizmo(Circle circle, int resolution = 16, Color? color = null, float size = GizmoSize) : GizmoBase(color, size)
    {
        public override void Draw(SpriteBatch spriteBatch, Texture2D pixel)
        {
            var (center, radius) = circle;
            var sectionPosAngle = MathF.PI*2f / resolution;

            var a = MathExtras.PolarToCartesian(new(0, radius)).ToXna();
            var b = MathExtras.PolarToCartesian(new(sectionPosAngle, radius)).ToXna();

            var (sectionAngle, sectionLength) = MathExtras.CartesianToPolar((b - a).ToNumerics()).ToXna();

            for (var i = 0; i < resolution; i++)
            {
                var posAngle = i * sectionPosAngle;
                var lineAngle = posAngle - sectionAngle;

                spriteBatch.BetterDraw(
                    pixel,
                    center + MathExtras.PolarToCartesian(new(posAngle, radius)).ToXna(),
                    new(sectionLength, size),
                    -lineAngle,
                    color,
                    new(0, .5f)
                );
            }
        }
    }

    public GizmoSystem AddCustom(CustomGizmoDrawer drawer, Color? color = null, float size = GizmoSize)
        => Add(new CustomGizmo(drawer, color, size));
    public delegate void CustomGizmoDrawer(SpriteBatch spriteBatch, Texture2D pixel, Color? color, float size);
    public record CustomGizmo(CustomGizmoDrawer drawer, Color? color = null, float size = GizmoSize) : GizmoBase(color, size)
    {
        public override void Draw(SpriteBatch spriteBatch, Texture2D pixel)
            => drawer.Invoke(spriteBatch, pixel, color, size);
    }
}

Bronnen


Laatst geüpdatet: June 9, 2024
Gecreëerd: May 9, 2024