2020/DebugConsole

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(An updated version of the debug console a little more in line with its time (2020))
 
m (anticipated the need for users to add log entry fields (perf: log entry by ref) + enforced robustness in regard to null or empty filter options because they will inevitably toy with it when doing their own versions)
 
(9 intermediate revisions by one user not shown)
Line 6: Line 6:
  
 
== Description ==
 
== Description ==
A more up to date, as of 2020 (Unity 5.6.1f1 through 2019.3.7.f1), a little more professional, minimal debug console.
+
A more up to date, as of 2020 (Unity 5.6.1f1 through 2019.3.8.f1), a little more professional, minimal debug console.
  
 
== Archive File ==
 
== Archive File ==
Line 14: Line 14:
 
== Archive content ==
 
== Archive content ==
  
A c# script.<BR>A Resource folder containing a GUI skin.
+
A c# script.<BR>A Resources folder containing a GUI skin.
  
 
== Usage ==
 
== Usage ==
<b>. Installation:</b> Unzip the archive content in your Standard Assets folder (<b>required</b>).
+
<b>. Installation:</b> Unzip the archive content in your Standard Assets folder <b>(required)</b>.
  
 
<BR><b>. Setup:</b> no action required.
 
<BR><b>. Setup:</b> no action required.
Line 26: Line 26:
  
 
== Professionals ==
 
== Professionals ==
<b>. Net compatibility level:</b> 2.0 minimum
+
<b>. Net compatibility level:</b> 2.0 minimum - no dynamic methods.
  
 
<BR><b>. Unsafe code:</b> None.
 
<BR><b>. Unsafe code:</b> None.
Line 41: Line 41:
  
 
== C# - DebugConsole.cs ==
 
== C# - DebugConsole.cs ==
The script _must_ be named DebugConsole.cs. It has no real use. It is for compliance with other wiki articles only. The script is available through the archive linked at the top of this page.
+
The script <b>must</b> be named DebugConsole.cs. It has no real use. It is for compliance with other wiki articles only. The script is available through the archive linked at the top of this page.
  
  
Line 79: Line 79:
 
//************************************************************************************************
 
//************************************************************************************************
  
        private const string FMT_TRACE  = "  * {0} : line {1} : {2}()\r\n";
+
private const string FMT_TRACE  = "  * {0} : line {1} : {2}()\r\n";
  
        private const char  MEMBER_SEP = '.';
+
private const char  MEMBER_SEP = '.';
  
 
//************************************************************************************************
 
//************************************************************************************************
Line 87: Line 87:
 
//************************************************************************************************
 
//************************************************************************************************
  
        static private string QualifiedMethodName( StackFrame frame )
+
static private string QualifiedMethodName( StackFrame frame )
        {
+
{
            return frame.GetMethod().DeclaringType.Name + MEMBER_SEP + frame.GetMethod().Name;
+
return frame.GetMethod().DeclaringType.Name + MEMBER_SEP + frame.GetMethod().Name;
        }
+
}
  
 
//************************************************************************************************
 
//************************************************************************************************
Line 96: Line 96:
 
//************************************************************************************************
 
//************************************************************************************************
  
        static private string Msg( string msg, TRACE trace = TRACE.DEFAULT )
+
static private string Msg( string msg, TRACE trace = TRACE.DEFAULT )
        {
+
{
            switch( trace )
+
switch( trace )
            {
+
{
        //****************************************************************************************
+
//****************************************************************************************
        //
+
//
        //****************************************************************************************
+
//****************************************************************************************
  
                case TRACE.FULL:
+
case TRACE.FULL:
                {
+
{
                    StackTrace  full  = new StackTrace ( 2 , true );
+
StackTrace  full  = new StackTrace ( 2 , true );
  
                    StackFrame[] frames = full.GetFrames();
+
StackFrame[] frames = full.GetFrames();
  
                    string      output = string.Empty;
+
string      output = string.Empty;
  
                    for( int frame = frames.Length - 1; frame >= 0; --frame )
+
for( int frame = frames.Length - 1; frame >= 0; --frame )
                    {
+
{
                        StackFrame Frame = frames[ frame ];
+
StackFrame Frame = frames[ frame ];
  
                        output += string.Format( FMT_TRACE, Frame.GetFileName(), Frame.GetFileLineNumber(), QualifiedMethodName( Frame ) );
+
output += string.Format( FMT_TRACE, Frame.GetFileName(), Frame.GetFileLineNumber(), QualifiedMethodName( Frame ) );
                    }
+
}
  
                    return output + msg;
+
return output + msg;
                }
+
}
  
        //****************************************************************************************
+
//****************************************************************************************
        //
+
//
        //****************************************************************************************
+
//****************************************************************************************
  
                case TRACE.SHORT:
+
case TRACE.SHORT:
                {
+
{
                    StackTrace stack = new StackTrace( 2 , true );
+
StackTrace stack = new StackTrace( 2 , true );
  
                    StackFrame Frame = stack.GetFrame( 0 );
+
StackFrame Frame = stack.GetFrame( 0 );
  
                    return string.Format( FMT_TRACE, Frame.GetFileName(), Frame.GetFileLineNumber(), QualifiedMethodName( Frame ) ) + msg;
+
return string.Format( FMT_TRACE, Frame.GetFileName(), Frame.GetFileLineNumber(), QualifiedMethodName( Frame ) ) + msg;
                }
+
}
  
        //****************************************************************************************
+
//****************************************************************************************
        //
+
//
        //****************************************************************************************
+
//****************************************************************************************
  
                default :
+
default :
                {
+
{
                    return msg;
+
return msg;
                }
+
}
            }
+
}
        }
+
}
  
 
//************************************************************************************************
 
//************************************************************************************************
Line 171: Line 171:
 
     //****************************************************************************************************
 
     //****************************************************************************************************
  
    public struct Entry
+
public struct Entry
    {
+
{
        public int     category;
+
public int   category;
  
        public string   date;
+
public string date;
  
        public string   msg;
+
public string msg;
    }
+
}
  
 
     //****************************************************************************************************
 
     //****************************************************************************************************
Line 216: Line 216:
 
         //************************************************************************************************
 
         //************************************************************************************************
  
         static public ulong Align       ( ulong s, ulong a ) { return ( s + ( a - 1 ) ) & ~( a - 1 ); }
+
         static public ulong Align( ulong s, ulong a ) { return ( s + ( a - 1 ) ) & ~( a - 1 ); }
                                 
+
        static public ulong AlignNonZero( ulong s, ulong a ) { return ( s != 0 ) ? Align( s, a ) : ( a ); }
+
  
 
         //************************************************************************************************
 
         //************************************************************************************************
Line 226: Line 224:
 
         public Buffer( int paramCapacity = 0 )
 
         public Buffer( int paramCapacity = 0 )
 
         {
 
         {
             m_capacity = ( paramCapacity != 0 ) ? ( int )AlignNonZero( ( ulong )Mathf.Max( 0, paramCapacity ), 4 ) : 1024;
+
             m_capacity = ( paramCapacity != 0 ) ? ( int )Align( ( ulong )Mathf.Max( 4, paramCapacity ), 4 ) : 1024;
  
 
             m_entries  = new Entry[ m_capacity ];
 
             m_entries  = new Entry[ m_capacity ];
Line 257: Line 255:
  
  
             m_entries[ m_end ].category = category;
+
             ref Entry e = ref m_entries[ m_end ];
 +
 
 +
            e.category = category;
 
          
 
          
             m_entries[ m_end ].date     = date;
+
             e.date     = date;
  
             m_entries[ m_end ].msg     = msg;
+
             e.msg       = msg;
 
         }
 
         }
  
Line 268: Line 268:
 
         //************************************************************************************************
 
         //************************************************************************************************
  
         public Entry this[ int index ] { get { return m_entries[ index & ( m_capacity - 1 ) ]; } }
+
         public ref Entry this[ int index ] { get { return ref m_entries[ index & ( m_capacity - 1 ) ]; } }
 
     }
 
     }
  
Line 274: Line 274:
 
     //
 
     //
 
     //****************************************************************************************************
 
     //****************************************************************************************************
 +
 +
    static public class GUIStacks
 +
    {
 +
        static private readonly Stack< GUISkin > m_skin  = new Stack< GUISkin >( 16 );
 +
 +
        static private readonly Stack< Color  > m_color = new Stack< Color  >( 16 );
 +
                                                                             
 +
        static private readonly Stack< Color  > m_cont  = new Stack< Color  >( 16 );
 +
                                                                             
 +
        static private readonly Stack< Color  > m_bgnd  = new Stack< Color  >( 16 );
 +
 +
 +
        static public void PushSkin          ( GUISkin skin  ) { m_skin.Push ( GUI.skin );            GUI.skin            = skin;  }
 +
                                                                                                     
 +
        static public void PushColor          ( Color  color ) { m_color.Push( GUI.color );          GUI.color          = color; }
 +
                                                                                                                     
 +
        static public void PushContentColor  ( Color  color ) { m_cont.Push ( GUI.contentColor );    GUI.contentColor    = color; }
 +
                                                       
 +
        static public void PushBackgroundColor( Color  color ) { m_bgnd.Push ( GUI.backgroundColor ); GUI.backgroundColor = color; }
 +
 +
        static public void PushSkin          () { m_skin.Push ( GUI.skin );            }
 +
                                                                                                     
 +
        static public void PushColor          () { m_color.Push( GUI.color );          }
 +
                                                                                                                     
 +
        static public void PushContentColor  () { m_cont.Push ( GUI.contentColor );    }
 +
                                                       
 +
        static public void PushBackgroundColor() { m_bgnd.Push ( GUI.backgroundColor ); }
 +
 +
        static public void PopSkin            () { GUI.skin            = m_skin.Pop (); }
 +
                                                       
 +
        static public void PopColor          () { GUI.color          = m_color.Pop(); }
 +
                                                                                                                     
 +
        static public void PopContentColor    () { GUI.contentColor    = m_cont.Pop (); }
 +
                                                       
 +
        static public void PopBackgroundColor () { GUI.backgroundColor = m_bgnd.Pop (); }
 +
    }
 +
 +
    //****************************************************************************************************
 +
    //
 +
    //****************************************************************************************************
 +
 
     public class GUIDropDown
 
     public class GUIDropDown
 
     {
 
     {
Line 325: Line 366:
 
         private void UpdateLayout( Rect rect, int selected, object[] options )
 
         private void UpdateLayout( Rect rect, int selected, object[] options )
 
         {
 
         {
 +
            if( Event.current.type != EventType.Layout ) return;
 +
 
             m_options      = options;
 
             m_options      = options;
 
                              
 
                              
 
             m_nbOptions    = ( options != null ) ? options.Length : 0;
 
             m_nbOptions    = ( options != null ) ? options.Length : 0;
 
                              
 
                              
             m_nbDisplayed  = Mathf.Min( m_nbOptions, MAX_LINES );
+
             m_nbDisplayed  = Mathf.Min( m_nbOptions, MAX_LINES );
 
                              
 
                              
             m_selected      = Mathf.Min( m_nbOptions, selected   );
+
             m_selected      = Mathf.Min( m_nbOptions - 1, selected );
  
 
             m_selectionRect = rect;
 
             m_selectionRect = rect;
Line 338: Line 381:
 
             if( m_expanded )
 
             if( m_expanded )
 
             {
 
             {
                 m_scrollRect = new Rect ( rect.x, rect.y + rect.height, rect.width, m_nbDisplayed * LINE_H );
+
                 if( m_nbOptions > 0 )
 +
                {
 +
                    m_scrollRect = new Rect ( rect.x, rect.y + rect.height, rect.width, m_nbDisplayed * LINE_H );
  
                m_viewRect  = new Rect ( 0.0f, 0.0f, m_scrollRect.width - ( m_nbDisplayed < m_nbOptions ? 15.0f : 0.0f ), m_nbOptions * LINE_H );
+
                    m_viewRect  = new Rect ( 0.0f, 0.0f, m_scrollRect.width - ( m_nbDisplayed < m_nbOptions ? 15.0f : 0.0f ), m_nbOptions * LINE_H );
 +
                }
 +
                else
 +
                {
 +
                    m_expanded = false;
 +
                }
 
             }
 
             }
 
         }
 
         }
Line 356: Line 406:
 
                 int firstVisible = ( int )( m_scroll.y / LINE_H );
 
                 int firstVisible = ( int )( m_scroll.y / LINE_H );
  
                 int lastVsible  = Mathf.Clamp( firstVisible + m_nbDisplayed, 0, m_nbOptions - 1 );
+
                 int lastVisible  = Mathf.Clamp( firstVisible + m_nbDisplayed, 0, m_nbOptions - 1 );
  
  
                 Color bckup = GUI.contentColor;
+
                 GUIStacks.PushContentColor();
  
                 m_scroll   = GUI.BeginScrollView( m_scrollRect, m_scroll, m_viewRect, false, false );
+
                 m_scroll = GUI.BeginScrollView( m_scrollRect, m_scroll, m_viewRect, false, false );
  
                 for( int option = firstVisible; option <= lastVsible; ++option )
+
                 for( int option = firstVisible; option <= lastVisible; ++option )
 
                 {
 
                 {
 
                     GUI.contentColor = ( option == m_selected ) ? COLOR_SELECTED : COLOR_NORMAL;
 
                     GUI.contentColor = ( option == m_selected ) ? COLOR_SELECTED : COLOR_NORMAL;
Line 377: Line 427:
 
                 GUI.EndScrollView();
 
                 GUI.EndScrollView();
  
                 GUI.contentColor = bckup;
+
                 GUIStacks.PopContentColor();
 
             }
 
             }
 
         }
 
         }
Line 405: Line 455:
 
         public int Draw( GUISkin btn_skin, GUISkin drop_down_skin, Rect rect, int selected, object[] options = null )
 
         public int Draw( GUISkin btn_skin, GUISkin drop_down_skin, Rect rect, int selected, object[] options = null )
 
         {
 
         {
             UpdateLayout ( rect, selected, ( options != null ) ? options : m_options );
+
             GUIStacks.PushSkin();
  
            GUISkin skin = GUI.skin;
+
                UpdateLayout ( rect, selected, ( options != null ) ? options : m_options );
  
            DrawSelection( btn_skin );
+
                DrawSelection( btn_skin );
  
            DrawOptions  ( drop_down_skin );
+
                DrawOptions  ( drop_down_skin );
  
             GUI.skin = skin;
+
             GUIStacks.PopSkin();
  
 
             return m_selected;
 
             return m_selected;
Line 434: Line 484:
 
             List< MethodInfo > result = new List< MethodInfo >( 64 );
 
             List< MethodInfo > result = new List< MethodInfo >( 64 );
  
             System.Type        from   = typeof( Application );
+
             System.Type        type   = typeof( Application );
  
             PropertyInfo[]    props  = from.GetProperties( BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static );
+
             PropertyInfo[]    props  = type.GetProperties( BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static );
  
 
             for( int pr = 0; pr < props.Length; ++pr )
 
             for( int pr = 0; pr < props.Length; ++pr )
Line 456: Line 506:
 
         [ RuntimeInitializeOnLoadMethod( RuntimeInitializeLoadType.BeforeSceneLoad ) ] static private void OnBeforeSceneLoadRuntimeMethod()
 
         [ RuntimeInitializeOnLoadMethod( RuntimeInitializeLoadType.BeforeSceneLoad ) ] static private void OnBeforeSceneLoadRuntimeMethod()
 
         {
 
         {
             SceneManager.sceneLoaded += OnSceneLoaded;
+
             if( UnityEngine.Debug.isDebugBuild )
 +
            {
 +
                SceneManager.sceneLoaded += OnSceneLoaded;
  
            Scene  scene      = SceneManager.GetActiveScene();
 
  
            string scene_name = ( scene != null ) ? scene.name : "None";
 
  
            Console.LogSystem( string.Format( "Console init from scene: {0}", scene_name ) );
+
                Scene  scene      = SceneManager.GetActiveScene();
  
 +
                string scene_name = ( scene != null ) ? scene.name : "None";
  
 +
                Console.LogSystem( string.Format( "Console init from scene: {0}", scene_name ) );
  
            List< MethodInfo > properties = GetApplicationProperties();
 
  
            for( int pr = 0; pr < properties.Count; ++pr )
 
            {
 
                MethodInfo  prop  = properties[ pr ];
 
  
                 string      value = prop.Invoke( null, null ).ToString();
+
                 List< MethodInfo > properties = GetApplicationProperties();
  
                 Console.LogSystem( string.Format( "Application.{0}: {1}", prop.Name.Substring( 4 ), value ) );
+
                 for( int pr = 0; pr < properties.Count; ++pr )
 +
                {
 +
                    MethodInfo  prop  = properties[ pr ];
 +
 
 +
                    string      value = prop.Invoke( null, null ).ToString();
 +
 
 +
                    Console.LogSystem( string.Format( "Application.{0}: {1}", prop.Name.Substring( 4 ), value ) );
 +
                }
 
             }
 
             }
 
         }
 
         }
Line 481: Line 536:
 
         //
 
         //
 
         //************************************************************************************************
 
         //************************************************************************************************
 +
       
 
         static private void OnSceneLoaded( Scene scene, LoadSceneMode mode )
 
         static private void OnSceneLoaded( Scene scene, LoadSceneMode mode )
 
         {
 
         {
Line 497: Line 553:
 
               public enum CATEGORY { INFO, WARNING, ERROR, SYSTEM, COUNT }
 
               public enum CATEGORY { INFO, WARNING, ERROR, SYSTEM, COUNT }
  
               private const string STR_HIDE    = "HIDE";
+
               private const string     STR_HIDE    = "HIDE";
                                               
+
                                                     
               private const string STR_SHOW    = "SHOW";
+
               private const string     STR_SHOW    = "SHOW";
 
+
                                       
               private const string STR_SORT_ASC = "RECENT FIRST";
+
               private const string     STR_SORT_ASC = "RECENT FIRST";
 
+
                                       
               private const string STR_SORT_DSC = "OLDER FIRST";
+
               private const string     STR_SORT_DSC = "OLDER FIRST";
 
+
                                       
               private const string STR_CLEAR    = "CLEAR";
+
               private const string     STR_CLEAR    = "CLEAR";
 
+
                                       
               private const int   CLIPING      = 1024;
+
               private const int         CLIPING      = 1024;
  
         static private string[]     SPLIT_SEMS  = new string[ 3 ] { "\r\n", "\r", "\n" };
+
         static private readonly string[] SPLIT_SEMS  = new string[ 3 ] { "\r\n", "\r", "\n" };
  
 
         //************************************************************************************************
 
         //************************************************************************************************
Line 515: Line 571:
 
         //************************************************************************************************
 
         //************************************************************************************************
  
         static private Color  m_colDate = new Color( 0.7f, 0.7f, 0.7f, 1.0f );
+
         static private         Color  m_colDate = new Color( 0.7f, 0.7f, 0.7f, 1.0f );
 +
                               
 +
        static private          Color  m_colInfo = new Color( 1.0f, 1.0f, 1.0f, 1.0f );
 +
                               
 +
        static private          Color  m_colWarn = new Color( 0.8f, 0.8f, 0.4f, 1.0f );
 +
                               
 +
        static private          Color  m_colErr  = new Color( 1.0f, 0.4f, 0.4f, 1.0f );
 +
                               
 +
        static private          Color  m_colSys  = new Color( 0.7f, 0.7f, 1.0f, 1.0f );
  
         static private Color  m_colInfo = new Color( 1.0f, 1.0f, 1.0f, 1.0f );
+
         static private readonly Color[] m_cols    = new Color[ ( int )CATEGORY.COUNT ] { m_colInfo, m_colWarn, m_colErr, m_colSys };
 
+
        static private Color  m_colWarn = new Color( 0.8f, 0.8f, 0.4f, 1.0f );
+
 
+
        static private Color  m_colErr  = new Color( 1.0f, 0.4f, 0.4f, 1.0f );
+
 
+
        static private Color  m_colSys  = new Color( 0.7f, 0.7f, 1.0f, 1.0f );
+
 
+
        static private Color[] m_cols    = new Color[ ( int )CATEGORY.COUNT ] { m_colInfo, m_colWarn, m_colErr, m_colSys };
+
  
 
         //************************************************************************************************
 
         //************************************************************************************************
Line 531: Line 587:
 
         //************************************************************************************************
 
         //************************************************************************************************
  
         static private object   m_lock      = new object();
+
         static private object           m_lock      = new object();
  
         static private Buffer   m_buffer    = new Buffer();
+
         static private readonly Buffer   m_buffer    = new Buffer();
  
         static private Buffer[] m_catBuffers = new Buffer[ ( int )CATEGORY.COUNT ] { new Buffer(), new Buffer(), new Buffer(), new Buffer() };
+
         static private readonly Buffer[] m_catBuffers = new Buffer[ ( int )CATEGORY.COUNT ] { new Buffer(), new Buffer(), new Buffer(), new Buffer() };
  
 
         //************************************************************************************************
 
         //************************************************************************************************
Line 541: Line 597:
 
         //************************************************************************************************
 
         //************************************************************************************************
  
        static private Stack< Color  > m_colStack    = new Stack< Color  >( 32 );
+
         static private EventSystem         m_eventSystem  = null;
                                                     
+
                                                         
        static private Stack< GUISkin > m_sknStack    = new Stack< GUISkin >( 32 );
+
         static private GUISkin             m_skin        = null;
                                                     
+
                                                         
         static private EventSystem     m_eventSystem  = null;
+
         static private GUISkin             m_skin_dd      = null;
                                                     
+
                                           
         static private GUISkin         m_skin        = null;
+
         static private bool                 m_layout_dirty = false;
                                                     
+
                                           
         static private GUISkin         m_skin_dd      = null;
+
         static private Vector2             m_pos          = Vector2.zero;
 
+
                                                         
         static private bool             m_layout_dirty = false;
+
         static private Vector2             m_size        = Vector2.zero;
                                       
+
                                                         
         static private Vector2         m_pos          = Vector2.zero;
+
         static private Vector2             m_headerSize  = Vector2.zero;
                                                     
+
                                                         
         static private Vector2         m_size        = Vector2.zero;
+
         static private Vector2             m_scrollSize  = Vector2.zero;
                                                     
+
                                                         
         static private Vector2         m_headerSize  = Vector2.zero;
+
         static private Vector2             m_scrollPos    = Vector2.zero;
                                                     
+
                                                         
         static private Vector2         m_scrollSize  = Vector2.zero;
+
         static private Rect                 m_globalRect  = default( Rect );
                                                     
+
                                                         
         static private Vector2         m_scrollPos    = Vector2.zero;
+
         static private Rect                 m_headerRect  = default( Rect );
                                                     
+
                                                         
         static private Rect             m_globalRect  = default( Rect );
+
         static private Rect                 m_scrollRect  = default( Rect );
                                                     
+
                                                         
         static private Rect             m_headerRect  = default( Rect );
+
         static private bool                 m_expanded    = true;
                                                     
+
                                                         
         static private Rect             m_scrollRect  = default( Rect );
+
         static private bool                 m_ascending    = true;
                                                     
+
                                                         
         static private bool             m_expanded    = true;
+
         static private CATEGORY             m_filter      = CATEGORY.COUNT;
                                                     
+
                                               
         static private bool             m_ascending    = true;
+
         static private readonly GUIDropDown m_dropDown     = new GUIDropDown( "INFOS", "WARNINGS", "ERRORS", "SYSTEM", "ALL" );
                                                     
+
         static private CATEGORY         m_filter      = CATEGORY.COUNT;
+
                                                     
+
         static private GUIDropDown     m_dropDown   = new GUIDropDown( "INFOS", "WARNINGS", "ERRORS", "SYSTEM", "ALL" );
+
  
 
         //************************************************************************************************
 
         //************************************************************************************************
Line 583: Line 635:
 
         static Console()
 
         static Console()
 
         {
 
         {
 +
            LoadResources();
 +
 
             if( UnityEngine.Debug.isDebugBuild )
 
             if( UnityEngine.Debug.isDebugBuild )
 
             {
 
             {
                LoadResources();
 
 
 
                 Application.logMessageReceivedThreaded += LogCallback;
 
                 Application.logMessageReceivedThreaded += LogCallback;
  
Line 603: Line 655:
 
             if( m_skin_dd == null ) { m_skin_dd = Resources.Load< GUISkin >( "DebugConsoleDropDownSkin" ); }
 
             if( m_skin_dd == null ) { m_skin_dd = Resources.Load< GUISkin >( "DebugConsoleDropDownSkin" ); }
 
         }
 
         }
 
        //************************************************************************************************
 
        //
 
        //************************************************************************************************
 
 
        static private void GUIPushColor()              { m_colStack.Push( GUI.color ); }
 
 
        static private void GUIPopColor ()              { GUI.color = m_colStack.Pop(); }
 
 
        static private void GUIPushSkin ( GUISkin skin ) { m_sknStack.Push( GUI.skin ); GUI.skin = skin; }
 
 
        static private void GUIPopSkin  ()              { GUI.skin = m_sknStack.Pop();  }
 
  
 
         //************************************************************************************************
 
         //************************************************************************************************
Line 683: Line 723:
 
             string[] entries = entry.Split( SPLIT_SEMS, System.StringSplitOptions.RemoveEmptyEntries );
 
             string[] entries = entry.Split( SPLIT_SEMS, System.StringSplitOptions.RemoveEmptyEntries );
  
             foreach( string s in entries ) LogEntry( s, category );
+
             for( int ent = 0; ent < entries.Length; ++ent ) LogEntry( entries[ ent ], category );
 
         }
 
         }
  
Line 692: Line 732:
 
         static private void LogEntry( string entry, CATEGORY category )
 
         static private void LogEntry( string entry, CATEGORY category )
 
         {
 
         {
             lock( m_lock )
+
             if( entry.Length > CLIPING ) entry = entry.Substring( 0, CLIPING );
            {
+
                if( entry.Length > CLIPING ) entry = entry.Substring( 0, CLIPING );
+
  
 +
            string date = System.DateTime.Now.ToString();
  
                string date = System.DateTime.Now.ToString() + ": ";
 
  
 +
            lock( m_lock )
 +
            {
 
                 m_buffer.Push                      ( date, entry, ( int )category );
 
                 m_buffer.Push                      ( date, entry, ( int )category );
  
Line 740: Line 780:
 
         static private void UpdateLayout()
 
         static private void UpdateLayout()
 
         {
 
         {
 +
            if( Event.current.type != EventType.Layout ) return;
 +
 
             Vector2 size = new Vector2( Screen.width * ( Application.isMobilePlatform ? 1.0f : 0.5f ), Screen.height * 0.5f );
 
             Vector2 size = new Vector2( Screen.width * ( Application.isMobilePlatform ? 1.0f : 0.5f ), Screen.height * 0.5f );
  
Line 795: Line 837:
 
                 butRect.x += buttonsW;  
 
                 butRect.x += buttonsW;  
 
                  
 
                  
               
+
 
 
                 m_filter = ( CATEGORY )m_dropDown.Draw( m_skin, m_skin_dd, butRect, ( int )m_filter );
 
                 m_filter = ( CATEGORY )m_dropDown.Draw( m_skin, m_skin_dd, butRect, ( int )m_filter );
 
             }
 
             }
Line 810: Line 852:
 
             const float lineH    =  21.0f;
 
             const float lineH    =  21.0f;
  
             Buffer      buffer  = ( m_filter >= CATEGORY.COUNT ) ? m_buffer : m_catBuffers[ ( int )m_filter ];
+
             Buffer      buffer  = ( ( m_filter < 0 ) || ( m_filter >= CATEGORY.COUNT ) ) ? m_buffer : m_catBuffers[ ( int )m_filter ];
  
 
             int        count    = buffer.count;
 
             int        count    = buffer.count;
Line 817: Line 859:
  
  
            GUIPushColor();
 
  
 
             GUI.Box( m_scrollRect, ( Texture )null );
 
             GUI.Box( m_scrollRect, ( Texture )null );
Line 837: Line 878:
 
                 int  start        = buffer.start;
 
                 int  start        = buffer.start;
  
 +
 +
                GUIStacks.PushColor();
  
 
                 for( int entry = firstVisible; entry <= lastVisible; ++entry )
 
                 for( int entry = firstVisible; entry <= lastVisible; ++entry )
 
                 {
 
                 {
                     dateRect.y = lineRect.y = entry * lineH;
+
                     dateRect.y     = lineRect.y = entry * lineH;
  
                     int   index = ( m_ascending ) ? start + ( ( count - 1 ) - entry ) : start + entry;
+
                     int       index = ( m_ascending ) ? start + ( ( count - 1 ) - entry ) : start + entry;
  
                     Entry Entry = buffer[ index ];
+
                     ref Entry ent  = ref buffer[ index ];
  
                     GUI.color   = m_colDate;                       GUI.Label( dateRect, Entry.date );
+
                     GUI.color       = m_colDate;                     GUI.Label( dateRect, ent.date );
  
                     GUI.color   = m_cols[ ( int )Entry.category ]; GUI.Label( lineRect, Entry.msg  );
+
                     GUI.color       = m_cols[ ( int )ent.category ]; GUI.Label( lineRect, ent.msg  );
 
                 }
 
                 }
            }
 
  
            GUI.EndScrollView();
+
                GUIStacks.PopColor();
 +
            }
  
             GUIPopColor();
+
             GUI.EndScrollView ();
 
         }
 
         }
  
Line 864: Line 907:
 
         {
 
         {
 
             if( UnityEngine.Debug.isDebugBuild == false ) return;
 
             if( UnityEngine.Debug.isDebugBuild == false ) return;
 +
 +
            GUIStacks.PushSkin( m_skin );
  
 
             UpdateLayout();
 
             UpdateLayout();
 
            GUIPushSkin ( m_skin );
 
 
            GUIPushColor();
 
  
 
             if( m_expanded )
 
             if( m_expanded )
Line 876: Line 917:
 
             }
 
             }
 
   
 
   
             DrawHeader ();
+
             DrawHeader();
  
             GUIPopColor();
+
             ConsumeMouseEvents();
  
             GUIPopSkin ();
+
             GUIStacks.PopSkin ();
 
+
            ConsumeMouseEvents();
+
 
         }
 
         }
 
     }
 
     }

Latest revision as of 14:36, 4 May 2020

Author: Serge Billault

Contents

[edit] Description

A more up to date, as of 2020 (Unity 5.6.1f1 through 2019.3.8.f1), a little more professional, minimal debug console.

[edit] Archive File

UnityDebugConsole

[edit] Archive content

A c# script.
A Resources folder containing a GUI skin.

[edit] Usage

. Installation: Unzip the archive content in your Standard Assets folder (required).


. Setup: no action required.


. Scripting: no action required.


. Unity Legacy Compatibility (old VS new Unity Input System): no action required.

[edit] Professionals

. Net compatibility level: 2.0 minimum - no dynamic methods.


. Unsafe code: None.


. Reflectivity: Some.


. Networking: None.


. QA Teams functions support: None.


. Advanced features: None.


. Performances: Suitable for productions (translation for students: High).

[edit] C# - DebugConsole.cs

The script must be named DebugConsole.cs. It has no real use. It is for compliance with other wiki articles only. The script is available through the archive linked at the top of this page.


using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
 
//********************************************************************************************************
//
//********************************************************************************************************
 
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
 
//********************************************************************************************************
//
//********************************************************************************************************
 
namespace DEBUG
{
    //****************************************************************************************************
    //
    //****************************************************************************************************
 
    public enum TRACE { NONE, SHORT, FULL, DEFAULT = NONE }
 
	//****************************************************************************************************
	//
	//****************************************************************************************************
 
	static public class Debug
	{
		//************************************************************************************************
		//
		//************************************************************************************************
 
		private const string FMT_TRACE  = "  * {0} : line {1} : {2}()\r\n";
 
		private const char   MEMBER_SEP = '.';
 
		//************************************************************************************************
		//
		//************************************************************************************************
 
		static private string QualifiedMethodName( StackFrame frame )
		{
			return frame.GetMethod().DeclaringType.Name + MEMBER_SEP + frame.GetMethod().Name;
		}
 
		//************************************************************************************************
		//
		//************************************************************************************************
 
		static private string Msg( string msg, TRACE trace = TRACE.DEFAULT )
		{
			switch( trace )
			{
				//****************************************************************************************
				//
				//****************************************************************************************
 
				case TRACE.FULL:
				{
					StackTrace   full   = new StackTrace ( 2 , true );
 
					StackFrame[] frames = full.GetFrames();
 
					string       output = string.Empty;
 
					for( int frame = frames.Length - 1; frame >= 0; --frame )
					{
						StackFrame Frame = frames[ frame ];
 
						output += string.Format( FMT_TRACE, Frame.GetFileName(), Frame.GetFileLineNumber(), QualifiedMethodName( Frame ) );
					}
 
					return output + msg;
				}
 
				//****************************************************************************************
				//
				//****************************************************************************************
 
				case TRACE.SHORT:
				{
					StackTrace stack = new StackTrace( 2 , true );
 
					StackFrame Frame = stack.GetFrame( 0 );
 
					return string.Format( FMT_TRACE, Frame.GetFileName(), Frame.GetFileLineNumber(), QualifiedMethodName( Frame ) ) + msg;
				}
 
				//****************************************************************************************
				//
				//****************************************************************************************
 
				default :
				{
					return msg;
				}
			}
		}
 
		//************************************************************************************************
		//
		//************************************************************************************************
 
		static public void Log       ( object msg, TRACE trace = TRACE.DEFAULT ) { if( UnityEngine.Debug.isDebugBuild ) UnityEngine.Debug.Log       ( Msg( msg.ToString(), trace ) ); }
 
		static public void LogWarning( object msg, TRACE trace = TRACE.DEFAULT ) { if( UnityEngine.Debug.isDebugBuild ) UnityEngine.Debug.LogWarning( Msg( msg.ToString(), trace ) ); }
 
		static public void LogError  ( object msg, TRACE trace = TRACE.DEFAULT ) { if( UnityEngine.Debug.isDebugBuild ) UnityEngine.Debug.LogError  ( Msg( msg.ToString(), trace ) ); }
 
		//************************************************************************************************
		//
		//************************************************************************************************
 
		static public void Log       ( object msg, UnityEngine.Object context, TRACE trace = TRACE.DEFAULT ) { if( UnityEngine.Debug.isDebugBuild ) UnityEngine.Debug.Log       ( Msg( msg.ToString(), trace ), context ); }
 
		static public void LogWarning( object msg, UnityEngine.Object context, TRACE trace = TRACE.DEFAULT ) { if( UnityEngine.Debug.isDebugBuild ) UnityEngine.Debug.LogWarning( Msg( msg.ToString(), trace ), context ); }
 
		static public void LogError  ( object msg, UnityEngine.Object context, TRACE trace = TRACE.DEFAULT ) { if( UnityEngine.Debug.isDebugBuild ) UnityEngine.Debug.LogError  ( Msg( msg.ToString(), trace ), context ); }
	}
 
    //****************************************************************************************************
    //
    //****************************************************************************************************
 
	public struct Entry
	{
		public int    category;
 
		public string date;
 
		public string msg;
	}
 
    //****************************************************************************************************
    //
    //****************************************************************************************************
 
    public class Buffer
    {
        //************************************************************************************************
        //
        //************************************************************************************************
 
        private Entry[] m_entries  = null;
 
        private int     m_capacity = 0;
 
        private int     m_start    = 0;
 
        private int     m_end      = 0;
 
        private int     m_count    = 0;
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        public int capacity { get { return m_capacity; } }
 
        public int start    { get { return m_start;    } }
 
        public int end      { get { return m_end;      } }
 
        public int count    { get { return m_count;    } }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static public ulong Align( ulong s, ulong a ) { return ( s + ( a - 1 ) ) & ~( a - 1 ); }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        public Buffer( int paramCapacity = 0 )
        {
            m_capacity = ( paramCapacity != 0 ) ? ( int )Align( ( ulong )Mathf.Max( 4, paramCapacity ), 4 ) : 1024;
 
            m_entries  = new Entry[ m_capacity ];
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        public void Clear( )
        {
            m_start = 0;
 
            m_end   = 0;
 
            m_count = 0;
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        public void Push( string date, string msg, int category )
        {
            if( m_count < m_capacity ) ++m_count;
 
            else      m_start = ( m_start + 1 )   & ( m_capacity - 1 );
 
            m_end = ( m_start + ( m_count - 1 ) ) & ( m_capacity - 1 );
 
 
            ref Entry e = ref m_entries[ m_end ];
 
            e.category  = category;
 
            e.date      = date;
 
            e.msg       = msg;
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        public ref Entry this[ int index ] { get { return ref m_entries[ index & ( m_capacity - 1 ) ]; } }
    }
 
    //****************************************************************************************************
    //
    //****************************************************************************************************
 
    static public class GUIStacks
    {
        static private readonly Stack< GUISkin > m_skin  = new Stack< GUISkin >( 16 );
 
        static private readonly Stack< Color   > m_color = new Stack< Color   >( 16 );
 
        static private readonly Stack< Color   > m_cont  = new Stack< Color   >( 16 );
 
        static private readonly Stack< Color   > m_bgnd  = new Stack< Color   >( 16 );
 
 
        static public void PushSkin           ( GUISkin skin  ) { m_skin.Push ( GUI.skin );            GUI.skin            = skin;  }
 
        static public void PushColor          ( Color   color ) { m_color.Push( GUI.color );           GUI.color           = color; }
 
        static public void PushContentColor   ( Color   color ) { m_cont.Push ( GUI.contentColor );    GUI.contentColor    = color; }
 
        static public void PushBackgroundColor( Color   color ) { m_bgnd.Push ( GUI.backgroundColor ); GUI.backgroundColor = color; }
 
        static public void PushSkin           () { m_skin.Push ( GUI.skin );            }
 
        static public void PushColor          () { m_color.Push( GUI.color );           }
 
        static public void PushContentColor   () { m_cont.Push ( GUI.contentColor );    }
 
        static public void PushBackgroundColor() { m_bgnd.Push ( GUI.backgroundColor ); }
 
        static public void PopSkin            () { GUI.skin            = m_skin.Pop (); }
 
        static public void PopColor           () { GUI.color           = m_color.Pop(); }
 
        static public void PopContentColor    () { GUI.contentColor    = m_cont.Pop (); }
 
        static public void PopBackgroundColor () { GUI.backgroundColor = m_bgnd.Pop (); }
    }
 
    //****************************************************************************************************
    //
    //****************************************************************************************************
 
    public class GUIDropDown
    {
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private Color COLOR_NORMAL   = new Color( 0.6f, 0.6f, 0.6f, 1.0f );
 
        static private Color COLOR_SELECTED = new Color( 1.0f, 1.0f, 1.0f, 1.0f );
 
        private const int    MAX_LINES      = 8;
 
        private const float  LINE_H         = 32.0f;
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        private Rect     m_selectionRect = default( Rect );
 
        private Rect     m_scrollRect    = default( Rect );
 
        private Rect     m_viewRect      = default( Rect );
 
        private object[] m_options       = null;
 
        private int      m_nbOptions     = 0;
 
        private int      m_nbDisplayed   = 0;
 
        private bool     m_expanded      = false;
 
        private Vector2  m_scroll        = Vector2.zero;
 
        private int      m_selected      = 0;
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        public GUIDropDown( params object[] options )
        {
            m_options = options;
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        private void UpdateLayout( Rect rect, int selected, object[] options )
        {
            if( Event.current.type != EventType.Layout ) return;
 
            m_options       = options;
 
            m_nbOptions     = ( options != null ) ? options.Length : 0;
 
            m_nbDisplayed   = Mathf.Min( m_nbOptions, MAX_LINES );
 
            m_selected      = Mathf.Min( m_nbOptions - 1, selected );
 
            m_selectionRect = rect;
 
 
            if( m_expanded )
            {
                if( m_nbOptions > 0 )
                {
                    m_scrollRect = new Rect ( rect.x, rect.y + rect.height, rect.width, m_nbDisplayed * LINE_H );
 
                    m_viewRect   = new Rect ( 0.0f, 0.0f, m_scrollRect.width - ( m_nbDisplayed < m_nbOptions ? 15.0f : 0.0f ), m_nbOptions * LINE_H );
                }
                else
                {
                    m_expanded = false;
                }
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        private void DrawOptions( GUISkin skin )
        {
            if( m_expanded )
            {
                GUI.skin = skin;
 
                int firstVisible = ( int )( m_scroll.y / LINE_H );
 
                int lastVisible  = Mathf.Clamp( firstVisible + m_nbDisplayed, 0, m_nbOptions - 1 );
 
 
                GUIStacks.PushContentColor();
 
                m_scroll = GUI.BeginScrollView( m_scrollRect, m_scroll, m_viewRect, false, false );
 
                for( int option = firstVisible; option <= lastVisible; ++option )
                {
                    GUI.contentColor = ( option == m_selected ) ? COLOR_SELECTED : COLOR_NORMAL;
 
                    if( GUI.Button( new Rect( 0.0f, option * LINE_H, m_viewRect.width, LINE_H ), m_options[ option ].ToString() ) )
                    {
                        m_selected = option;
 
                        m_expanded = false;
                    }
                }
 
                GUI.EndScrollView();
 
                GUIStacks.PopContentColor();
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        private void DrawSelection( GUISkin skin )
        {
            GUI.skin = skin;
 
            object selection    = ( m_nbOptions  >  0 ) ? m_options[ m_selected ]  : null;
 
            string selectionStr = ( selection != null ) ? selection.ToString() : "<NULL>";
 
            if( GUI.Button( m_selectionRect, selectionStr ) && ( m_nbOptions > 0 ) )
            {
                m_expanded = ! m_expanded;
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        public int Draw( GUISkin btn_skin, GUISkin drop_down_skin, Rect rect, int selected, object[] options = null )
        {
            GUIStacks.PushSkin();
 
                UpdateLayout ( rect, selected, ( options != null ) ? options : m_options );
 
                DrawSelection( btn_skin );
 
                DrawOptions  ( drop_down_skin );
 
            GUIStacks.PopSkin();
 
            return m_selected;
        }
    }
 
    //****************************************************************************************************
    //
    //****************************************************************************************************
 
    static public class Console
    {
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static public List< MethodInfo > GetApplicationProperties()
        {
            List< MethodInfo > result = new List< MethodInfo >( 64 );
 
            System.Type        type   = typeof( Application );
 
            PropertyInfo[]     props  = type.GetProperties( BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static );
 
            for( int pr = 0; pr < props.Length; ++pr )
            {
                PropertyInfo prop = props[ pr ];
 
                MethodInfo   func = prop.GetGetMethod();
 
                if( func != null ) result.Add( func );
            }
 
            return result;
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        [ RuntimeInitializeOnLoadMethod( RuntimeInitializeLoadType.BeforeSceneLoad ) ] static private void OnBeforeSceneLoadRuntimeMethod()
        {
            if( UnityEngine.Debug.isDebugBuild )
            {
                SceneManager.sceneLoaded += OnSceneLoaded;
 
 
 
                Scene  scene      = SceneManager.GetActiveScene();
 
                string scene_name = ( scene != null ) ? scene.name : "None";
 
                Console.LogSystem( string.Format( "Console init from scene: {0}", scene_name ) );
 
 
 
                List< MethodInfo > properties = GetApplicationProperties();
 
                for( int pr = 0; pr < properties.Count; ++pr )
                {
                    MethodInfo  prop  = properties[ pr ];
 
                    string      value = prop.Invoke( null, null ).ToString();
 
                    Console.LogSystem( string.Format( "Application.{0}: {1}", prop.Name.Substring( 4 ), value ) );
                }
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void OnSceneLoaded( Scene scene, LoadSceneMode mode )
        {
            if( DebugConsole.Instance == null )
            {
                GameObject console = new GameObject( "DebugConsole", typeof( DebugConsole ) );
 
                if( console != null ) UnityEngine.Object.DontDestroyOnLoad( console );
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
               public enum CATEGORY { INFO, WARNING, ERROR, SYSTEM, COUNT }
 
               private const string      STR_HIDE     = "HIDE";
 
               private const string      STR_SHOW     = "SHOW";
 
               private const string      STR_SORT_ASC = "RECENT FIRST";
 
               private const string      STR_SORT_DSC = "OLDER FIRST";
 
               private const string      STR_CLEAR    = "CLEAR";
 
               private const int         CLIPING      = 1024;
 
        static private readonly string[] SPLIT_SEMS   = new string[ 3 ] { "\r\n", "\r", "\n" };
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private          Color   m_colDate = new Color( 0.7f, 0.7f, 0.7f, 1.0f );
 
        static private          Color   m_colInfo = new Color( 1.0f, 1.0f, 1.0f, 1.0f );
 
        static private          Color   m_colWarn = new Color( 0.8f, 0.8f, 0.4f, 1.0f );
 
        static private          Color   m_colErr  = new Color( 1.0f, 0.4f, 0.4f, 1.0f );
 
        static private          Color   m_colSys  = new Color( 0.7f, 0.7f, 1.0f, 1.0f );
 
        static private readonly Color[] m_cols    = new Color[ ( int )CATEGORY.COUNT ] { m_colInfo, m_colWarn, m_colErr, m_colSys };
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private object            m_lock       = new object();
 
        static private readonly Buffer   m_buffer     = new Buffer();
 
        static private readonly Buffer[] m_catBuffers = new Buffer[ ( int )CATEGORY.COUNT ] { new Buffer(), new Buffer(), new Buffer(), new Buffer() };
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private EventSystem          m_eventSystem  = null;
 
        static private GUISkin              m_skin         = null;
 
        static private GUISkin              m_skin_dd      = null;
 
        static private bool                 m_layout_dirty = false;
 
        static private Vector2              m_pos          = Vector2.zero;
 
        static private Vector2              m_size         = Vector2.zero;
 
        static private Vector2              m_headerSize   = Vector2.zero;
 
        static private Vector2              m_scrollSize   = Vector2.zero;
 
        static private Vector2              m_scrollPos    = Vector2.zero;
 
        static private Rect                 m_globalRect   = default( Rect );
 
        static private Rect                 m_headerRect   = default( Rect );
 
        static private Rect                 m_scrollRect   = default( Rect );
 
        static private bool                 m_expanded     = true;
 
        static private bool                 m_ascending    = true;
 
        static private CATEGORY             m_filter       = CATEGORY.COUNT;
 
        static private readonly GUIDropDown m_dropDown     = new GUIDropDown( "INFOS", "WARNINGS", "ERRORS", "SYSTEM", "ALL" );
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static Console()
        {
            LoadResources();
 
            if( UnityEngine.Debug.isDebugBuild )
            {
                Application.logMessageReceivedThreaded += LogCallback;
 
                Application.lowMemory                  += LowMemoryCallback;
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void LoadResources()
        {
            if( m_skin    == null ) { m_skin    = Resources.Load< GUISkin >( "DebugConsoleSkin"         ); }
 
            if( m_skin_dd == null ) { m_skin_dd = Resources.Load< GUISkin >( "DebugConsoleDropDownSkin" ); }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void Clear()
        {
            lock( m_lock )
            {
                m_buffer.Clear();
 
                for( int buf = 0; buf < m_catBuffers.Length; ++buf ) m_catBuffers[ buf ].Clear();
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void LowMemoryCallback()
        {
            Log( "LOW MEMORY", CATEGORY.ERROR );
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void LogCallback( string condition, string stackTrace, LogType type )
        {
            bool   trace = ( type != LogType.Log );
 
            string entry = ( trace ) ? stackTrace + condition : condition;
 
            if     ( type == LogType.Log     ) Log( entry, CATEGORY.INFO    );
 
            else if( type == LogType.Warning ) Log( entry, CATEGORY.WARNING );
 
            else                               Log( entry, CATEGORY.ERROR   );
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static public void LogInfo   ( string entry ) { Log( entry, CATEGORY.INFO    ); }
 
        static public void LogWarning( string entry ) { Log( entry, CATEGORY.WARNING ); }
 
        static public void LogError  ( string entry ) { Log( entry, CATEGORY.ERROR   ); }
 
        static public void LogSystem ( string entry ) { Log( entry, CATEGORY.SYSTEM  ); }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static public void Log( string entry, CATEGORY category = CATEGORY.INFO )
        {
            if( UnityEngine.Debug.isDebugBuild == false ) return;
 
            if( string.IsNullOrEmpty( entry ) )           return;
 
            if( category >= CATEGORY.COUNT    )           return;
 
 
            string[] entries = entry.Split( SPLIT_SEMS, System.StringSplitOptions.RemoveEmptyEntries );
 
            for( int ent = 0; ent < entries.Length; ++ent ) LogEntry( entries[ ent ], category );
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void LogEntry( string entry, CATEGORY category )
        {
            if( entry.Length > CLIPING ) entry = entry.Substring( 0, CLIPING );
 
            string date = System.DateTime.Now.ToString();
 
 
            lock( m_lock )
            {
                m_buffer.Push                       ( date, entry, ( int )category );
 
                m_catBuffers[ ( int )category ].Push( date, entry, ( int )category );
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void ConsumeMouseEvents()
        {
            #if ENABLE_INPUT_SYSTEM
 
                Vector2 pointerPos = UnityEngine.InputSystem.Pointer.current.position.ReadValue();
 
            #else
 
                Vector2 pointerPos = Input.mousePosition;
 
            #endif
 
 
            Vector2 mousePos = new Vector2( pointerPos.x, Screen.height - pointerPos.y );
 
            bool    focused  = m_globalRect.Contains( mousePos );
 
            if( ( Event.current.isMouse ) && ( focused ) ) Event.current.Use();
 
 
            if( EventSystem.current != null ) m_eventSystem         = EventSystem.current;
 
            if( m_eventSystem       != null ) m_eventSystem.enabled = ( focused == false );
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void UpdateLayout()
        {
            if( Event.current.type != EventType.Layout ) return;
 
            Vector2 size = new Vector2( Screen.width * ( Application.isMobilePlatform ? 1.0f : 0.5f ), Screen.height * 0.5f );
 
            if( ( m_size != size ) || ( m_layout_dirty ) )
            {
                m_size         = size;
 
                m_layout_dirty = false;
 
 
                m_headerSize   = new Vector2( m_size.x, 24.0f );
 
                m_scrollSize   = new Vector2( m_size.x, ( m_expanded ) ? ( m_size.y - m_headerSize.y ) : 0.0f );
 
                m_pos          = new Vector2( 0.0f,     ( m_expanded ) ? ( Screen.height - m_size.y  ) : Screen.height - m_headerSize.y );
 
 
                m_headerRect   = new Rect   ( m_pos.x, m_pos.y,                  m_headerSize.x, m_headerSize.y );
 
                m_scrollRect   = new Rect   ( m_pos.x, m_pos.y + m_headerSize.y, m_scrollSize.x, m_scrollSize.y );
 
                m_globalRect   = new Rect   ( m_pos.x, m_pos.y, m_size.x, m_size.y );
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void DrawHeader()
        {
            const int nbButtons = 5;
 
            float     buttonsW  = m_headerSize.x / nbButtons;
 
            Rect      butRect   = new Rect( m_headerRect.x, m_headerRect.y, buttonsW, m_headerSize.y );
 
 
            if( GUI.Button( butRect, ( m_expanded ) ? STR_HIDE : STR_SHOW  ) ) 
            { 
                m_expanded = ( ! m_expanded ); 
 
                m_layout_dirty = true; 
            }
 
 
            if( m_expanded )
            {
                butRect.x += buttonsW; if( GUI.Button( butRect, STR_SORT_ASC ) ) m_ascending =  true;
 
                butRect.x += buttonsW; if( GUI.Button( butRect, STR_SORT_DSC ) ) m_ascending = false;
 
                butRect.x += buttonsW; if( GUI.Button( butRect, STR_CLEAR    ) ) Clear(); 
 
                butRect.x += buttonsW; 
 
 
                m_filter = ( CATEGORY )m_dropDown.Draw( m_skin, m_skin_dd, butRect, ( int )m_filter );
            }
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static private void DrawMessages()
        {
            const float dateW    = 140.0f;
 
            const float lineH    =  21.0f;
 
            Buffer      buffer   = ( ( m_filter < 0 ) || ( m_filter >= CATEGORY.COUNT ) ) ? m_buffer : m_catBuffers[ ( int )m_filter ];
 
            int         count    = buffer.count;
 
            Rect        viewRect = new Rect( 0.0f,  0.0f, Screen.width, lineH * count );
 
 
 
            GUI.Box( m_scrollRect, ( Texture )null );
 
            m_scrollPos = GUI.BeginScrollView( m_scrollRect, m_scrollPos, viewRect, false, true );
 
            if( Event.current.type == EventType.Repaint )
            {
                Rect  dateRect     = new Rect( 0.0f,  0.0f,                  dateW, lineH );
 
                Rect  lineRect     = new Rect( dateW, 0.0f, viewRect.width - dateW, lineH );
 
                int   maxVisibles  = ( int )( m_scrollRect.height / lineH ) + 1;
 
                int   firstVisible = ( int )( m_scrollPos.y       / lineH );
 
                int   lastVisible  = firstVisible + ( Mathf.Min( maxVisibles, count ) - 1 );
 
                int   start        = buffer.start;
 
 
                GUIStacks.PushColor();
 
                for( int entry = firstVisible; entry <= lastVisible; ++entry )
                {
                    dateRect.y      = lineRect.y = entry * lineH;
 
                    int       index = ( m_ascending ) ? start + ( ( count - 1 ) - entry ) : start + entry;
 
                    ref Entry ent   = ref buffer[ index ];
 
                    GUI.color       = m_colDate;                     GUI.Label( dateRect, ent.date );
 
                    GUI.color       = m_cols[ ( int )ent.category ]; GUI.Label( lineRect, ent.msg  );
                }
 
                GUIStacks.PopColor();
            }
 
            GUI.EndScrollView ();
        }
 
        //************************************************************************************************
        //
        //************************************************************************************************
 
        static public void OnGUI()
        {
            if( UnityEngine.Debug.isDebugBuild == false ) return;
 
            GUIStacks.PushSkin( m_skin );
 
            UpdateLayout();
 
            if( m_expanded )
            {
                lock( m_lock ) { DrawMessages(); }
            }
 
            DrawHeader();
 
            ConsumeMouseEvents();
 
            GUIStacks.PopSkin ();
        }
    }
 
    //****************************************************************************************************
    //
    //****************************************************************************************************
 
    public class DebugConsole : MonoBehaviour
    {
        static public DebugConsole Instance { get; private set; }
 
        private void Awake    () { if( Instance == null ) Instance = this; }
 
        private void OnDestroy() { if( Instance == this ) Instance = null; }
 
        public void  OnGUI    () { if( Instance == this ) DEBUG.Console.OnGUI(); }
    }
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox