Unity中實現自定義調試器

一:前言

在Unity編輯器下能夠查看log輸出、內存信息、系統信息等,但發佈到移動端不少信息都沒法查看,這個自定義的調試器能夠查看一些遊戲信息,方便調試
編輯器


二:使用

——在遊戲啓動時調用Debugger.Ins.OpenDebugger()開啓調試器ide


三:代碼

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;

/// <summary>
/// 調試器
/// </summary>
public class Debugger : MonoBehaviour
{
    private static Debugger _instance;
    public static Debugger Ins
    {
        get
        {
            if (_instance == null)
            {
                var go = new GameObject(typeof(Debugger).ToString());
                GameObject.DontDestroyOnLoad(go);
                _instance = go.AddComponent<Debugger>();
            }
            return _instance;
        }
    }

    bool showDebugger;//是否顯示調試器

    int windowTypeIndex;//窗口類型下標
    //窗口類型字符串
    string[] debuggerTypeStr = new string[]
    {
        "Log",       //Log
        "Memory",    //內存
        "Screen",    //屏幕
        "System",    //系統
        "Environment"//環境
    };

    GUIStyle titleStyle;//標題樣式
    GUIStyle logStyle_selected;//log被選擇的樣式
    GUIStyle logStyle_info;//log樣式(info)
    GUIStyle logStyle_warning;//log樣式(warning)
    GUIStyle logStyle_error;//log樣式(error)
    GUIStyle stackStyle;//堆棧樣式

    /// <summary>
    /// 打開調試器
    /// </summary>
    public void OpenDebugger()
    {

    }

    public void Awake()
    {
        InitStyle();

        Application.logMessageReceived += LogCallBack;
    }

    private void OnDestroy()
    {
        Application.logMessageReceived -= LogCallBack;
    }

    /// <summary>
    /// 初始化樣式
    /// </summary>
    void InitStyle()
    {
        titleStyle = new GUIStyle();
        titleStyle.fontSize = 30;
        titleStyle.fontStyle = FontStyle.Bold;
        titleStyle.alignment = TextAnchor.MiddleCenter;
        titleStyle.normal.textColor = Color.cyan;

        logStyle_selected = new GUIStyle();
        logStyle_selected.fontSize = 24;
        logStyle_selected.normal.textColor = Color.cyan;

        logStyle_info = new GUIStyle();
        logStyle_info.fontSize = 24;
        logStyle_info.normal.textColor = Color.white;

        logStyle_warning = new GUIStyle();
        logStyle_warning.fontSize = 24;
        logStyle_warning.normal.textColor = Color.yellow;

        logStyle_error = new GUIStyle();
        logStyle_error.fontSize = 24;
        logStyle_error.normal.textColor = Color.red;

        stackStyle = new GUIStyle();
        stackStyle.fontSize = 24;
    }

    private void OnGUI()
    {
        if (showDebugger)
        {
            DrawDebugger();
        }
        else
        {
            if (GUILayout.Button("調試器", GUILayout.Width(100), GUILayout.Height(100)))
            {
                showDebugger = true;
            }
        }
    }

    /// <summary>
    /// 繪製調試器
    /// </summary>
    void DrawDebugger()
    {
        GUILayout.BeginVertical("box", GUILayout.MinWidth(Screen.width));

        GUILayout.Label("調試器", titleStyle);

        GUILayout.BeginVertical("box");
        windowTypeIndex = GUILayout.Toolbar(windowTypeIndex, debuggerTypeStr, GUILayout.MinWidth(Screen.width / 10), GUILayout.MinHeight(Screen.height / 20));
        if (windowTypeIndex == 0)
        {
            //繪製Log窗口
            DrawLogWindow();
        }
        else if (windowTypeIndex == 1)
        {
            //繪製內存窗口
            DrawMemoryWindow();
        }
        else if (windowTypeIndex == 2)
        {
            //繪製屏幕窗口
            DrawScreenWindow();
        }
        else if (windowTypeIndex == 3)
        {
            //繪製系統窗口
            DrawSystemWindow();
        }
        else if (windowTypeIndex == 4)
        {
            //繪製環境窗口
            DrawEnviromentWindow();
        }
        GUILayout.EndVertical();
        GUILayout.Space(10);

        GUILayout.BeginVertical("box");
        if (GUILayout.Button("Hide", GUILayout.MinWidth(Screen.width / 10), GUILayout.MinHeight(Screen.height / 20)))
        {
            showDebugger = false;
        }
        if (GUILayout.Button("Close", GUILayout.MinWidth(Screen.width / 10), GUILayout.MinHeight(Screen.height / 20)))
        {
            Destroy(gameObject);
            _instance = null;
        }
        GUILayout.EndVertical();

        GUILayout.EndVertical();
    }

    #region Log

    //Log信息
    public class LogInfo
    {
        public int uid;
        public string condition;
        public string stackTrace;
        public LogType logType;
        public DateTime logTime;
    }

    List<LogInfo> logCache = new List<LogInfo>();//全部Log
    LogInfo curLog;//當前選擇的Log
    Vector2 logScrollPos;//log區域滑動條位置
    Vector2 stackScrollPos;//堆棧區域滑動條位置
    int logCounter;//log計數器
    int infoCounter;//info計數器
    int warningCounter;//warning計數器
    int errorCounter;//error計數器
    bool showInfo = true;//顯示info
    bool showWarning = true;//顯示warning
    bool showError = true;//顯示error
    const float scrollSpeed = 0.02f;//滑動條速度

    void LogCallBack(string condition, string stackTrace, LogType logType)
    {
        LogInfo logInfo = new LogInfo();
        logInfo.uid = ++logCounter;
        logInfo.condition = condition;
        logInfo.stackTrace = stackTrace;
        logInfo.logType = logType;
        if (logType == LogType.Error
            || logType == LogType.Exception
            || logType == LogType.Assert)
        {
            errorCounter++;
        }
        else if (logType == LogType.Warning)
        {
            warningCounter++;
        }
        else
        {
            infoCounter++;
        }
        logInfo.logTime = DateTime.Now;
        logCache.Add(logInfo);
    }

    /// <summary>
    /// 繪製Log窗口
    /// </summary>
    void DrawLogWindow()
    {
        GUILayout.BeginHorizontal();
        GUILayout.BeginVertical("box");

        //頂部區域
        GUILayout.BeginHorizontal("box");
        showInfo = GUILayout.Toggle(showInfo, string.Format("info [{0}]", infoCounter));
        showWarning = GUILayout.Toggle(showWarning, string.Format("warning [{0}]", warningCounter));
        showError = GUILayout.Toggle(showError, string.Format("error [{0}]", errorCounter));
        if (GUILayout.Button("Clear", GUILayout.MinWidth(Screen.width / 20), GUILayout.MinHeight(Screen.height / 40)))
        {
            curLog = null;
            infoCounter = 0;
            warningCounter = 0;
            errorCounter = 0;
            logCache.Clear();
        }
        GUILayout.EndHorizontal();

        //Log區域
        GUILayout.BeginVertical("box");
        logScrollPos = GUILayout.BeginScrollView(logScrollPos, GUILayout.MinWidth(Screen.width * 0.8f), GUILayout.MinHeight(Screen.height * 0.3f));
        foreach (var log in logCache)
        {
            if (!IsShowLog(log.logType)) continue;

            string infoStr = string.Format("[{0}] {1}", log.logTime.ToString("HH:mm:ss"), log.condition);
            if (curLog == null)
            {
                if (GUILayout.Button(infoStr, GetStyle(log.logType)))
                {
                    curLog = log;
                }
            }
            else
            {
                if (curLog.uid == log.uid)
                {
                    if (GUILayout.Button(infoStr, logStyle_selected))
                    {

                    }
                }
                else
                {
                    if (GUILayout.Button(infoStr, GetStyle(log.logType)))
                    {
                        curLog = log;
                    }
                }
            }
        }
        GUILayout.EndScrollView();
        GUILayout.EndVertical();
        GUILayout.EndVertical();
        GUILayout.BeginVertical();
        GUILayout.FlexibleSpace();
        if (GUILayout.RepeatButton("Up", GUILayout.MinWidth(100), GUILayout.MinHeight(50)))
        {
            logScrollPos += Screen.height * scrollSpeed * Vector2.down;
        }
        GUILayout.Space(10);
        GUILayout.BeginHorizontal();
        if (GUILayout.RepeatButton("Left", GUILayout.MinWidth(50), GUILayout.MinHeight(100)))
        {
            logScrollPos += Screen.width * scrollSpeed * Vector2.left;
        }
        if (GUILayout.RepeatButton("Right", GUILayout.MinWidth(50), GUILayout.MinHeight(100)))
        {
            logScrollPos += Screen.width * scrollSpeed * Vector2.right;
        }
        GUILayout.EndHorizontal();
        GUILayout.Space(10);
        if (GUILayout.RepeatButton("Down", GUILayout.MinWidth(100), GUILayout.MinHeight(50)))
        {
            logScrollPos += Screen.height * scrollSpeed * Vector2.up;
        }
        GUILayout.FlexibleSpace();
        GUILayout.EndVertical();
        GUILayout.EndHorizontal();

        //堆棧區域
        if (curLog == null) return;
        GUILayout.BeginHorizontal();
        GUILayout.BeginVertical("box");
        stackScrollPos = GUILayout.BeginScrollView(stackScrollPos, GUILayout.MinWidth(Screen.width * 0.8f), GUILayout.MinHeight(Screen.height * 0.3f));
        string logStr = string.Format("[{0}] {1}", curLog.logTime.ToString("HH:mm:ss"), curLog.condition);
        string stackStr = curLog.stackTrace;
        GUILayout.Label(logStr);
        GUILayout.Space(10);
        GUILayout.Label(stackStr);
        GUILayout.EndScrollView();
        GUILayout.EndVertical();
        GUILayout.BeginVertical();
        GUILayout.FlexibleSpace();
        if (GUILayout.RepeatButton("Up", GUILayout.MinWidth(100), GUILayout.MinHeight(50)))
        {
            stackScrollPos += Screen.height * scrollSpeed * Vector2.down;
        }
        GUILayout.Space(10);
        GUILayout.BeginHorizontal();
        if (GUILayout.RepeatButton("Left", GUILayout.MinWidth(50), GUILayout.MinHeight(100)))
        {
            stackScrollPos += Screen.width * scrollSpeed * Vector2.left;
        }
        if (GUILayout.RepeatButton("Right", GUILayout.MinWidth(50), GUILayout.MinHeight(100)))
        {
            stackScrollPos += Screen.width * scrollSpeed * Vector2.right;
        }
        GUILayout.EndHorizontal();
        GUILayout.Space(10);
        if (GUILayout.RepeatButton("Down", GUILayout.MinWidth(100), GUILayout.MinHeight(50)))
        {
            stackScrollPos += Screen.height * scrollSpeed * Vector2.up;
        }
        GUILayout.FlexibleSpace();
        GUILayout.EndVertical();
        GUILayout.EndHorizontal();
    }

    /// <summary>
    /// 獲得樣式
    /// </summary>
    GUIStyle GetStyle(LogType logType)
    {
        switch (logType)
        {
            case LogType.Error:
            case LogType.Exception:
            case LogType.Assert:
                return logStyle_error;
            case LogType.Warning:
                return logStyle_warning;
            case LogType.Log:
                return logStyle_info;
            default:
                return logStyle_info;
        }
    }

    /// <summary>
    /// 是否顯示此類型的Log
    /// </summary>
    bool IsShowLog(LogType logType)
    {
        switch (logType)
        {
            case LogType.Error:
            case LogType.Exception:
            case LogType.Assert:
                return showError;
            case LogType.Warning:
                return showWarning;
            case LogType.Log:
                return showInfo;
            default:
                return showInfo;
        }
    }

    #endregion

    #region Memory

    /// <summary>
    /// 繪製內存窗口
    /// </summary>
    void DrawMemoryWindow()
    {
        GUILayout.BeginVertical("Box");
        GUILayout.Label("總內存:" + Profiler.GetTotalReservedMemoryLong() / 1000000 + "MB");
        GUILayout.Label("已佔用內存:" + Profiler.GetTotalAllocatedMemoryLong() / 1000000 + "MB");
        GUILayout.Label("空閒中內存:" + Profiler.GetTotalUnusedReservedMemoryLong() / 1000000 + "MB");
        GUILayout.Label("總Mono堆內存:" + Profiler.GetMonoHeapSizeLong() / 1000000 + "MB");
        GUILayout.Label("已佔用Mono堆內存:" + Profiler.GetMonoUsedSizeLong() / 1000000 + "MB");
        GUILayout.EndVertical();
    }

    #endregion

    #region Screen

    float FPS;//幀率
    float updateFpsInterval = 1;//更新幀率的間隔
    float fpsCounter;//fps計數器
    float lastUpdateFpsTime;//上一次更新幀率的時間

    private void Update()
    {
        fpsCounter++;
        if (Time.realtimeSinceStartup - lastUpdateFpsTime >= updateFpsInterval)
        {
            FPS = fpsCounter / updateFpsInterval;
            lastUpdateFpsTime = Time.realtimeSinceStartup;
            fpsCounter = 0;
        }
    }

    /// <summary>
    /// 繪製屏幕窗口
    /// </summary>
    void DrawScreenWindow()
    {
        GUILayout.BeginVertical("Box");
        GUILayout.Label(string.Format("<color={0}>FPS:{1:F1}</color>",
           FPS < 30 ? "#FF0000" : "#00FF00",
           FPS));
        GUILayout.Label("DPI:" + Screen.dpi);
        GUILayout.Label("分辨率:" + Screen.currentResolution.ToString());
        GUILayout.EndVertical();
    }

    #endregion

    #region System

    /// <summary>
    /// 繪製系統窗口
    /// </summary>
    void DrawSystemWindow()
    {
        GUILayout.BeginVertical("Box");
        GUILayout.Label("操做系統:" + SystemInfo.operatingSystem);
        GUILayout.Label("系統內存:" + SystemInfo.systemMemorySize + "MB");
        GUILayout.Label("處理器:" + SystemInfo.processorType);
        GUILayout.Label("處理器數量:" + SystemInfo.processorCount);
        GUILayout.Label("顯卡:" + SystemInfo.graphicsDeviceName);
        GUILayout.Label("顯卡類型:" + SystemInfo.graphicsDeviceType);
        GUILayout.Label("顯存:" + SystemInfo.graphicsMemorySize + "MB");
        GUILayout.Label("顯卡標識:" + SystemInfo.graphicsDeviceID);
        GUILayout.Label("顯卡供應商:" + SystemInfo.graphicsDeviceVendor);
        GUILayout.Label("顯卡供應商標識碼:" + SystemInfo.graphicsDeviceVendorID);
        GUILayout.Label("設備模式:" + SystemInfo.deviceModel);
        GUILayout.Label("設備名稱:" + SystemInfo.deviceName);
        GUILayout.Label("設備類型:" + SystemInfo.deviceType);
        GUILayout.Label("設備標識:" + SystemInfo.deviceUniqueIdentifier);
        GUILayout.EndVertical();
    }

    #endregion

    #region Environment

    /// <summary>
    /// 繪製環境窗口
    /// </summary>
    void DrawEnviromentWindow()
    {
        GUILayout.BeginVertical("box");
        GUILayout.Label("項目名稱:" + Application.productName);
        GUILayout.Label("項目包名:" + Application.identifier);
        GUILayout.Label("項目版本:" + Application.version);
        GUILayout.Label("Unity版本:" + Application.unityVersion);
        GUILayout.Label("公司名稱:" + Application.companyName);
        GUILayout.EndVertical();
    }

    #endregion
}
相關文章
相關標籤/搜索