Unity中实现自定义调试器

Posted Hello Bug.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity中实现自定义调试器相关的知识,希望对你有一定的参考价值。

一:前言

在Unity编辑器下可以查看log输出、内存信息、系统信息等,但发布到移动端很多信息都无法查看,这个自定义的调试器可以查看一些游戏信息,方便调试


二:使用

——在游戏启动时调用Debugger.Ins.OpenDebugger()开启调试器


三:代码

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
}

 

以上是关于Unity中实现自定义调试器的主要内容,如果未能解决你的问题,请参考以下文章

如何在 WordPress 中实现自定义标头的引导小部件代码?

Winform中实现自定义屏保效果(附代码下载)

[转]Java中实现自定义的注解处理器

如何在Canvas中实现自定义路径动画

如何在Canvas中实现自定义路径动画

在具有条件的 keras 中实现自定义损失函数