Introduction
The MMDebugMenu is a runtime debug menu, designed to work on desktop and mobile, that you can drop in any scene to tweak values, trigger cheats, and read logs while your game runs, including in builds on device. It slides in from the edge of the screen, and is organized in tabs, filled with buttons, checkboxes, sliders, choices, values and texts. It also comes with a log tab, that displays your console logs, and a command line.
You’ll find it in MMTools/Accessories/MMDebugMenu, and a demo of it in action in MMTools/Demos/MMDebugMenu. It requires the Unity UI (uGUI) package.
Setup
- Drag the MMDebugMenu prefab (in MMDebugMenu/Prefabs) into your scene. It’s a canvas, so if you don’t already have an EventSystem in your scene, add one.
- Create a MMDebugMenuData asset (right click in your project, Create > MoreMountains > MMDebugMenu > MMDebugMenuData), or duplicate the one in the demo folder.
- Drag that asset into the Data slot of the MMDebugMenu.
- Press play, and press the toggle key to open and close the menu. By default it’s the backquote key if you’re using the Input System package, and the quote key if you’re using the legacy input manager. You can change both in the data asset.
From code, you can also call OpenMenu(), CloseMenu() or ToggleMenu() on the MMDebugMenu component, for example from a button of your own on mobile. Its OnOpenEvent and OnCloseEvent UnityEvents let you react when it opens or closes (to pause your game, for example).
Defining the menu’s contents
Everything about the menu is defined in its MMDebugMenuData asset:
- the Tabs list: each tab has a name, and a list of items
- whether or not to display the debug (log) tab, and its name
- the toggle direction, duration and curve, the toggle key (for both the old and new input systems)
- fonts and colors (background, accent, text)
- the prefabs used to create each item, in case you want to customize their look
Each item in a tab has a Type:
- Title: a header, to separate sections
- Spacer: small or big empty space
- Button: a button that triggers a button event with the specified name when pressed
- Checkbox: a toggle, with an initial state, that triggers a checkbox event with its new value when changed
- Slider: a float or int slider, with remapped min and max values and an initial value, that triggers a slider event with its value when it moves
- Choices: two or three mutually exclusive buttons, each triggering its own button event
- Value: a label and a value, updated by a MMRadioReceiver on the specified channel, to display any value from your game
- Text: a tiny, small or long text block
Reacting to the menu
When the user interacts with a button, checkbox or slider, the menu broadcasts an event carrying the name you’ve set for that item. There are two ways to react to these.
Without code
Add a MMDebugMenuButtonEventListener, MMDebugMenuCheckboxEventListener or MMDebugMenuSliderEventListener component to any object, set the event name to match the one of your item, and bind what you want to its UnityEvents. The checkbox listener has separate events for any change, true, and false. The slider listener’s event passes the slider’s value.
Via code
Register to the corresponding event struct, and filter on the name and mode:
using MoreMountains.Tools;
using UnityEngine;
public class DebugCheats : MonoBehaviour
{
public Health PlayerHealth;
protected virtual void OnButtonEvent(string buttonEventName, bool active, MMDebugMenuButtonEvent.EventModes eventMode)
{
if ((eventMode == MMDebugMenuButtonEvent.EventModes.FromButton) && (buttonEventName == "FullHealth"))
{
PlayerHealth.ResetHealth();
}
}
protected virtual void OnSliderEvent(string sliderEventName, float value, MMDebugMenuSliderEvent.EventModes eventMode)
{
if ((eventMode == MMDebugMenuSliderEvent.EventModes.FromSlider) && (sliderEventName == "Timescale"))
{
Time.timeScale = value;
}
}
protected virtual void OnEnable()
{
MMDebugMenuButtonEvent.Register(OnButtonEvent);
MMDebugMenuSliderEvent.Register(OnSliderEvent);
}
protected virtual void OnDisable()
{
MMDebugMenuButtonEvent.Unregister(OnButtonEvent);
MMDebugMenuSliderEvent.Unregister(OnSliderEvent);
}
}
These events work both ways: the menu sends them in From modes (FromButton, FromCheckbox, FromSlider), and you can send them yourself in Set modes to update the menu’s items from your game, for example to have a checkbox reflect a state that changed elsewhere:
MMDebugMenuCheckboxEvent.Trigger("GodMode", true, MMDebugMenuCheckboxEvent.EventModes.SetCheckbox);
The log tab
When the debug tab is enabled, it displays everything that goes through Unity’s console (Debug.Log, warnings, errors), which makes it very handy to check what’s happening in a build on device. Note that you can also display values directly on screen, outside of the menu, via MMDebug.DebugOnScreen("Label", value).
Command line
At the bottom of the log tab, you’ll find a command prompt. Type a command and press enter to run it. Commands come in two flavors: without arguments (now), or with arguments separated by spaces (timescale 0.5). A number of commands are included:
- help: lists all available commands
- now: outputs Time.time
- clear: clears the console
- restart / reload: restarts or reloads the current scene
- sysinfo: outputs system information
- quit / exit: quits the application
- vsync X: sets the vsync count
- framerate X: sets the target framerate
- timescale X: sets the timescale
- biggest X Y: outputs the biggest of the two specified integers (mostly there as an example of a command with multiple arguments)
Adding your own commands
Add the [MMDebugLogCommand] attribute to any static method, in any class, and it becomes a command. Its name is the method’s name. The first letter of what the user types gets capitalized before looking for a matching method, so a method named Coins can be typed as coins. For commands with arguments, the method takes a string array, in which the first entry is the command itself, and the following ones are the arguments. You can add a [MMDebugLogCommandArgumentCount(X)] attribute to make sure at least X arguments are passed.
using MoreMountains.Tools;
using UnityEngine;
public static class MyDebugCommands
{
[MMDebugLogCommand]
public static void Coins()
{
MMDebug.DebugLogTime("You have " + GameManager.Instance.Coins + " coins");
}
[MMDebugLogCommand]
[MMDebugLogCommandArgumentCount(1)]
public static void Setcoins(string[] args)
{
if (int.TryParse(args[1], out int amount))
{
GameManager.Instance.Coins = amount;
MMDebug.DebugLogTime("Coins set to " + amount);
}
}
}
Typing coins will display your coins, and setcoins 500 will set them to 500. Note that only the first letter gets capitalized, so a method named SetCoins would have to be typed exactly as SetCoins. You can also declare two versions of the same command, one without parameters and one taking a string array, and the right one will be called depending on whether arguments were typed.