[Unity] QuickDebugDrawer

While working on a project in Unity, I catch myself needed show some debug info right into the game view, but I tired to write something like this everytime:

GUI.Label(new Rect(10, 10, 200, 20), "Some label #1");
GUI.Label(new Rect(10, 20, 200, 20), "Some label #2");

So, I create a simple formatter, which is doing just that under the hood. It is also support several useful features, such as hideable panels/headers, warning/error colors, and declarative packed info arrays. Here it is:

Simple usage

Let’s make some test for this. Here is simple usage example:

public class QuickDebugTest : MonoBehaviour
{
    //screen pos x,y: 50,50
    IDebugDrawer dbg = new QuickDebugDrawer() { sx = 50f, sy = 50f };
    int myTestValue = 12;

    void OnGUI()
    {
        //Prepare frame
        dbg.Prepare();

        //hideable panel: name, width
        dbg.StartPanel("New Panel", 200f);

        //Show pair: name, value
        dbg.Pair("Test value", myTestValue.ToString());
        dbg.Separator();

        //header and labels
        dbg.Header("Some header");
        dbg.Label("Just single Label");
        dbg.Label("!Warning label");
        dbg.Label("!!Error label");
    }
}

This will give us the following result:

Note that clicking on “Panel #1”, “Panel #2” and “Some header” will toggle their content visibility. Chars “!” and “!!” before value string marks them as warning/error. So you can use these methods:

//starts new panel and set it as current panel
public void StartPanel(string name, int width); 

//starts new header and set it as current header
public void Header(string name);

//show text-value pair
public void Pair(string text, string value);

//show simple label
public void Label(string text);

//make indentation
public void Separator();

//returns true if panel/header with
//specified name is hiden (collapsed) now
public bool isCollapsed(string name);

//show packed info block
public void ShowInfo(params object[] pack);

Show block at once

The example above is not bad, but still requires to write a lot of unnecessarily code. .ShowInfo() gives you possibility to send an entire block/package, in more-declarative way. So, exactly same example as above will look like this:

public class QuickDebugTest : MonoBehaviour
{
    IDebugDrawer dbg = new QuickDebugDrawer() { sx = 50f, sy = 50f };
    int myTestValue = 12;

    void OnGUI()
    {
        dbg.Prepare();
        dbg.ShowInfo(
        "@@Panel #1 |200", //width = 200, optional
            "Test value", myTestValue,
            "---", //separator

            "**Some header",
                "##Just single Label",
                "##!Warning label",
                "##!!Error label",

        "@@Panel #2",
            "Date/Time", DateTime.Now
        );
    }
}

Pretty short, same result. Please note, that this method also relieves you from using .ToString() everytime, so you can specify just values. As you can see it uses first two characters in string to determine purpose:

@@ start new panel
** start header
## single label
--- separator
without any: pair text-value in order

So, here is another example:

public class QuickDebugTest : MonoBehaviour
{
    IDebugDrawer dbg = new QuickDebugDrawer() { sx = 50f, sy = 50f };

    void OnGUI()
    {
        dbg.Prepare();
        dbg.ShowInfo(
        "@@System Info|400", //width = 400

            "**Device",
                "Type", SystemInfo.deviceType,
                "Model", SystemInfo.deviceModel,

            "**CPU",
                "Type", SystemInfo.processorType,
                "Count/threads", SystemInfo.processorCount,
                "Frequency", SystemInfo.processorFrequency,

            "**GPU",
                "Name", SystemInfo.graphicsDeviceName,
                "Version", SystemInfo.graphicsDeviceVersion,
                "Type", SystemInfo.graphicsDeviceType,
                "---", //separator
                "Memory size", SystemInfo.graphicsMemorySize,
                "Shader level", SystemInfo.graphicsShaderLevel,
                "ID:", SystemInfo.graphicsDeviceID,

        "@@System support",

            "**Render targets",
                "Max targets", SystemInfo.supportedRenderTargetCount,
                "Render 3D textures", SystemInfo.supports3DRenderTextures,
                "Render to cubemap", SystemInfo.supportsRenderToCubemap,
                "Render HDR textures", SystemInfo.SupportsRenderTextureFormat(
                        RenderTextureFormat.ARGBHalf),
                "Image effects", SystemInfo.supportsImageEffects,

            "**Advanced",
                "Compute shaders", SystemInfo.supportsComputeShaders,
                "Instanced rendering", SystemInfo.supportsInstancing,
                "Motion vectors", SystemInfo.supportsMotionVectors,
                "2D array textures", SystemInfo.supports2DArrayTextures,
                "3D textures", SystemInfo.supports3DTextures,
                "Cubemap arrays", SystemInfo.supportsCubemapArrayTextures
        );
    }
}

Result:

Use it as shared service

Most of the time you want to use it as shared service for your instances. So you find a GlobalDebugDrawer.cs in package, which is just a wrapper for QuickDebugDrawer. It’s like a singleton, but must be placed in the scene manually. It automatically prepare every frame for you, and just invoke onDebugInfo event. So example usage for this would be something like this:

public class GlobalDebugDrawerTest : MonoBehaviour
{
    void OnEnable() 
    {
        GlobalDebugDrawer.onDebugGUI += ShowDebug;
    }

    void OnDisable()
    {
        GlobalDebugDrawer.onDebugGUI -= ShowDebug;
    }

    void ShowDebug(IDebugDrawer dbg)
    {
        dbg.ShowInfo(
            "@@My own panel",
            	"##My values..."
        );
    }
}

Conditions

How about conditional strings? Using direct calls we can write just:

if (someCondition)
    dbg.Label("Some label");

But how can we make such with ShowInfo() ?

Here is ShowIf(bool condition, params object[] pack) method exactly for this purpose. Usage example:

public class ConditionalTest : MonoBehaviour
{
    InventoryContainer _inventory;
    bool inventoryValid {get {return _inventory != null && _inventory.isActive;}}

    void OnEnable()
    {
        GlobalDebugDrawer.onDebugGUI += ShowDebug;
    }

    void OnDisable()
    {
        GlobalDebugDrawer.onDebugGUI -= ShowDebug;
    }

    void ShowDebug(IDebugDrawer dbg)
    {
        dbg.ShowInfo(
            "@@Player Character",

                "**Inventory", //header
                dbg.ShowIf(inventoryValid,
                    //all these pairs will be shown only if inventoryValid
                    "Backpack name", _inventory.backpackName,
                    "Weight", _inventory.currentWeight,
                    "Items Count", _inventory.itemsCount,
                    "Speed affect", _inventory.speedAffect
                ),
            
                "##My other values..."
        );
    }
}

Here is how I use QuickDebugDrawer on practice:

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *