├── .gitignore
├── Assets
├── SmartConsole.meta
└── SmartConsole
│ ├── Code.meta
│ ├── Code
│ ├── SmartConsole.cs
│ └── SmartConsole.cs.meta
│ ├── Fonts.meta
│ ├── Fonts
│ ├── SourceCodePro-Light.otf
│ ├── SourceCodePro-Light.otf.meta
│ ├── scp.license.txt
│ └── scp.license.txt.meta
│ ├── Scenes.meta
│ ├── Scenes
│ ├── Example.unity
│ └── Example.unity.meta
│ ├── readme.txt
│ └── readme.txt.meta
├── LICENSE.md
├── ProjectSettings
├── AudioManager.asset
├── DynamicsManager.asset
├── EditorBuildSettings.asset
├── EditorSettings.asset
├── GraphicsSettings.asset
├── InputManager.asset
├── NavMeshLayers.asset
├── NetworkManager.asset
├── Physics2DSettings.asset
├── ProjectSettings.asset
├── QualitySettings.asset
├── TagManager.asset
└── TimeManager.asset
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | Assembly-CSharp-vs.csproj
2 | Assembly-CSharp.csproj
3 | Library/
4 | SmartConsole-csharp.sln
5 | SmartConsole.sln
--------------------------------------------------------------------------------
/Assets/SmartConsole.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 70cf4627c03a5bc4f845536b903417cd
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/Code.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 15d4dd996aff7c24889509b6bd741229
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/Code/SmartConsole.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014 Cranium Software
2 |
3 | // SmartConsole
4 | //
5 | // A Quake style debug console where you can add variables with the
6 | // CreateVariable functions and commands with the RegisterCommand functions
7 | // the variables wrap their underlying types, the commands are delegates
8 | // taking a string with the line of console input and returning void
9 |
10 | // TODO:
11 | // * sort out spammy history and 'return' key handling on mobile platforms
12 | // * improve cvar interface
13 | // * allow history to scroll
14 | // * improve autocomplete
15 | // * allow executing console script from file
16 |
17 | using UnityEngine;
18 | using System.Collections;
19 | using System.Collections.Generic;
20 |
21 | #if UNITY_EDITOR
22 | using UnityEditor;
23 | #endif
24 |
25 | // SE: broadly patterned after the debug console implementation from GLToy...
26 | // https://code.google.com/p/gltoy/source/browse/trunk/GLToy/Independent/Core/Console/GLToy_Console.h
27 |
28 | ///
29 | /// A Quake style debug console - should be added to an otherwise empty game object and have a font set in the inspector
30 | ///
31 | public class SmartConsole : MonoBehaviour
32 | {
33 | public delegate void ConsoleCommandFunction( string parameters );
34 |
35 | // allow to specify font (because we need one imported)
36 | ///
37 | /// The font used to render the console
38 | ///
39 | public Font m_font = null;
40 |
41 | // control the general layout here
42 |
43 | private const float k_animTime = 0.4f;
44 | private const float k_lineSpace = 0.05f;
45 | private const int k_historyLines = 120;
46 | private static Vector3 k_position = new Vector3( 0.01f, 0.65f, 0.0f );
47 | private static Vector3 k_fullPosition = new Vector3( 0.01f, 0.05f, 0.0f );
48 | private static Vector3 k_hidePosition = new Vector3( 0.01f, 1.1f, 0.0f );
49 | private static Vector3 k_scale = new Vector3( 0.5f, 0.5f, 1.0f );
50 |
51 | // SE: annoying having to leak this out publicly - basically to facilitate the weird and wonderful cvar implementation
52 | ///
53 | /// A class representing a console command - WARNING: this is only exposed as a hack!
54 | ///
55 | public class Command
56 | {
57 | public ConsoleCommandFunction m_callback = null;
58 | public string m_name = null;
59 | public string m_paramsExample = "";
60 | public string m_help = "(no description)";
61 | };
62 |
63 | // SE - this is a bit elaborate, needed to provide a way to do this
64 | // without relying on memory addresses or pointers... which has resulted in
65 | // a little blob of bloat and overhead for something that should be trivial... :/
66 |
67 | ///
68 | /// A class representing a console variable
69 | ///
70 | public class Variable< T > : Command where T : new()
71 | {
72 | public Variable( string name )
73 | {
74 | Initialise( name, "", new T() );
75 | }
76 |
77 | public Variable( string name, string description )
78 | {
79 | Initialise( name, description, new T() );
80 | }
81 |
82 | public Variable( string name, T initialValue )
83 | {
84 | Initialise( name, "", initialValue );
85 | }
86 |
87 | public Variable( string name, string description, T initalValue )
88 | {
89 | Initialise( name, description, initalValue );
90 | }
91 |
92 | public void Set( T val ) // SE: I don't seem to know enough C# to provide a user friendly assignment operator solution
93 | {
94 | m_value = val;
95 | }
96 |
97 | public static implicit operator T( Variable< T > var )
98 | {
99 | return var.m_value;
100 | }
101 |
102 | private void Initialise( string name, string description, T initalValue )
103 | {
104 | m_name = name;
105 | m_help = description;
106 | m_paramsExample = "";
107 | m_value = initalValue;
108 | m_callback = CommandFunction;
109 | }
110 |
111 | private static void CommandFunction( string parameters )
112 | {
113 | string[] split = CVarParameterSplit( parameters );
114 | if( ( split.Length != 0 ) && s_variableDictionary.ContainsKey( split[ 0 ] ) )
115 | {
116 | Variable< T > variable = s_variableDictionary[ split[ 0 ] ] as Variable< T >;
117 | string conjunction = " is set to ";
118 | if( split.Length == 2 )
119 | {
120 | variable.SetFromString( split[ 1 ] );
121 | conjunction = " has been set to ";
122 | }
123 |
124 | WriteLine( variable.m_name + conjunction + variable.m_value );
125 | }
126 | }
127 |
128 | private void SetFromString( string value )
129 | {
130 | m_value = (T)System.Convert.ChangeType( value, typeof( T ) );
131 | }
132 |
133 | private T m_value;
134 | };
135 |
136 | // ...
137 |
138 | void Awake()
139 | {
140 | if( !gameObject.activeSelf )
141 | {
142 | return;
143 | }
144 |
145 | if( m_font == null )
146 | {
147 | Debug.LogError( "SmartConsole requires a font to be set in the inspector" );
148 | }
149 |
150 | Initialise( this );
151 | }
152 |
153 | // SE: these can be made non-static to enable some functionality when paused and un-paused
154 | // however... there are a lot of problems when making changes when paused anyway...
155 | // so instead I've left them static to detect that case and warn the user that they are doing
156 | // something flakey instead...
157 | static int s_flippy = 0;
158 | static bool s_blink = false;
159 | static bool s_first = true;
160 | void Update()
161 | {
162 | if( !gameObject.activeSelf )
163 | {
164 | return;
165 | }
166 |
167 | // SE - delayed initialisation. unfortunate
168 | if( s_first )
169 | {
170 | if( ( s_fps == null ) || ( s_textInput == null ) )
171 | {
172 | Debug.LogWarning( "Some variables are null that really shouldn't be! Did you make code changes whilst paused? Be aware that such changes are not safe in general!" );
173 | return; // SE: can't do anything meaningful here, probably the user recompiled things
174 | }
175 |
176 | SetTopDrawOrderOnGUIText( s_fps.guiText );
177 | SetTopDrawOrderOnGUIText( s_textInput.guiText );
178 | foreach( GameObject historyLine in s_historyDisplay )
179 | {
180 | SetTopDrawOrderOnGUIText( historyLine.guiText );
181 | }
182 |
183 | s_first = false;
184 | }
185 |
186 | HandleInput();
187 |
188 | if( s_showConsole )
189 | {
190 | s_visiblityLerp += Time.deltaTime / k_animTime;
191 | }
192 | else
193 | {
194 | s_visiblityLerp -= Time.deltaTime / k_animTime;
195 | }
196 |
197 | s_visiblityLerp = Mathf.Clamp01( s_visiblityLerp );
198 |
199 | transform.position = Vector3.Lerp( k_hidePosition, ( s_drawFullConsole ? k_fullPosition : k_position ), SmootherStep( s_visiblityLerp ) );
200 | transform.localScale = k_scale;
201 |
202 | if( ( s_textInput != null )
203 | && ( s_textInput.guiText != null ) )
204 | {
205 | s_textInput.guiText.text = ">" + s_currentInputLine + ( ( s_blink ) ? "_" : "" );
206 | }
207 |
208 | ++s_flippy;
209 | s_flippy &= 7;
210 | if( s_flippy == 0 )
211 | {
212 | s_blink = !s_blink;
213 | }
214 |
215 | if( s_drawFPS )
216 | {
217 | s_fps.guiText.text = "" + ( 1.0f / Time.deltaTime ) + " fps ";
218 | s_fps.transform.position = new Vector3( 0.8f, 1.0f, 0.0f );
219 | }
220 | else
221 | {
222 | s_fps.transform.position = new Vector3( 1.0f, 10.0f, 0.0f );
223 | }
224 | }
225 |
226 | ///
227 | /// Clears out the console log
228 | ///
229 | ///
230 | ///
231 | /// SmartConsole.Clear();
232 | ///
233 | ///
234 | public static void Clear()
235 | {
236 | s_outputHistory.Clear();
237 | SetStringsOnHistoryElements();
238 | }
239 |
240 | ///
241 | /// Write a message to the debug console (only - not the log)
242 | ///
243 | ///
244 | /// The message to display
245 | ///
246 | ///
247 | ///
248 | /// SmartConsole.Print( "Hello world!" );
249 | ///
250 | ///
251 | public static void Print( string message )
252 | {
253 | WriteLine( message );
254 | }
255 |
256 | ///
257 | /// Write a message to the debug console (only - not the log)
258 | ///
259 | ///
260 | /// The message to display
261 | ///
262 | ///
263 | ///
264 | /// SmartConsole.WriteLine( "Hello world!" );
265 | ///
266 | ///
267 | public static void WriteLine( string message )
268 | {
269 | s_outputHistory.Add( DeNewLine( message ) );
270 | s_currentCommandHistoryIndex = s_outputHistory.Count - 1;
271 | SetStringsOnHistoryElements();
272 | }
273 |
274 | ///
275 | /// Execute a string as if it were a single line of input to the console
276 | ///
277 | public static void ExecuteLine( string inputLine )
278 | {
279 | WriteLine( ">" + inputLine );
280 | string[] words = CComParameterSplit( inputLine );
281 | if( words.Length > 0 )
282 | {
283 | if( s_masterDictionary.ContainsKey( words[ 0 ] ) )
284 | {
285 | s_commandHistory.Add( inputLine );
286 | s_masterDictionary[ words[ 0 ] ].m_callback( inputLine );
287 | }
288 | else
289 | {
290 | WriteLine( "Unrecognised command or variable name: " + words[ 0 ] );
291 | }
292 | }
293 | }
294 |
295 | // public static void ExecuteFile( string path ) {} //...
296 |
297 | public static void RemoveCommandIfExists( string name )
298 | {
299 | s_commandDictionary.Remove( name );
300 | s_masterDictionary.Remove( name );
301 | }
302 |
303 | ///
304 | /// Register a console command with an example of usage and a help description
305 | /// e.g. SmartConsole.RegisterCommand( "echo", "echo ", "writes to the console log", SmartConsole.Echo );
306 | ///
307 | public static void RegisterCommand( string name, string exampleUsage, string helpDescription, ConsoleCommandFunction callback )
308 | {
309 | Command command = new Command();
310 | command.m_name = name;
311 | command.m_paramsExample = exampleUsage;
312 | command.m_help = helpDescription;
313 | command.m_callback = callback;
314 |
315 | s_commandDictionary.Add( name, command );
316 | s_masterDictionary.Add( name, command );
317 | }
318 |
319 | ///
320 | /// Register a console command with a help description
321 | /// e.g. SmartConsole.RegisterCommand( "help", "displays help information for console command where available", SmartConsole.Help );
322 | ///
323 | public static void RegisterCommand( string name, string helpDescription, ConsoleCommandFunction callback )
324 | {
325 | RegisterCommand( name, "", helpDescription, callback );
326 | }
327 |
328 | ///
329 | /// Register a console command
330 | /// e.g. SmartConsole.RegisterCommand( "foo", Foo );
331 | ///
332 | public static void RegisterCommand( string name, ConsoleCommandFunction callback )
333 | {
334 | RegisterCommand( name, "", "(no description)", callback );
335 | }
336 |
337 | ///
338 | /// Create a console variable
339 | /// e.g. SmartConsole.Variable< bool > showFPS = SmartConsole.CreateVariable< bool >( "show.fps", "whether to draw framerate counter or not", false );
340 | ///
341 | public static Variable< T > CreateVariable< T >( string name, string description, T initialValue ) where T : new()
342 | {
343 | if( s_variableDictionary.ContainsKey( name ) )
344 | {
345 | Debug.LogError( "Tried to add already existing console variable!" );
346 | return null;
347 | }
348 |
349 | Variable< T > returnValue = new Variable< T >( name, description, initialValue );
350 | s_variableDictionary.Add( name, returnValue );
351 | s_masterDictionary.Add( name, returnValue );
352 |
353 | return returnValue;
354 | }
355 |
356 | ///
357 | /// Create a console variable without specifying a default value
358 | /// e.g. SmartConsole.Variable< float > gameSpeed = SmartConsole.CreateVariable< float >( "game.speed", "the current speed of the game" );
359 | ///
360 | public static Variable< T > CreateVariable< T >( string name, string description ) where T : new()
361 | {
362 | return CreateVariable< T >( name, description, new T() );
363 | }
364 |
365 | ///
366 | /// Create a console variable without specifying a description or default value
367 | /// e.g. SmartConsole.Variable< string > someString = SmartConsole.CreateVariable< string >( "some.string" );
368 | ///
369 | public static Variable< T > CreateVariable< T >( string name ) where T : new()
370 | {
371 | return CreateVariable< T >( name, "" );
372 | }
373 |
374 | ///
375 | /// Destroy a console variable (so its name can be reused)
376 | ///
377 | public static void DestroyVariable< T >( Variable< T > variable ) where T : new()
378 | {
379 | s_variableDictionary.Remove( variable.m_name );
380 | s_masterDictionary.Remove( variable.m_name );
381 | }
382 |
383 | // --- commands
384 |
385 | private static void Help( string parameters )
386 | {
387 | // try and lay it out nicely...
388 | const int nameLength = 25;
389 | const int exampleLength = 35;
390 | foreach( Command command in s_commandDictionary.Values )
391 | {
392 | string outputString = command.m_name;
393 | for( int i = command.m_name.Length; i < nameLength; ++i )
394 | {
395 | outputString += " ";
396 | }
397 |
398 | if( command.m_paramsExample.Length > 0 )
399 | {
400 | outputString += " example: " + command.m_paramsExample;
401 | }
402 | else
403 | {
404 | outputString += " ";
405 | }
406 |
407 | for( int i = command.m_paramsExample.Length; i < exampleLength; ++i )
408 | {
409 | outputString += " ";
410 | }
411 |
412 | WriteLine( outputString + command.m_help );
413 | }
414 | }
415 |
416 | private static void Echo( string parameters )
417 | {
418 | string outputMessage = "";
419 | string[] split = CComParameterSplit( parameters );
420 | for( int i = 1; i < split.Length; ++i )
421 | {
422 | outputMessage += split[ i ] + " ";
423 | }
424 |
425 | if( outputMessage.EndsWith( " " ) )
426 | {
427 | outputMessage.Substring( 0, outputMessage.Length - 1 );
428 | }
429 |
430 | WriteLine( outputMessage );
431 | }
432 |
433 | private static void Clear( string parameters )
434 | {
435 | Clear();
436 | }
437 |
438 | private static void LastExceptionCallStack( string parameters )
439 | {
440 | DumpCallStack( s_lastExceptionCallStack );
441 | }
442 |
443 | private static void LastErrorCallStack( string parameters )
444 | {
445 | DumpCallStack( s_lastErrorCallStack );
446 | }
447 |
448 | private static void LastWarningCallStack( string parameters )
449 | {
450 | DumpCallStack( s_lastWarningCallStack );
451 | }
452 |
453 | private static void Quit( string parameters )
454 | {
455 | #if UNITY_EDITOR
456 | EditorApplication.isPlaying = false;
457 | #else
458 | Application.Quit();
459 | #endif
460 | }
461 |
462 |
463 | private static void ListCvars( string parameters )
464 | {
465 | // try and lay it out nicely...
466 | const int nameLength = 50;
467 | foreach( Command variable in s_variableDictionary.Values )
468 | {
469 | string outputString = variable.m_name;
470 | for( int i = variable.m_name.Length; i < nameLength; ++i )
471 | {
472 | outputString += " ";
473 | }
474 |
475 | WriteLine( outputString + variable.m_help );
476 | }
477 | }
478 |
479 | // --- internals
480 |
481 | private static void Initialise( SmartConsole instance )
482 | {
483 | // run this only once...
484 | if( s_textInput != null )
485 | {
486 | return;
487 | }
488 |
489 | Application.RegisterLogCallback( LogHandler );
490 |
491 | InitialiseCommands();
492 | InitialiseVariables();
493 | InitialiseUI( instance );
494 | }
495 |
496 | const float k_toogleCDTime = 0.35f;
497 | static float s_toggleCooldown = 0.0f;
498 | static int s_currentCommandHistoryIndex = 0;
499 | private static void HandleInput()
500 | {
501 | const float k_minFrameRate = 0.0166f;
502 | s_toggleCooldown += ( Time.deltaTime < k_minFrameRate ) ? k_minFrameRate : Time.deltaTime;
503 |
504 | if( s_toggleCooldown < k_toogleCDTime )
505 | {
506 | return;
507 | }
508 |
509 | bool tapped = false;
510 | if( Input.touchCount > 0 )
511 | {
512 | tapped = IsInputCoordInBounds( Input.touches[ 0 ].position );
513 | }
514 | else if( Input.GetMouseButton( 0 ) )
515 | {
516 | tapped = IsInputCoordInBounds( new Vector2( Input.mousePosition.x, Input.mousePosition.y ) );
517 | }
518 |
519 | if( tapped || Input.GetKeyUp( KeyCode.BackQuote ) )
520 | {
521 | if( !s_consoleLock )
522 | {
523 | s_showConsole = !s_showConsole;
524 | if( s_showConsole )
525 | {
526 | s_currentInputLine = ""; // clear out last input because its annoying otherwise...
527 | #if UNITY_IPHONE || UNITY_ANDROID
528 | TouchScreenKeyboard.Open( "", TouchScreenKeyboardType.Default, false, true, false, true );
529 | #endif
530 | }
531 | }
532 | s_toggleCooldown = 0.0f;
533 | }
534 |
535 | if( s_commandHistory.Count > 0 )
536 | {
537 | bool update = false;
538 | if( Input.GetKeyDown( KeyCode.UpArrow ) )
539 | {
540 | update = true;
541 | --s_currentCommandHistoryIndex;
542 |
543 | }
544 | else if( Input.GetKeyDown( KeyCode.DownArrow ) )
545 | {
546 | update = true;
547 | ++s_currentCommandHistoryIndex;
548 | }
549 |
550 | if( update )
551 | {
552 | s_currentCommandHistoryIndex = Mathf.Clamp( s_currentCommandHistoryIndex, 0, s_commandHistory.Count - 1 );
553 | s_currentInputLine = s_commandHistory[ s_currentCommandHistoryIndex ];
554 | }
555 | }
556 |
557 | HandleTextInput();
558 | }
559 |
560 | private static void InitialiseCommands()
561 | {
562 | RegisterCommand( "clear", "clear the console log", Clear );
563 | RegisterCommand( "cls", "clear the console log (alias for Clear)", Clear );
564 | RegisterCommand( "echo", "echo ", "writes to the console log (alias for echo)", Echo );
565 | RegisterCommand( "help", "displays help information for console command where available", Help );
566 | RegisterCommand( "list", "lists all currently registered console variables", ListCvars );
567 | RegisterCommand( "print", "print ", "writes to the console log", Echo );
568 | RegisterCommand( "quit", "quit the game (not sure this works with iOS/Android)", Quit );
569 | RegisterCommand( "callstack.warning", "display the call stack for the last warning message", LastWarningCallStack );
570 | RegisterCommand( "callstack.error", "display the call stack for the last error message", LastErrorCallStack );
571 | RegisterCommand( "callstack.exception", "display the call stack for the last exception message", LastExceptionCallStack );
572 | }
573 |
574 | private static void InitialiseVariables()
575 | {
576 | s_drawFPS = CreateVariable< bool >( "show.fps", "whether to draw framerate counter or not", false );
577 |
578 | s_drawFullConsole = CreateVariable< bool >( "console.fullscreen", "whether to draw the console over the whole screen or not", false );
579 | s_consoleLock = CreateVariable< bool >( "console.lock", "whether to allow showing/hiding the console", false );
580 | s_logging = CreateVariable< bool >( "console.log", "whether to redirect log to the console", true );
581 | }
582 |
583 | private static Font s_font = null;
584 | private static void InitialiseUI( SmartConsole instance )
585 | {
586 | s_font = instance.m_font;
587 | if( s_font == null )
588 | {
589 | Debug.LogError( "SmartConsole needs to have a font set on an instance in the editor!" );
590 | s_font = new Font( "Arial" );
591 | }
592 |
593 | s_fps = instance.AddChildWithGUIText( "FPSCounter" );
594 | s_textInput = instance.AddChildWithGUIText( "SmartConsoleInputField" );
595 | s_historyDisplay = new GameObject[ k_historyLines ];
596 | for( int i = 0; i < k_historyLines; ++i )
597 | {
598 | s_historyDisplay[ i ] = instance.AddChildWithGUIText( "SmartConsoleHistoryDisplay" + i );
599 | }
600 |
601 | instance.Layout();
602 | }
603 |
604 | private GameObject AddChildWithGUIText( string name )
605 | {
606 | return AddChildWithComponent< GUIText >( name );
607 | }
608 |
609 | private GameObject AddChildWithComponent< T >( string name ) where T : Component
610 | {
611 | GameObject returnObject = new GameObject();
612 | returnObject.AddComponent< T >();
613 | returnObject.transform.parent = transform;
614 | returnObject.name = name;
615 | return returnObject;
616 | }
617 |
618 | private static void SetTopDrawOrderOnGUIText( GUIText text )
619 | {
620 | // SE - TODO: work out how to do this. its currently pretty annoying because the text goes behind
621 | // why would i ever want that behaviour?!?!?!
622 | //MaterialManager.SetRenderLayer( text.renderer, RenderLayers.OVERLAY, false );
623 | //text.renderer.sortingOrder = 0;
624 | }
625 |
626 | private static void HandleTextInput()
627 | {
628 | bool autoCompleteHandled = false;
629 | foreach( char c in Input.inputString )
630 | {
631 | switch( c )
632 | {
633 | case '\b': s_currentInputLine = ( s_currentInputLine.Length > 0 ) ? s_currentInputLine.Substring( 0, s_currentInputLine.Length - 1 ) : ""; break;
634 | case '\n':
635 | case '\r': ExecuteCurrentLine(); s_currentInputLine = ""; break;
636 | case '\t': AutoComplete(); autoCompleteHandled = true; break; // SE - unity doesn't seem to give this here so we check a keydown as well...
637 | default: s_currentInputLine = s_currentInputLine + c; break;
638 | }
639 | }
640 |
641 | if( !autoCompleteHandled && Input.GetKeyDown( KeyCode.Tab ) )
642 | {
643 | AutoComplete();
644 | }
645 | }
646 |
647 | private static void ExecuteCurrentLine()
648 | {
649 | ExecuteLine( s_currentInputLine );
650 | }
651 |
652 | private static void AutoComplete()
653 | {
654 | string[] lookup = CComParameterSplit( s_currentInputLine );
655 | if( lookup.Length == 0 )
656 | {
657 | // don't auto complete if we have typed any parameters so far or nothing at all...
658 | return;
659 | }
660 |
661 | Command nearestMatch = s_masterDictionary.AutoCompleteLookup( lookup[ 0 ] );
662 |
663 | // only complete to the next dot if there is one present in the completion string which
664 | // we don't already have in the lookup string
665 | int dotIndex = 0;
666 | do
667 | {
668 | dotIndex = nearestMatch.m_name.IndexOf( ".", dotIndex + 1 );
669 | }
670 | while( ( dotIndex > 0 ) && ( dotIndex < lookup[ 0 ].Length ) );
671 |
672 | string insertion = nearestMatch.m_name;
673 | if( dotIndex >= 0 )
674 | {
675 | insertion = nearestMatch.m_name.Substring( 0, dotIndex + 1 );
676 | }
677 |
678 | if( insertion.Length < s_currentInputLine.Length )
679 | {
680 | do
681 | {
682 | if( AutoCompleteTailString( "true" ) ) break;
683 | if( AutoCompleteTailString( "false" ) ) break;
684 | if( AutoCompleteTailString( "True" ) ) break;
685 | if( AutoCompleteTailString( "False" ) ) break;
686 | if( AutoCompleteTailString( "TRUE" ) ) break;
687 | if( AutoCompleteTailString( "FALSE" ) ) break;
688 | }
689 | while( false );
690 | }
691 | else if( insertion.Length >= s_currentInputLine.Length ) // SE - is this really correct?
692 | {
693 | s_currentInputLine = insertion;
694 | }
695 | }
696 |
697 | private static bool AutoCompleteTailString( string tailString )
698 | {
699 | for( int i = 1; i < tailString.Length; ++i )
700 | {
701 | if( s_currentInputLine.EndsWith( " " + tailString.Substring( 0, i ) ) )
702 | {
703 | s_currentInputLine = s_currentInputLine.Substring( 0, s_currentInputLine.Length - 1 ) + tailString.Substring( i - 1 );
704 | return true;
705 | }
706 | }
707 |
708 | return false;
709 | }
710 |
711 | private void Layout()
712 | {
713 | float y = 0.0f;
714 | LayoutTextAtY( s_textInput, y );
715 | LayoutTextAtY( s_fps, y );
716 | y += k_lineSpace;
717 | for( int i = 0; i < k_historyLines; ++i )
718 | {
719 | LayoutTextAtY( s_historyDisplay[ i ], y );
720 | y += k_lineSpace;
721 | }
722 | }
723 |
724 | private static void LayoutTextAtY( GameObject o, float y )
725 | {
726 | o.transform.localPosition = new Vector3( 0.0f, y, 0.0f );
727 | o.guiText.fontStyle = FontStyle.Normal;
728 | o.guiText.font = s_font;
729 | }
730 |
731 | private static void SetStringsOnHistoryElements()
732 | {
733 | for( int i = 0; i < k_historyLines; ++i )
734 | {
735 | int historyIndex = s_outputHistory.Count - 1 - i;
736 | if( historyIndex >= 0 )
737 | {
738 | s_historyDisplay[ i ].guiText.text = s_outputHistory[ s_outputHistory.Count - 1 - i ];
739 | }
740 | else
741 | {
742 | s_historyDisplay[ i ].guiText.text = "";
743 | }
744 | }
745 | }
746 |
747 | private static bool IsInputCoordInBounds( Vector2 inputCoordinate )
748 | {
749 | return ( inputCoordinate.x < ( 0.05f * Screen.width ) ) && ( inputCoordinate.y > ( 0.95f * Screen.height ) );
750 | }
751 |
752 | private static void LogHandler( string message, string stack, LogType type )
753 | {
754 | if( !s_logging )
755 | {
756 | return;
757 | }
758 |
759 | string assertPrefix = "[Assert]: ";
760 | string errorPrefix = "[Debug.LogError]: ";
761 | string exceptPrefix = "[Debug.LogException]: ";
762 | string warningPrefix = "[Debug.LogWarning]: ";
763 | string otherPrefix = "[Debug.Log]: ";
764 |
765 | string prefix = otherPrefix;
766 | switch( type )
767 | {
768 | case LogType.Assert:
769 | {
770 | prefix = assertPrefix;
771 | break;
772 | }
773 |
774 | case LogType.Warning:
775 | {
776 | prefix = warningPrefix;
777 | s_lastWarningCallStack = stack;
778 | break;
779 | }
780 |
781 | case LogType.Error:
782 | {
783 | prefix = errorPrefix;
784 | s_lastErrorCallStack = stack;
785 | break;
786 | }
787 |
788 | case LogType.Exception:
789 | {
790 | prefix = exceptPrefix;
791 | s_lastExceptionCallStack = stack;
792 | break;
793 | }
794 |
795 | default:
796 | {
797 | break;
798 | }
799 | }
800 |
801 | WriteLine( prefix + message );
802 |
803 | switch( type )
804 | {
805 | case LogType.Assert:
806 | case LogType.Error:
807 | case LogType.Exception:
808 | {
809 | //WriteLine ( "Call stack:\n" + stack );
810 | break;
811 | }
812 |
813 | default:
814 | {
815 | break;
816 | }
817 | }
818 | }
819 |
820 | public static string[] CComParameterSplit( string parameters )
821 | {
822 | return parameters.Split( new char[]{ ' ' }, System.StringSplitOptions.RemoveEmptyEntries );
823 | }
824 |
825 | public static string[] CComParameterSplit( string parameters, int requiredParameters )
826 | {
827 | string[] split = CComParameterSplit( parameters );
828 | if( split.Length < ( requiredParameters + 1 ) )
829 | {
830 | WriteLine( "Error: not enough parameters for command. Expected " + requiredParameters + " found " + ( split.Length - 1 ) );
831 | }
832 |
833 | if( split.Length > ( requiredParameters + 1 ) )
834 | {
835 | int extras = ( ( split.Length - 1 ) - requiredParameters );
836 | WriteLine( "Warning: " + extras + "additional parameters will be dropped:" );
837 | for( int i = split.Length - extras; i < split.Length; ++i )
838 | {
839 | WriteLine ( "\"" + split[ i ] + "\"" );
840 | }
841 | }
842 |
843 | return split;
844 | }
845 |
846 | private static string[] CVarParameterSplit( string parameters )
847 | {
848 | string[] split = CComParameterSplit( parameters );
849 | if( split.Length == 0 )
850 | {
851 | WriteLine( "Error: not enough parameters to set or display the value of a console variable." );
852 | }
853 |
854 | if( split.Length > 2 )
855 | {
856 | int extras = ( split.Length - 3 );
857 | WriteLine( "Warning: " + extras + "additional parameters will be dropped:" );
858 | for( int i = split.Length - extras; i < split.Length; ++i )
859 | {
860 | WriteLine ( "\"" + split[ i ] + "\"" );
861 | }
862 | }
863 |
864 | return split;
865 | }
866 |
867 | private static string DeNewLine( string message )
868 | {
869 | return message.Replace( "\n", " | " );
870 | }
871 |
872 | private static void DumpCallStack( string stackString )
873 | {
874 | string[] lines = stackString.Split( new char[]{ '\r', '\n' } );
875 |
876 | if( lines.Length == 0 )
877 | {
878 | return;
879 | }
880 |
881 | int ignoreCount = 0;
882 | while( ( lines[ lines.Length - 1 - ignoreCount ].Length == 0 ) && ( ignoreCount < lines.Length ) )
883 | {
884 | ++ignoreCount;
885 | }
886 | int lineCount = lines.Length - ignoreCount;
887 | for( int i = 0; i < lineCount; ++i )
888 | {
889 | // SE - if the call stack is 100 deep without recursion you have much bigger problems than you can ever solve with a debugger...
890 | WriteLine( ( i + 1 ).ToString() + ( ( i < 9 ) ? " " : " " ) + lines[ i ] );
891 | }
892 | }
893 |
894 | private class AutoCompleteDictionary< T >: SortedDictionary< string, T >
895 | {
896 | public AutoCompleteDictionary()
897 | : base( new AutoCompleteComparer() )
898 | {
899 | m_comparer = this.Comparer as AutoCompleteComparer;
900 | }
901 |
902 | public T LowerBound( string lookupString )
903 | {
904 | m_comparer.Reset();
905 | this.ContainsKey( lookupString );
906 | return this[ m_comparer.LowerBound ];
907 | }
908 |
909 | public T UpperBound( string lookupString )
910 | {
911 | m_comparer.Reset();
912 | this.ContainsKey( lookupString );
913 | return this[ m_comparer.UpperBound ];
914 | }
915 |
916 | public T AutoCompleteLookup( string lookupString )
917 | {
918 | m_comparer.Reset();
919 | this.ContainsKey( lookupString );
920 | string key = ( m_comparer.UpperBound == null ) ? m_comparer.LowerBound : m_comparer.UpperBound;
921 | return this[ key ];
922 | }
923 |
924 | private class AutoCompleteComparer : IComparer< string >
925 | {
926 | private string m_lowerBound = null;
927 | private string m_upperBound = null;
928 |
929 | public string LowerBound { get{ return m_lowerBound; } }
930 | public string UpperBound { get{ return m_upperBound; } }
931 |
932 | public int Compare( string x, string y )
933 | {
934 | int comparison = Comparer< string >.Default.Compare( x, y );
935 |
936 | if( comparison >= 0 )
937 | {
938 | m_lowerBound = y;
939 | }
940 |
941 | if( comparison <= 0 )
942 | {
943 | m_upperBound = y;
944 | }
945 |
946 | return comparison;
947 | }
948 |
949 | public void Reset()
950 | {
951 | m_lowerBound = null;
952 | m_upperBound = null;
953 | }
954 | }
955 |
956 | private AutoCompleteComparer m_comparer;
957 | }
958 |
959 | private float SmootherStep( float t )
960 | {
961 | return ( ( 6 * t - 15 ) * t + 10 ) * t * t * t;
962 | }
963 |
964 | private static Variable< bool > s_drawFPS = null;
965 | private static Variable< bool > s_drawFullConsole = null;
966 | private static Variable< bool > s_consoleLock = null;
967 | private static Variable< bool > s_logging = null;
968 |
969 | private static GameObject s_fps = null;
970 | private static GameObject s_textInput = null;
971 | private static GameObject[] s_historyDisplay = null;
972 |
973 | private static AutoCompleteDictionary< Command > s_commandDictionary = new AutoCompleteDictionary< Command >();
974 | private static AutoCompleteDictionary< Command > s_variableDictionary = new AutoCompleteDictionary< Command >();
975 |
976 | private static AutoCompleteDictionary< Command > s_masterDictionary = new AutoCompleteDictionary< Command >();
977 |
978 | private static List< string > s_commandHistory = new List< string >();
979 | private static List< string > s_outputHistory = new List< string >();
980 |
981 | private static string s_lastExceptionCallStack = "(none yet)";
982 | private static string s_lastErrorCallStack = "(none yet)";
983 | private static string s_lastWarningCallStack = "(none yet)";
984 |
985 | private static string s_currentInputLine = "";
986 |
987 | private static float s_visiblityLerp = 0.0f;
988 | private static bool s_showConsole = false;
989 | }
990 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/Code/SmartConsole.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: eb978a00509576d4f96249bafe2db16f
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/Fonts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 15d1ff86dbb31884699955272cc9af81
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/Fonts/SourceCodePro-Light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/Assets/SmartConsole/Fonts/SourceCodePro-Light.otf
--------------------------------------------------------------------------------
/Assets/SmartConsole/Fonts/SourceCodePro-Light.otf.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 00381f306568a7f4d812458a7212ac7f
3 | TrueTypeFontImporter:
4 | serializedVersion: 2
5 | fontSize: 16
6 | forceTextureCase: -2
7 | characterSpacing: 1
8 | characterPadding: 0
9 | includeFontData: 1
10 | use2xBehaviour: 0
11 | fontNames: []
12 | customCharacters:
13 | fontRenderingMode: 3
14 | userData:
15 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/Fonts/scp.license.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/Assets/SmartConsole/Fonts/scp.license.txt
--------------------------------------------------------------------------------
/Assets/SmartConsole/Fonts/scp.license.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c77d410d69a9c9046a8df259032f4611
3 | TextScriptImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/Scenes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a2888b25ca2afef409d5a2128b217ee0
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/Scenes/Example.unity:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/Assets/SmartConsole/Scenes/Example.unity
--------------------------------------------------------------------------------
/Assets/SmartConsole/Scenes/Example.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b9ad17b30d2ec324c8a2ce474db76713
3 | DefaultImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/readme.txt:
--------------------------------------------------------------------------------
1 | # SmartConsole
2 |
3 | A Quake style debug console for Unity with autocomplete, a command history and the ability to register custom commands and variables
4 |
5 | The implementation is broadly patterned after the debug console implementation from GLToy: https://code.google.com/p/gltoy/source/browse/trunk/GLToy/Independent/Core/Console/GLToy_Console.h
6 |
7 | This document is formatted following the conventions of Markdown to aid readability - you can use websites like http://markable.in/editor/ to view it in a more visually pleasing format than plain-text.
8 |
9 | ## Examples
10 |
11 | There is an example scene included in the package under SmartConsole/Scenes/Example.scene
12 |
13 | ## Usage
14 |
15 | The SmartConsole component should be added to an otherwise empty game object and have a font set in the inspector. For ease of use static functions are provided so that the instance need not be tracked by calling code.
16 |
17 | The console is accessed with the backtick ` key when a keyboard is available, otherwise touching or clicking the top right corner will open the console ready for input
18 |
19 | Custom commands and variables can be registered using the SmartConsole class interface described later in this document. There are some built-in commands and variables.
20 |
21 | ## Built-in Commands
22 |
23 | ### clear
24 |
25 | clear the console log
26 |
27 | ### cls
28 |
29 | clear the console log (alias for clear)
30 |
31 | ### echo
32 |
33 | writes to the console log
34 |
35 | ### help
36 |
37 | lists all console commands with their descriptions and example usage (where available)
38 |
39 | ### list
40 |
41 | lists all currently registered console variables
42 |
43 | ### print
44 |
45 | writes to the console log (alias for echo)
46 |
47 | ### quit
48 |
49 | quit the game (where possible)
50 |
51 | ### callstack.warning
52 |
53 | display the call stack for the last warning message
54 |
55 | ### callstack.error
56 |
57 | display the call stack for the last error message
58 |
59 | ### callstack.exception
60 |
61 | display the call stack for the last exception message
62 |
63 | ## Built-in Variables
64 |
65 | ### show.fps
66 |
67 | whether to draw framerate counter or not
68 |
69 | ### console.fullscreen
70 |
71 | whether to draw the console over the whole screen or not
72 |
73 | ### console.lock
74 |
75 | whether to allow showing/hiding the console
76 |
77 | ### console.log
78 |
79 | whether to redirect log to the console
80 |
81 | ## SmartConsole Class Interface
82 |
83 | the SmartConsole class provides most of the functionality in the package:
84 |
85 | ### public delegate void ConsoleCommandFunction( string parameters )
86 |
87 | a delegate type for registering as a console command
88 |
89 | ### public static void Clear()
90 |
91 | clear the console history
92 |
93 | ### public static void Print( string message )
94 | ### public static void WriteLine( string message )
95 |
96 | write the message string to the debug console (only - not the log)
97 |
98 | ### public static void ExecuteLine( string inputLine )
99 |
100 | execute a string as if entered to the console
101 |
102 |
103 | ### public static void RemoveCommandIfExists( string name )
104 |
105 | unregister the named command, if it exists
106 |
107 | ### public static void RegisterCommand( string name, string exampleUsage, string helpDescription, ConsoleCommandFunction callback )
108 | ### public static void RegisterCommand( string name, string helpDescription, ConsoleCommandFunction callback )
109 | ### public static void RegisterCommand( string name, ConsoleCommandFunction callback )
110 |
111 | register a console command with an optional example usage and help description
112 |
113 |
114 | ### public static Variable< T > CreateVariable< T >( string name, string description, T initialValue ) where T : new()
115 | ### public static Variable< T > CreateVariable< T >( string name, string description ) where T : new()
116 | ### public static Variable< T > CreateVariable< T >( string name ) where T : new()
117 |
118 | used to create a console variable e.g.
119 |
120 | SmartConsole.Variable< bool > showFPS = SmartConsole.CreateVariable< bool >( "show.fps", "whether to draw framerate counter or not", false );
121 |
122 | ### public static void DestroyVariable< T >( Variable< T > variable ) where T : new()
123 |
124 | destroy a console variable (so its name can be reused)
125 |
126 | ## SmartConsole.Variable< T > Generic Class Interface
127 |
128 | SmartConsole.Variable< T > references are returned when using the CreateVariable function and should be kept if you want to modify or read a console variable
129 |
130 |
131 | ### public void Set( T val )
132 |
133 | provided as a workaround to not being able to overload the assignment operator - sets the value of the console variable
134 |
135 | ### public static implicit operator T( Variable< T > var )
136 |
137 | allows reading of a console variable in a syntactically convenient fashion e.g.
138 |
139 | float f = 0.0f;
140 | SmartConsole.Variable< float > myFloatVar = SmartConsole.CreateVariable< float >( "my.float", "an example", 1.0f );
141 | f = myFloatVar; // f == 1.0f
142 |
143 | ## Version History
144 |
145 | ### 1.0.3
146 | - add a warning if the user pauses, makes code changes, then unpauses
147 | (it seems like this behaviour may have changed in recent Unity updates, but relying on edit-and-continue is not safe anyway!)
148 |
149 | ### 1.0.2
150 |
151 | - add this document
152 |
153 | ### 1.0
154 |
155 | - initial release
156 | - displays console on screen
157 | - redirects unity log to console
158 | - input and rendering tested with iOS and Android
159 | - command history
160 | - autocomplete
161 | - fps counter
162 | - command registration and deletion
163 | - variable registration and deletion
164 | - display call stacks in game
165 |
166 | ## Contact Us
167 |
168 | info@cranium-software.co.uk
169 |
170 |
--------------------------------------------------------------------------------
/Assets/SmartConsole/readme.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 272d424960e42084bbad48d0990dd7e6
3 | TextScriptImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2016 Cranium Software
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
4 | associated documentation files ("SmartConsole"), to deal in SmartConsole without restriction,
5 | including without limitation the rights to use, copy, modify, merge, publish, distribute,
6 | sublicense, and/or sell copies of SmartConsole, and to permit persons to whom SmartConsole is
7 | furnished to do so, subject to the following conditions:
8 |
9 | (1) The above copyright notice and this permission notice shall be included in all copies or
10 | substantial portions of SmartConsole, including but not limited to redistribution in binary
11 | form.
12 |
13 | (2) Except as contained in this notice, the name(s) of the above copyright holders shall not be
14 | used in advertising or otherwise to promote the sale, use or other dealings in SmartConsole
15 | without prior written authorization.
16 |
17 | SMARTCONSOLE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
18 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
21 | OF OR IN CONNECTION WITH SMARTCONSOLE OR THE USE OR OTHER DEALINGS IN SMARTCONSOLE.
22 |
--------------------------------------------------------------------------------
/ProjectSettings/AudioManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/AudioManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/DynamicsManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/DynamicsManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/EditorBuildSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/EditorBuildSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/EditorSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/EditorSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/GraphicsSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/GraphicsSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/InputManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/InputManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/NavMeshLayers.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/NavMeshLayers.asset
--------------------------------------------------------------------------------
/ProjectSettings/NetworkManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/NetworkManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/Physics2DSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/Physics2DSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/ProjectSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/ProjectSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/QualitySettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/QualitySettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/TagManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/TagManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/TimeManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CraniumSoftware/SmartConsole/314bc8bb3df597ed75984a8477f53d4841f32134/ProjectSettings/TimeManager.asset
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SmartConsole - a Quake-style console for Unity projects
2 |
3 | We decided to open source and make free our SmartConsole plugin.
4 |
5 | It may be a little outdated since it was originally built for Unity 4.x.
6 |
7 | Hopefully it is still of some use. :)
8 |
--------------------------------------------------------------------------------