using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections.Generic; using System.Diagnostics; namespace SemiColinGames { public static class Debug { struct DebugRect { public Rectangle Rect; public Color Color; public DebugRect(Rectangle rect, Color color) { Rect = rect; Color = color; } } struct DebugLine { public Point Start; public Point End; public Color Color; public DebugLine(Point start, Point end, Color color) { Start = start; End = end; Color = color; } } public static bool Enabled = true; // This is a LinkedList instead of a List because SetFpsText() adds to its front. static readonly LinkedList toasts = new LinkedList(); // Lines in excess of MAX_LINES get dropped on the floor. const int MAX_LINES = 2000; const int MAX_LINE_VERTICES = MAX_LINES * 2; static int lineIdx = 0; static VertexPositionColor[] lineVertices; static VertexBuffer vertexBuffer; [Conditional("DEBUG")] public static void Initialize(GraphicsDevice graphics) { lineVertices = new VertexPositionColor[MAX_LINE_VERTICES]; vertexBuffer?.Dispose(); vertexBuffer = new VertexBuffer( graphics, typeof(VertexPositionColor), MAX_LINE_VERTICES, BufferUsage.WriteOnly); } [Conditional("DEBUG")] public static void WriteLine(string s) { System.Diagnostics.Debug.WriteLine(s); } [Conditional("DEBUG")] public static void WriteLine(string s, params object[] args) { System.Diagnostics.Debug.WriteLine(s, args); } [Conditional("DEBUG")] public static void Clear(bool paused) { toasts.Clear(); if (!paused) { lineIdx = 0; } } [Conditional("DEBUG")] public static void AddToast(string s) { toasts.AddLast(s); } // FPS text is always displayed as the first toast (if set). [Conditional("DEBUG")] public static void SetFpsText(string s) { toasts.AddFirst(s); } [Conditional("DEBUG")] public static void AddRect(Rectangle rect, Color color) { AddLine(rect.Left, rect.Top + 1, rect.Right, rect.Top + 1, color); AddLine(rect.Left + 1, rect.Top + 1, rect.Left + 1, rect.Bottom, color); AddLine(rect.Right, rect.Top + 1, rect.Right, rect.Bottom, color); AddLine(rect.Left + 1, rect.Bottom, rect.Right, rect.Bottom, color); } [Conditional("DEBUG")] public static void AddRect(AABB box, Color color) { Rectangle rect = new Rectangle( (int) (box.Position.X - box.HalfSize.X), (int) (box.Position.Y - box.HalfSize.Y), (int) (box.HalfSize.X * 2), (int) (box.HalfSize.Y * 2)); AddRect(rect, color); } [Conditional("DEBUG")] public static void AddPoint(Vector2 v, Color color) { AddPoint(v.ToPoint(), color); } [Conditional("DEBUG")] public static void AddPoint(Point p, Color color) { AddLine(p.X, p.Y - 2, p.X, p.Y + 1, color); AddLine(p.X - 2, p.Y, p.X + 1, p.Y, color); } [Conditional("DEBUG")] public static void AddLine(int p1x, int p1y, int p2x, int p2y, Color color) { if (lineIdx >= MAX_LINE_VERTICES) { return; } lineVertices[lineIdx] = new VertexPositionColor(new Vector3(p1x, p1y, 0), color); lineVertices[lineIdx + 1] = new VertexPositionColor(new Vector3(p2x, p2y, 0), color); lineIdx += 2; } [Conditional("DEBUG")] public static void AddLine(Point start, Point end, Color color) { AddLine(start.X, start.Y, end.X, end.Y, color); } [Conditional("DEBUG")] public static void AddLine(Vector2 start, Vector2 end, Color color) { AddLine(start.ToPoint(), end.ToPoint(), color); } [Conditional("DEBUG")] public static void DrawToasts(SpriteBatch spriteBatch) { if (!Enabled) { return; } // UI should start at least 48px from the left edge of the screen and 27 from the top, as per: // https://docs.microsoft.com/en-us/windows/uwp/design/devices/designing-for-tv#tv-safe-area // Can test this on actual Xbox via: // Settings > Launch Settings > General > TV & Display Options > Resolution > 720p. int y = 27; foreach (var toast in toasts) { spriteBatch.DrawString(Textures.DebugFont, toast, new Vector2(48, y), Color.Teal); y += 30; } } [Conditional("DEBUG")] public static void Draw(GraphicsDevice graphics, BasicEffect lightingEffect) { if (!Enabled) { return; } if (lineIdx == 0) { return; } graphics.SetVertexBuffer(vertexBuffer); vertexBuffer.SetData(lineVertices); foreach (EffectPass pass in lightingEffect.CurrentTechnique.Passes) { pass.Apply(); graphics.DrawPrimitives(PrimitiveType.LineList, 0, lineIdx / 2); } } } }