Anyone familiar with Unity should already be aware of how useful the editor’s inspector menu can be. It allows you to modify (almost) any data members of your object even while running the game in play mode. For a lot of people that’s probably more than enough power to do what they want. What happens though, if you are play testing and someone has managed to break something horrifically? Do you make them start over, or do you have an easy way to “fix” things quickly. What if you are running a “production” build and find yourself wanting to tweak something? Do you go back to the editor or are you able to modify variables right there? Those are a couple scenarios that having a more advanced debug menu can help with, even when using a powerful engine like Unity.
I originally made this menu for a different project entirely, one where we would have far less technical members of the team that may find themselves wanting to tweak gameplay parameters. These team members likely would not be familiar with Unity and more often than not would be working with actual builds of the game. In order to facilitate this work, many of the most important gameplay parameters were exposed through this debug menu, allowing them to make changes and save those changes to a log for someone else to look at later.

The above screenshot shows numerous parameters, as well as a couple special buttons. The bottom two buttons are always there. “Log All Values” will take every parameter exposed in the debug menu and print their current values to a log. In editor, this log will just be the console window, but in an executable build, the log will go to a file. “Refresh Debug Menu” is another necessary button. The debug menu is filled in at scene load time, but some objects may not exist at that time based on various factors. It is also the case that the debug menu instance may be a global object and persist between scenes. In both cases, the contents of the menu may not always be up to date, this button simply ensures you have a way to guarantee all the scene relevant data is correct.
The remaining fields and buttons are far more interesting. The top three rows are are float properties (though there’s no reason they couldn’t be integer based on this screenshot alone). The fourth row is a boolean property, and then the fifth and sixth rows are special buttons that trigger specific callbacks when clicked. The final thing that may have your attention is the button on the far right next to the first three properties. This is a “revert” button, when clicked, the value will revert back to what it was when the debug menu first loaded the property. This revert button was explicitly requested by someone when this menu was first created, so I’m fairly proud of that part in particular despite the actual implementation being fairly trivial.
Let’s get into the actual implementations for how some of this works. I won’t explain too deeply about C# Attributes, so if you want to learn more about that part in particular, I suggest reading about them on MSDN. I actually have two custom attributes in particular to discuss here. One is for properties, and the other is for methods (the buttons in the original screenshot).
[AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]
public class DebugMenuButton : Attribute
{
public string ButtonLabel { get; set; } = string.Empty;
public DebugMenuButton() { }
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple=false, Inherited=true)]
public class DebugMenuVariable : Attribute
{
public string DebugMenuName { get; set; } = string.Empty;
public DebugMenuVariable() { }
}
If you’re unfamiliar with C#, these classes may be a bit confusing, they don’t even do anything! Correct, attributes are purely for decoration in code. What makes this useful comes a bit later, but for now you can see that they at least have some internal properties for custom names which we make use of. The fun part comes next, actually adding properties and buttons to the debug menu is as simple as using these very basic classes as follows:
[DebugMenuVariable(DebugMenuName = "Run Speed")]
public float RunSpeed { get; set; } = 5.0f;
[DebugMenuVariable(DebugMenuName = "Jump Height")]
public float JumpHeight { get; set; } = 10.0f;
[DebugMenuVariable(DebugMenuName = "Air Speed")]
public float AirSpeed { get; set; } = 3.0f;
[DebugMenuVariable(DebugMenuName = "Can Double Jump")]
public bool CanDoubleJump { get; set; } = false;
[DebugMenuButton(ButtonLabel = "Apply powerup")]
public void ApplePowerupButton()
{
}
[DebugMenuButton(ButtonLabel = "Spawn enemy")]
public void SpawnEnemyButton()
{
}
Based on this code and the original screenshot, you should have a good idea of how this all maps together, but how does this all work? All we’ve done so far is make some custom decorators and thrown them onto properties and methods, we aren’t actually doing anything with them. This is where the fun part comes in: reflection.
Reflection provides a vast quantity of type information at runtime in C#. As long as we have a reference to an object, we can get pretty much anything out of it, methods, data members, properties, names, friends, mother’s maiden name, high school and even the street they grew up on. But the important parts we need are mostly in their attributes.
foreach (Component component in go.GetComponents(typeof(MonoBehaviour)))
{
// find all properties from component
var properties = from p in component.GetType().GetProperties()
let attr = p.GetCustomAttributes(typeof(DebugMenuVariable), true)
where attr.Length != 0
select new { Property = p, Attribute = attr.First() as DebugMenuVariable };
// add each property to debug menu
foreach (var propInfo in properties)
{
AddPropertyInfo(
propInfo.Property,
propInfo.Attribute,
component as MonoBehaviour
);
}
}
Filling out the debug menu is not a cheap operation, we need to look at every currently active GameObject, and then go a step further and look at very single MonoBehaviour attached to that GameObject. The end result will be worth it, and as long as this operation doesn’t happen every frame it’s fine, just be aware of exactly how much is going on here. The confusing part may be that first part of the for loop … what is all this weird syntax with “from,” “let,” etc. This is all from a standard C# library called Linq.
If you’re familiar with database systems like SQL, this should look very similar. What we are doing is treating our component like a database and looking up every data member that has been tagged with DebugMenuVariable. From there we save off the important information that we will need later; property reflection data (labeled Property in the select query), our DebugMenuVariable instance (containing attribute defined information like our custom labels), and a reference to the component whose data was tagged.
Now that we have all the information we need, it’s really just drawing it. For this particular menu I use the old Unity GUI implementation that is based on IMGUI. It’s fairly simple, straightforward, and does everything you really need from a debug menu. I’d never use these APIs for actual game UI, but for a debug menu? It does more than enough. There’s a lot going on in terms of actual layout and error handling, but the most interesting part is going to be how the user input is assigned back to the data source:
// This source of this function was written as a generic,
// BasicType is guaranteed to be something like int, float, etc.
// only try parsing new info if the gui has updated
if (GUI.changed)
{
// grab the TryParse method from BasicType in
// order to convert string to actual type
MethodInfo parseMethod =
typeof(BasicType).GetMethod(
"TryParse",
new [] {typeof(string), typeof(BasicType).MakeByRefType()}
);
// index 0 is the string for TryParse to handle
// index 1 will be filled in with actual data if TryParse succeeds
object[] parameters = new object[] {valueStr, null};
object result = parseMethod.Invoke(null, parameters);
// if TryParse succeeded, pass the result to the stored property
if ((bool)result)
{
reference.property.SetValue(
reference.componentReference,
parameters[1]
);
}
}
In order to handle this all as generically as possible, I use a C# generic method to be type agnostic across all integer and floating point types. The code would be a lot cleaner if I could use int.TryParse directly, etc, but where is the fun in that? Since this is all about reflection anyway, where’s the harm in using reflection to call TryParse on a type we don’t know. I feel that the comments describe fairly well how the TryParse reflection works, so I won’t go into more detail there. The final step is just one more bit of reflection; taking the parsed value from and using the PropertyInfo we saved all those steps ago to set the new value on our scene component.
It seems like a lot of work here, and of course there’s a lot of little details I skipped over with the overall menu itself, but this post was already getting long just talking about the real meat of this. I hope anyone reading this can extrapolate the rest with a little critical thinking and maybe do something similar themselves. I’ll likely continue to expand on this tool as I find myself wanting access to additional helpers at runtime, but for now I’m fairly pleased with how useful this has been across multiple projects.