Last active
February 3, 2016 10:27
-
-
Save SnopyDogy/8109058 to your computer and use it in GitHub Desktop.
A debug console for use in Unity games.Features include Command History, Auto-Complete and the use of Attributes to specify Commands.Based on the debug console found here: http://bitflipgames.com/2010/09/16/tips-for-working-with-unity-4-coding-and-general-tips/ For details see here: http://blog.gvnott.com/2013/12/24/the-s-w-a-p-debug-console/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -------------------------------------------------------------------------------- | |
// Copyright (C)2010 BitFlip Games | |
// Written by Guy Somberg guy@bitflipgames.com | |
// | |
// I, the copyright holder of this work, hereby release it into the public domain. | |
// This applies worldwide. In case this is not legally possible, I grant any | |
// entity the right to use this work for any purpose, without any conditions, | |
// unless such conditions are required by law. | |
// -------------------------------------------------------------------------------- | |
// Copyright (C)2013 Chaos Theory Games | |
// Modified by Greg Nott gvnott @ gmail . com | |
// For info on modifications: http://blog.gvnott.com/2013/12/23/the-s-w-a-p-debug-console/ | |
// -------------------------------------------------------------------------------- | |
using UnityEngine; | |
using System; | |
using System.Collections.Generic; | |
using System.Reflection; | |
#region Debug Console Class | |
public class DebugConsole | |
{ | |
private string ConsoleText = ""; | |
private bool displayConsole = false; | |
public bool DisplayConsole | |
{ | |
get { return displayConsole; } | |
set | |
{ | |
displayConsole = value; | |
if (!DisplayConsole) | |
{ | |
ConsoleText = ""; | |
PreviousCommandIndex = -1; | |
} | |
} | |
} | |
private List<string> PreviousCommands = new List<string>(); | |
private int PreviousCommandIndex = -1; | |
private Queue<string> History = new Queue<string>(); | |
private string AutoCompleteBase = ""; | |
private List<string> AutoCompleteOptions = new List<string>(); | |
private int AutoCompleteOptionsIndex = -1; | |
private Vector2 scrollPosition = new Vector2(0, 0); | |
private float scrollHeight = 0.0f; | |
// Constructor will automatically add any commands to the command List: | |
public DebugConsole() | |
{ | |
ConsoleCommands.FindCommands(); | |
} | |
private ConsoleCommands.ConsoleCommand GetCommand(string CommandText) | |
{ | |
foreach (ConsoleCommands.ConsoleCommand Command in ConsoleCommands.Commands) | |
{ | |
if (Command.CommandText.Equals(CommandText, StringComparison.CurrentCultureIgnoreCase)) | |
{ | |
return Command; | |
} | |
} | |
return null; | |
} | |
private void ExecuteCommand(string CommandText) | |
{ | |
// safty checks: | |
if (CommandText.Length == 0) | |
{ | |
return; | |
} | |
CommandText = CommandText.Trim(); | |
PreviousCommands.Add(CommandText); | |
History.Enqueue(CommandText); | |
string[] SplitCommandText = CommandText.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries); | |
ConsoleCommands.ConsoleCommand Command = GetCommand(SplitCommandText[0]); | |
if (Command != null) | |
{ | |
Command.Callback(SplitCommandText); | |
} | |
else // if command is invlaid, tell user and print help! | |
{ | |
Log("Invalid Command!"); | |
ConsoleCommands.GetHelp(null); | |
} | |
} | |
private void AutoComplete() | |
{ | |
string AutoCompleteText = AutoCompleteBase.Trim().ToLower(); | |
if (AutoCompleteOptionsIndex < 0) | |
{ | |
AutoCompleteOptions.Clear(); | |
foreach (ConsoleCommands.ConsoleCommand Command in ConsoleCommands.Commands) | |
{ | |
if (Command.CommandText.ToLower().StartsWith(AutoCompleteText)) | |
{ | |
AutoCompleteOptions.Add(Command.CommandText); | |
} | |
} | |
AutoCompleteOptions.Sort(); | |
if (AutoCompleteOptions.Count > 0) | |
{ | |
AutoCompleteOptionsIndex = 0; | |
PreviousCommandIndex = -1; | |
} | |
} | |
else | |
{ | |
if (AutoCompleteOptions.Count > 0) | |
{ | |
AutoCompleteOptionsIndex = (AutoCompleteOptionsIndex + 1) % AutoCompleteOptions.Count; | |
} | |
else | |
{ | |
AutoCompleteOptionsIndex = -1; | |
} | |
} | |
if (AutoCompleteOptionsIndex >= 0) | |
{ | |
ConsoleText = AutoCompleteOptions[AutoCompleteOptionsIndex]; | |
} | |
} | |
private void ClearAutoComplete() | |
{ | |
AutoCompleteBase = ""; | |
AutoCompleteOptions.Clear(); | |
AutoCompleteOptionsIndex = -1; | |
} | |
public void OnGUI() | |
{ | |
if (DisplayConsole) | |
{ | |
GUI.depth = 1000; | |
string BaseText = ConsoleText; | |
if (PreviousCommandIndex >= 0) | |
{ | |
BaseText = PreviousCommands[PreviousCommandIndex]; | |
} | |
Event CurrentEvent = Event.current; | |
if ((CurrentEvent.isKey) && | |
(!CurrentEvent.control) && | |
(!CurrentEvent.shift) && | |
(!CurrentEvent.alt)) | |
{ | |
bool isKeyDown = (CurrentEvent.type == EventType.KeyDown); | |
if (isKeyDown) | |
{ | |
if (CurrentEvent.keyCode == KeyCode.Return || CurrentEvent.keyCode == KeyCode.KeypadEnter) | |
{ | |
ExecuteCommand(BaseText); | |
//DisplayConsole = true; // set to false to have console disapear after pressing enter. | |
return; | |
} | |
if (CurrentEvent.keyCode == KeyCode.UpArrow) | |
{ | |
if (PreviousCommandIndex <= -1) | |
{ | |
PreviousCommandIndex = PreviousCommands.Count - 1; | |
ClearAutoComplete(); | |
} | |
else if(PreviousCommandIndex > 0) | |
{ | |
PreviousCommandIndex--; | |
ClearAutoComplete(); | |
} | |
return; | |
} | |
if (CurrentEvent.keyCode == KeyCode.DownArrow) | |
{ | |
if (PreviousCommandIndex == PreviousCommands.Count - 1) | |
{ | |
PreviousCommandIndex = -1; | |
ClearAutoComplete(); | |
} | |
else if (PreviousCommandIndex >= 0) | |
{ | |
PreviousCommandIndex++; | |
ClearAutoComplete(); | |
} | |
return; | |
} | |
if (CurrentEvent.keyCode == KeyCode.Tab) | |
{ | |
if (AutoCompleteBase.Length == 0) | |
{ | |
AutoCompleteBase = BaseText; | |
} | |
AutoComplete(); | |
return; | |
} | |
if (CurrentEvent.keyCode == KeyCode.End || CurrentEvent.keyCode == KeyCode.BackQuote) | |
{ | |
DisplayConsole = false; | |
return; | |
} | |
} | |
} | |
GUI.SetNextControlName("ConsoleOutputBox"); | |
string history = FormatHistoryText(); | |
//Rect outputFieldRect = new Rect(0.0f, (float)Screen.height - 100.0f, (float)Screen.width, 80.0f); | |
GUILayout.BeginArea (new Rect(0.0f, (float)Screen.height - 120.0f, (float)Screen.width, 100.0f)); | |
scrollPosition = GUILayout.BeginScrollView (scrollPosition, GUILayout.Width (Screen.width), GUILayout.Height (100)); | |
GUI.skin.box.wordWrap = true; // set the wordwrap on for box only. | |
GUI.skin.box.richText = true; | |
GUI.skin.box.alignment = TextAnchor.UpperLeft; | |
GUILayout.Box(history); // draw history text. | |
GUILayout.EndScrollView (); | |
GUILayout.EndArea(); | |
GUI.SetNextControlName("ConsoleTextBox"); | |
Rect inputFieldRect = new Rect(0.0f, (float)Screen.height - 20.0f, (float)Screen.width, 20.0f); | |
string CommandText = GUI.TextField(inputFieldRect, BaseText); | |
if (PreviousCommandIndex == -1) | |
{ | |
ConsoleText = CommandText; | |
} | |
if (CommandText != BaseText) | |
{ | |
ConsoleText = CommandText; | |
PreviousCommandIndex = -1; | |
ClearAutoComplete(); | |
} | |
GUI.FocusControl("ConsoleTextBox"); | |
} | |
} | |
private string FormatHistoryText() | |
{ | |
float prevScrollHeight = scrollHeight; | |
scrollHeight = 0.0f; | |
string history = ""; | |
foreach(string str in History) | |
{ | |
history += str + "\n"; | |
} | |
int count = CountNewLines(history) - 4; // minus 4 because we do not need to count the first 5 lines! | |
if (count > 0) | |
{ | |
// if we are greater then 0 then count * 20.0f (size of line) | |
// takes us to the bottom of the debug console window. | |
scrollHeight = count * 20.0f; | |
if (count > 100) | |
{ | |
// We are too big, remove the oldst string: | |
History.Dequeue(); | |
} | |
} | |
if (scrollHeight < prevScrollHeight - 10.0f || scrollHeight > prevScrollHeight + 10.0f) | |
{ | |
// scroll height has changed, snap down to bottom of the history panel: | |
scrollPosition.y = scrollHeight; | |
} | |
return history; | |
} | |
private int CountNewLines(string str) | |
{ | |
int count = 0; | |
foreach (char c in str) | |
{ | |
if (c == '\n') | |
{ | |
++count; | |
} | |
} | |
return count; | |
} | |
public void Log(object message) | |
{ | |
History.Enqueue(message.ToString()); | |
} | |
public void LogWarning(object message) | |
{ | |
History.Enqueue("Warning: " + message.ToString()); | |
} | |
public void LogError(object message) | |
{ | |
History.Enqueue("Error: " + message.ToString()); | |
} | |
} | |
#endregion | |
#region Console Commands | |
public static class ConsoleCommands | |
{ | |
public delegate void Command(string[] Params); | |
public class ConsoleCommand | |
{ | |
public ConsoleCommand(string CommandText, string HelpText, Command Callback) | |
{ | |
this.CommandText = CommandText; | |
this.HelpText = HelpText; | |
this.Callback = Callback; | |
} | |
public string CommandText; | |
public string HelpText; | |
public Command Callback; | |
} | |
// 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. | |
} | |
public static List<ConsoleCommand> Commands = new List<ConsoleCommand>(); | |
// public static ConsoleCommand[] Commands = new ConsoleCommand[] | |
// { | |
// // Fill this with your commands: | |
// new ConsoleCommand("Help", "Prints the Debug Console help info!", GetHelp), | |
// new ConsoleCommand("Hide", "Hides the Debug Console", HideDevConsole), | |
// new ConsoleCommand("GetNetworkVersion", "Prints out the network version string.", NetworkController.GetNetworkVersion), | |
// new ConsoleCommand("GetNetInfo", "Prints out the network information.", NetworkController.GetNetInfo), | |
// new ConsoleCommand("ToggleWireframe", "Turns wireframe on/off.", WireframeOnOff.ToggleWireframe), | |
// new ConsoleCommand("test", "test", TestAttrib), | |
// }; | |
[WakyCommandAttribute("Help", HelpText = "Prints the Debug Console help text")] | |
public static void GetHelp(string[] Params) | |
{ | |
string helpText = "Waky Debug Console!!\n"; | |
helpText = helpText + "Press ~ to open and ~ or the 'End' key to close, press enter to enter command text, press tab to autocomplete command, press up/down arror to see command history.\n"; | |
helpText = helpText + "Commands: \n"; | |
foreach (ConsoleCommand command in Commands) | |
{ | |
helpText = helpText + command.CommandText + " -- " + command.HelpText + "\n"; | |
} | |
GameController.Log(helpText + "============================================================================="); | |
} | |
[WakyCommandAttribute("Hide", HelpText = "Hides the Debug Console")] | |
public static void HideDevConsole(string[] Params) | |
{ | |
GameController.Instance.DebugConsoleVisible(false); | |
} | |
} | |
// 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; | |
} | |
} | |
#endregion | |
#region Debug Manager | |
public class DebugManager : MonoBehaviour | |
{ | |
private DebugConsole debugConsole = null; | |
public bool ConsoleEnabled | |
{ | |
get { return (debugConsole != null) ? debugConsole.DisplayConsole : false; } | |
} | |
public bool ConsoleVisible | |
{ | |
get { return debugConsole.DisplayConsole; } | |
set { debugConsole.DisplayConsole = value; } | |
} | |
void Awake() | |
{ | |
debugConsole = new DebugConsole(); | |
} | |
void Update() | |
{ | |
if (debugConsole == null) | |
{ | |
return; // do nothing! | |
} | |
if (Input.GetKeyUp(KeyCode.BackQuote)) // tilde! | |
{ | |
debugConsole.DisplayConsole = true; | |
} | |
} | |
void OnGUI() | |
{ | |
if (debugConsole != null) | |
{ | |
debugConsole.OnGUI(); | |
} | |
} | |
public void Log(object message) | |
{ | |
if (debugConsole != null) | |
{ | |
debugConsole.Log(message); | |
} | |
} | |
public void LogWarning(object message) | |
{ | |
if (debugConsole != null) | |
{ | |
debugConsole.LogWarning(message); | |
} | |
} | |
public void LogError(object message) | |
{ | |
if (debugConsole != null) | |
{ | |
debugConsole.LogError(message); | |
} | |
} | |
} | |
#endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment