Last active
July 4, 2018 14:20
-
-
Save tantalum7/ba617167351fbe9ce1b6dfa796955199 to your computer and use it in GitHub Desktop.
unity-quake-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
#region Imports | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
using UnityEngine.UI; | |
#endregion | |
// | |
// I got fed up trying to use other people's bloated/messy/buggy debug consoles, | |
// so I made my own. Simple, easy to use and less than 250 lines (including comments). | |
// | |
// 1. Make sure there is a child object underneath the script with a Unity.UI.Text | |
// component attached named "ConsoleText" | |
// 2. Find this script in unity GetComponent<DebugConsole>(), and add new commands | |
// Debug.Console.RegisterCommand(string name, Action<string> action, string help="") | |
// 3. Fire up the console with ` | |
// | |
namespace Ta7.UI | |
{ | |
public class DebugConsole: MonoBehaviour | |
{ | |
#region Fields | |
public bool isOpen { get { return _isOpen; } private set { OpenConsole(value); } } | |
private bool _isOpen = true; | |
private Text logText; | |
private Patterns.LimitedStack<string> logStack = new Patterns.LimitedStack<string>(10); | |
private string currentLine = ""; | |
private Dictionary<string, Command> commandDict = new Dictionary<string, Command>(); | |
private string prompt = "> "; | |
private KeyCode toggleKey = KeyCode.BackQuote; | |
private char toggleChar = '`'; | |
private bool autoCompleteMode = false; | |
private List<string> autoCompleteMatches = new List<string>(); | |
private int autoCompleteIndex = 0; | |
private Vector3 showPosition; | |
private int recallIndex = 0; | |
#endregion | |
/// <summary> | |
/// Small embedded container class to store command name, help string and action together | |
/// </summary> | |
class Command | |
{ | |
public string name; | |
public string help; | |
public Action<string> action; | |
public Command(string name, string help, Action<string> action) | |
{ | |
this.name = name.ToLower(); | |
this.help = help; | |
this.action = action; | |
} | |
} | |
public void Awake() | |
{ | |
// Add our handler to grab unity log messages | |
Application.logMessageReceived += HandleLog; | |
// Register the builtin commands | |
RegisterCommand("echo", x => WriteLine(x), "Echo back command"); | |
RegisterCommand("help", HelpCmd, "List all commands"); | |
RegisterCommand("help2", x => WriteLine("No help!"), "not grreat"); | |
RegisterCommand("warning", x => Debug.Log("oh noes!"), "stuf"); | |
// Grab the logText component | |
logText = transform.Find("ConsoleText").GetComponent<Text>(); | |
// Set the show position to where it is now, then hide it | |
showPosition = transform.position; | |
OpenConsole(false); | |
} | |
public void Update() | |
{ | |
// If toggleKey is pressed, toggle isOpen | |
if(Input.GetKeyUp(toggleKey)) { isOpen = !isOpen; } | |
// Check if console is open | |
if (isOpen) | |
{ | |
// Start autocomplete if tab is pressed | |
if (Input.GetKeyUp(KeyCode.Tab)) { StartAutoComplete(); } | |
else if (Input.GetKeyUp(KeyCode.DownArrow)) | |
{ | |
currentLine = logStack[recallIndex]; | |
recallIndex = ++recallIndex >= logStack.Count ? logStack.Count -1 : recallIndex; | |
} | |
else if (Input.GetKeyUp(KeyCode.UpArrow)) | |
{ | |
recallIndex = --recallIndex < 0 ? 0 : recallIndex; | |
currentLine = logStack[recallIndex]; | |
} | |
else | |
{ | |
// Iterate through each character in the new input string | |
foreach (char c in Input.inputString) | |
{ | |
// If backspace is pressed, remove the last char from currentLine | |
if (c == '\b') { currentLine = (currentLine.Length > 0) ? currentLine.Substring(0, currentLine.Length - 1) : ""; } | |
// If its the togglekey, do nothing | |
else if (c == toggleChar) { } | |
// If newline or linefeed is pressed, execute the currentLine buffer | |
else if (c == '\n' | c == '\r') { HandleLine(); } | |
// Any other characters | |
else | |
{ | |
// If autocomplete is enabled, end it | |
if (autoCompleteMode) { EndAutoComplete(); } | |
// Append char to currentline | |
currentLine += c; | |
} | |
} | |
} | |
// Update the screen contents | |
UpdateScreen(); | |
} | |
} | |
/// <summary> | |
/// Register command with the console at any time. When the action is invoked, it will be passed the line buffer | |
/// e.g. "mycommand myvars and junk" | |
/// </summary> | |
public void RegisterCommand(string name, Action<string> action, string help="") | |
{ | |
commandDict[name] = new Command(name: name, help: help, action: action); | |
} | |
/// <summary> | |
/// Unregister a command | |
/// </summary> | |
public void UnregisterCommand(string name) | |
{ | |
commandDict.Remove(name); | |
} | |
/// <summary> | |
/// Write to the console. Unity Debug.Log messages will also appear on console, but with "Unity: Log:" in front | |
/// </summary> | |
public void WriteLine(string line) | |
{ | |
logStack.Push(line); | |
} | |
#region Private | |
private void UpdateScreen() | |
{ | |
// Join all the logStack items into a single string, separated by newlines | |
string text = String.Join("\r\n", logStack.Reversed().ToList()); | |
// Add the current incomplete buffer, and the auto the autocomplete buffer to the end | |
text = String.Format("{0}\r\n{1}{2}_", text, prompt, currentLine); | |
// Update the ui text | |
logText.text = text; | |
} | |
private void OpenConsole(bool enable) | |
{ | |
// If enable is true, put the console in the show position | |
if (enable) { transform.position = showPosition; } | |
// If enable is false, put the console miles away | |
else { transform.position = new Vector3(10000f, 10000f); } | |
// Set private backed var to match enable | |
_isOpen = enable; | |
} | |
private void StartAutoComplete() | |
{ | |
// We aren't in autoCompleteMode | |
if (!autoCompleteMode) | |
{ | |
// Set the autocomplete mode flag | |
autoCompleteMode = true; | |
// Set the autoComplete index to zero | |
autoCompleteIndex = 0; | |
// Grab a list of commandline matches | |
autoCompleteMatches = commandDict.Keys.ToList().FindAll(s => s.Contains(currentLine)).ToList(); | |
} | |
// Make sure there is at least one autoComplete match | |
if (autoCompleteMatches.Count > 0) | |
{ | |
// If autoCompleteIndex has gone over the number of possible matches, reset it to zero | |
if (autoCompleteMatches.Count <= autoCompleteIndex) { autoCompleteIndex = 0; } | |
// Set the autocomplete buffer to the commandMatch at the autoCompleteIndex | |
currentLine = autoCompleteMatches[autoCompleteIndex]; | |
// Increment autoCompleteIndex | |
autoCompleteIndex++; | |
} | |
} | |
private void EndAutoComplete() | |
{ | |
// Clear matches | |
autoCompleteMatches.Clear(); | |
// Reset auto complete mode flag | |
autoCompleteMode = false; | |
} | |
private void HandleLog(string message, string stackTrace, LogType type) | |
{ | |
// Write the unity message to the console | |
WriteLine(string.Format("Unity: {0}: {1}", type.ToString(), message)); | |
} | |
private void HandleLine() | |
{ | |
// Split the command out of the currentLine | |
string command = currentLine.Split(' ').FirstOrDefault() ?? ""; | |
// If the command string matches a command in the dict, invoke it | |
if (commandDict.ContainsKey(command)) { commandDict[command].action(currentLine); } | |
// Command not found, print error to console | |
else { WriteLine(String.Format("Command '{0}' not found", command)); } | |
// Clear the currentLine | |
currentLine = ""; | |
} | |
private void HelpCmd(string input) | |
{ | |
// Iterate through each command in the dict, and print the name and help string | |
foreach (Command cmd in commandDict.Values) { WriteLine(string.Format("{0,-20} {1}", cmd.name, cmd.help)); } | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment