Skip to content

Writing output to the console

Cronyx edited this page Mar 1, 2021 · 11 revisions

Overview

When using the DeveloperConsole package, you will at some point want to write output to the console. The DeveloperConsole API provides support for basic log, warning, and error messages, as well as support for outputting custom images and media through the DeveloperConsole.AppendEntry method, described below. In addition, any output from the Unity console will be logged to the in-game developer console by default, unless you tell it not to inside the console settings.

Text Output

Three methods are provided to log text to the console:

public static void DeveloperConsole.Log(object message);
public static void DeveloperConsole.LogWarning(object message);
public static void DeveloperConsole.LogError(object message);

The string form (i.e. message.ToString()) of the given message will be taken and logged to the console in a white color (Log), amber color (LogWarning), or red color (LogError). These colors can be customized in the console settings. Additionally, variants of these functions are provided for formatted strings:

public static void DeveloperConsole.LogFormat(string format, params object[] args);
public static void DeveloperConsole.LogWarningFormat(string format, params object[] args);
public static void DeveloperConsole.LogErrorFormat(string format, params object[] args);

and can be used like so:

using Cronyx.Console;

DeveloperConsole.LogFormat("{0} {1}!", "Hello", "World");

// Prints "Hello World!" to the console.

Custom Output

Virtually any user-created output, such as an image, video, or color picker, can be logged to the console by creating a custom ConsoleEntry class for that ouptut. ConsoleEntry is an abstract MonoBehaviour that defines an arbitrary entry to the console.

Creating your own ConsoleEntry and logging it to the console is a three-step process:

  1. Create a class extending ConsoleEntry that optionally overrides one or more of the virtual methods described below.
  2. Create a prefab whose root GameObject has your ConsoleEntry component attached.
  3. Pass this prefab to DeveloperConsole.AppendEntry to instantiate it and return an instance of your ConsoleEntry component.

The rest of this article will demonstrate this procedure by creating a ConsoleEntry that takes and logs a screenshot to the console.

Extending ConsoleEntry

In order to create a ConsoleEntry that logs a screenshot to the console, we will override several of the virtual methods provided by ConsoleEntry to take and display a screenshot. Refer to the following source code:

public class ScreenshotEntry : ConsoleEntry
{
    [SerializeField]
    private RawImage Image; // A raw image that will store the screenshot texture
    
    [SerializeField]
    private TextMeshProUGUI Caption; // A text component that will show a caption for the screenshot

    private Texture2D mTexture; // A texture object to store the screenshot data (so we can delete it later)

    // Use the Configure method to update this component so that it conforms to the console's visual settings,
    // such as the font, font color, or font size
    public override void Configure(ViewSettings settings)
    {
        Caption.font = settings.Font;
        Caption.fontSize = settings.FontSize;
        Caption.color = settings.FontColor;

        // Make caption's height equal to the font height
        Caption.rectTransform.sizeDelta = new Vector2(Caption.rectTransform.sizeDelta.x, settings.FontSize);
    }

    // Use the OnCreated method to handle any initialization code.
    // This method is guaranteed to be called after Configure.
    public override void OnCreated()
    {
    }

    // OnRemoved is called just before this component is destroyed.
    // Use it to perform any cleanup, release any resources, etc.
    public override void OnRemoved()
    {
        // Destroy the texture object to free up memory
        if (mTexture != null) Destroy(mTexture);
    }

    public void SetData (Texture2D texture, string caption)
    {
        mTexture = texture;
        Image.texture = mTexture;
        Caption.text = caption;
    }
}

Notice that in the Configure method, we use the passed ViewSettings object to adjust our console entries appearance so that it conforms to that of the console. ViewSettings contains data about the default console font, font size, font color, etc. OnCreated can be used for any initialization code, and similarly, OnRemoved can be used for any destruction code.

We do not take the screenshot directly inside this ConsoleEntry (for instance, inside its OnCreated method) as this would cause the console entry itself to appear in the screenshot. Rather, we let the creator of this console entry set the screenshot manually using the SetData method.

Creating a console entry prefab

Next, we must create a prefab that will house the above ScreenshotEntry component. There are a few stipulations/recommendations concerning the prefab that is used for console entries:

  1. The prefab's root GameObject must have your ConsoleEntry implementation attached as a component.
  2. The prefab's root GameObject must have a RectTransform (so that it can be used inside the UI).
  3. The prefab's root GameObject should have a ContentSizeFitter component attached so that your entry only uses as much space as it needs.
  4. The prefab's root RectTransform should have an x-pivot equal to zero (so that it is left-justified inside the console).

For our ScreenshotEntry component, we will use the following hierarchy. ScreenshotEntry is the root GameObject for our console entry, RawImage will hold our screenshot image, and finally Caption will hold a TextMeshPro text component that displays a caption beneath the image.

Let's start designing the Prefab:

In the editor, the finished prefab looks like this:

The white rectangle represents where the screenshot will appear, and the text beneath it holds the caption.

Finally, we will save the prefab to our resources folder so we can easily access it at runtime in our code:

Creating and using your console entry

To create and use your console entry, call the following function:

public static T DeveloperConsole.AppendEntry<T>(GameObject prefab) where T : ConsoleEntry;

The AppendEntry function will instantiate your prefab, add it to the UI, and return an instance of your ConsoleEntry implementation, T.

In the case of our screenshot entry, we will write a console command using the IConsoleCommand pattern (see here for more information) that waits until the end of the frame to fetch a screenshot and displays it:

[Command("screenshot")]
public class ScreenshotCommand : MonoBehaviour, IConsoleCommand
{
    public string Help => "Takes and displays a screenshot";

    private Coroutine mCoroutine;

    public void Invoke(string data)
    {
        if (mCoroutine != null) StopCoroutine(mCoroutine);
        mCoroutine = StartCoroutine(ShowScreenshot());
    }

    // A coroutine that will take and show the screenshot
    private IEnumerator ShowScreenshot()
    {
        yield return new WaitForEndOfFrame();

        // Take a screenshot (in the form of a Texture2D)
        Texture2D texture = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
        texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
        texture.Apply();

        // Get a caption for the image containing the date
        var now = DateTime.Now;
        string caption = $"Taken on {now.ToString("dddd, dd MMMM yyyy")} at {now.ToString("HH:mm:ss")}";

        // Create ScreenShot entry
        var entry = DeveloperConsole.AppendEntry<ScreenshotEntry>(Resources.Load<GameObject>("ScreenshotEntry"));
        entry.SetData(texture, caption);
    }
}

Notice how we created an entry using DeveloperConsole.AppendEntry, and passed a prefab loaded from resources using Resources.Load.

We are now able to call the screenshot command to take and display a screenshot, like so:

A similar process can be followed to add any other custom content to the console.