├── .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 | --------------------------------------------------------------------------------