De-clutter Your Debug Log: Making a Debug Panel in Unity [DRAFT]

Jonathan So
4 min readAug 5, 2021

--

GIF of the Debug Panel in action. Look at how smoothly the Timer is being updated!
GIF of the Debug Panel in action. Look at how smoothly the Timer is being updated!

The Debug.Log function: it’s that close friend who lets you know exactly what you want to know whenever you ask it. I use it for logging out pieces of data, when a function is called, a little reminder that I need to finish up a specific function… and in the early stages of developing a game, that’s OK. Once I moved forward on more complex gameplay mechanics, the debug messages piled up, and I realized that maybe I relied on Debug.Log a little too much; it stopped being helpful.

In the mid stages of working on Proton Power Pinball, my debug console became messy and hard to read. When a game moves out of that minimum viable product phase, there are a lot of moving parts to maintain and keep an eye on; every layer of complexity makes it harder to find and fix bugs. For me, there’s the dot-matrix display, what targets were most recently hit, the current game state, how long the ball has been in play, the list goes on. And when these different components start using the Debug.Log function, my debugging console began to look like this:

Too many Debug.Log statements cause clutter in the console.
Too many Debug.Log statements cause clutter in the console.

The console became difficult to parse, and I can only keep track of so many GameObjects in the inspectors (and I use two inspectors at once). I needed something to help me watch multiple variables from different objects in real-time. To make developing the game easier, I decided to make myself a Debug Panel.

Starting off, I created a blank GameObject in my scene, named “Debug Printer”, adding a new script called “DebugPrinter.cs” onto it. In the Canvas, I added a new Panel anchored to the top-left of the screen along with a Text object as its child — these two objects are where we’ll print our data. Time to get to work on the actual script.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // Import this to display/manipulate text
public class DebugPrinter : MonoBehaviour { // SINGLETON
public static DebugPrinter instance;
// PUBLIC
public bool debugOn;
public GameObject debugPanel;
public Text debugText;

First things first, this is going to involve changing UI elements (namely, the text), so I imported the UI Package at the top. I knew this was going to be a Singleton object, and made three public variables to control whether or not the debug panel was going to be seen, followed by the actual panel and text.

/* Builds a string and sets the text UI element to it. Meant to fire off constantly. */ 
private void UpdateText() {
string debug = “”;
// Build your debug string here, printing out variables.
debugText.text = debug;
}

The meat and potatoes of this script is the UpdateText() function — it builds a big string made up of multiple variables’ values (using their getter functions) and then sets our debugText’s value to that string. Honestly, it took more time creating all of the getter functions among all the manager objects than it did writing the function. All that’s left to do is set it in motion.

In the Start() function, if the bool debugOn is false, then I hide the panel from view. If it’s true, then I display the panel and use InvokeRepeating() to call UpdateText() at a constant interval (I chose 1/30 seconds).

Back in the Unity Editor, once I dragged the Canvas’ panel and text into their respective public variables on the Debug Printer object, it worked like a charm. I continued to add other pieces of data as well as a helper function, PrintList(), to print out the two List objects I use to store the game’s modes.

We can now look at the information from three manager objects, all without taking our eyes off the game.
We can now look at the information from three manager objects, all without taking our eyes off the game.

Seeing all the variables across multiple different managers in my game let me catch bugs almost immediately, like how the in-game timer was resetting when I didn’t want it to, or that the amount of balls in the bucket was off. The Debug.Log() function is now free to log important messages without clutter. If I wanted the same results without this panel, I’d have to have at least three inspectors locked to different managers, and I’d only be able to see the variables marked as public or [SerializeField].

Now that we have our helpful Debug Panel, we’ve taken a lot of the burden off of Debug.Log, and we’re able to actually debug our program much more efficiently. Since this has saved me both time and stress, I’d advise readers to do this earlier in development (like, for example, when you pass the minimum viable product milestone). Then, we can all go back to making Debug.Log print out things like “IMPLEMENT UPGRADE() FUNCTION NOW” every frame.

--

--