S.W.A.P.

The S.W.A.P. Debug Console

 

When we were developing the original Prototype for S.W.A.P. we ran into A LOT of problem, by far the worst of which was encountered on the afternoon the day before our deadline. With more then two players in the game there would be ‘ownership’ issues after a players swaps, i.e. two players would control the same avatar. In the end this bug forced us to rely on a video when pitching the game, instead of a live demo.

One reason why the bug caused such a problem was because we had no way to get debug information from the unity player. We needed a console, one where we could enter commands and receive feedback from the game and we didn’t have one.

Now the Unity player does provide a console, however it is just a striped down version of the console in the editor and is only available in debug builds. It will print errors to the screen such as exceptions or messages logged with Debug.LogError() in our scripts, but not much else. This means we would need to implement our own (with a budget of $0, buying one on the asset store wasn’t an option).

After googling around a little I found this post on the BitFlip games blog. Tip 3 contained the full source code for a debug console and best of all it was public domain. It provided all the features we needed, Auto Complete, Command Recall, used Delegates as commands, Command parameters, etc. So I quickly included it in the build and wrote some simple commands to start us off. It was when I added these initial commands that I came across the only real problem I had with this console, the commands need to be registered manually by added them in the ConsoleCommands class (at line 228 in the original). I thought this could be improved using c# reflection, this post is about how I went about it.

The Command Attribute

To start with I needed a way to distinguish between a normal method and one which I wanted to use as a console command. The simplest way to achieve this was to use a C# attribute. My attribute would need to do three things:

  • Mark which methods should be used as console commands
  • Specify the command text
  • provide the help text (if any).

It ended up looking like this:

// This Attribute is used to mark a method as a command for the console!!
[AttributeUsage(AttributeTargets.Method)]
public class WakyCommandAttribute : Attribute
{
	// The command entered on the console to run this method as a command.
	private string command;
	public string Command
	{
		get { return command; }
		set { command = value; }
	}

	// The help text for the command - optional
	private string helpText = "No help provided, sorry :(";
	public string HelpText
	{
		get { return helpText; }
		set { helpText = value; }
	}

	public WakyCommandAttribute(string szCommand)
	{
		command = szCommand;
	}
}

There’s not much to it really. Two properties, one for the command text and a second for the help text. There is also a constructor that takes in a string for the command text, this makes the command text compulsory while leaving the help text as optional.

To use the attribute you can just prepend a method with it like follows:

[WakyCommandAttribute("Help", HelpText = "Prints the Debug Console help text")]
public static void GetHelp(string[] Params)
	{
		// do something here!
	}

Not hard at all.

Finding Commands at Runtime

Now that we have the commands marked the next challenge is to find them at runtime, this is where C# reflection comes in.

To find the console commands I added the following function to the ConsoleCommands class:

// finds any marked commands in the current assembly
// and adds them to the commands list!
static public void FindCommands()
{
	WakyCommandAttribute wakyCommandAttrib;
	Assembly currAssembly = Assembly.GetCallingAssembly();

	foreach(Type type in currAssembly.GetTypes())
	{
        // check the methods in every class:
		if (type.IsClass)
		{
			foreach (MethodInfo method in type.GetMethods())
			{
                    	// if the method meets our requirments for a console command (i.e. is buplic and static).
				if (method.IsPublic && method.IsStatic)
				{
                        	// check its attributes:
					foreach (Attribute attr in method.GetCustomAttributes(true))
			            	{
						wakyCommandAttrib = attr as WakyCommandAttribute;

						if (wakyCommandAttrib != null)
						{
                                		// create and the command based on its attribute data.
							ConsoleCommand command = new ConsoleCommand(wakyCommandAttrib.Command, wakyCommandAttrib.HelpText, Delegate.CreateDelegate(typeof(Command), method) as Command);

							if (command != null)
							{
								if (Commands.Contains(command) == false)
								{
									Commands.Add(command);
								}
							}
						}
					} // end attrib loop.
				}
			} // end method loop
		}
	} // end Class loop.
}

We start by getting the current assembly, this works because the debug console, along with all your other Unity code gets compiled into a single assembly (or DLL) by Unity. Once we have the assembly we can then go through all its types looking for classes, as only classes have methods. once we find a class we check each of its public/static methods to see if it has the console command attribute specified. If it has the attribute then we can add it to the list of console commands.

There are a couple of other changes necessary to get this to work. The first is to change the array of console commands in the ConsoleCommand class to a list, like this:

// from this:
 public static ConsoleCommand[] Commands = new ConsoleCommand[]
{
    // Fill this with your commands ...
};

// to this:
public static List<ConsoleCommand> Commands = new List<ConsoleCommand>();

Note that you will need to update the rest of the Debug Console to use this list instead of an array. The second change is to add a constructor to the DebugConsole class, Like this:

// Constructor will automatically add any commands to the command List:
public DebugConsole()
{
	ConsoleCommands.FindCommands();
}

The constructor will automatically fill up the list of console commands by calling the FindCommands() function whenever we create an instance of the DebugConsole class. The combined effect of these changes is to cause the Debug console to automatically populate itself on creation with any methods tagged with our custom attribute, no manual adding of commands required!

Where can I get it?

There are a few other minor tweaks in my version of this console, things like how it is displayed and how you access it, nothing major. You can find the whole thing here. To use it just add the DebugManager to an empty game object in the scene, nothing else too it.

If you want to know more about S.W.A.P. then you should visit the official website.

One thought on “The S.W.A.P. Debug Console”

Leave a comment