2020/DebugConsole

From Unify Community Wiki
Jump to: navigation, search

Author: Serge Billault

Contents

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.

Archive File

UnityDebugConsole

Archive content

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

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.

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).

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