Unity编辑器拓展之四:ConsoleWindow中的双击日志定位

原文地址:http://blog.csdn.net/l449612236/article/details/76087616

感谢上位博主的分享。

本文在此基础上,做了分析加上了更加详细的介绍。

新建LogEditor脚本
直接上源码以及注释:

using System.Reflection;
using UnityEditor;
using UnityEngine;

public class LogEditor 
{
    private class LogEditorConfig
    {
        public string logScriptPath = "";   //自定义日志脚本路径
        public string logTypeName = "";     //脚本type
        public int instanceID = 0;

        public LogEditorConfig(string logScriptPath, System.Type logType)
        {
            this.logScriptPath = logScriptPath;
            this.logTypeName = logType.FullName;
        }
    }

    //配置的日志
    private static LogEditorConfig[] _logEditorConfig = new LogEditorConfig[]
    {
        new LogEditorConfig("Assets/Scripts/DDebug/DDebug.cs",typeof(DDebug))
    };

    //处理从ConsoleWindow双击跳转
    [UnityEditor.Callbacks.OnOpenAssetAttribute(-1)]
    private static bool OnOpenAsset(int instanceID,int line)
    {
        for (int i = _logEditorConfig.Length - 1; i >= 0; --i)
        {
            var configTmp = _logEditorConfig[i];
            UpdateLogInstanceID(configTmp);
            if (instanceID == configTmp.instanceID)
            {
                var statckTrack = GetStackTrace();
                if (!string.IsNullOrEmpty(statckTrack))
                {
                    /*
                    举例说明:下面这段是一条ConsoleWindow的日志信息
                    Awake
                    UnityEngine.Debug:Log(Object)
                    DDebug:Log(String) (at Assets/Scripts/DDebug/DDebug.cs:13)
                    Test:Awake() (at Assets/Scripts/Test.cs:13)

                    说明:
                    1、其中第一行的"Awake":是指调用自定义打印日志函数的函数名,本例是在Test脚本中的Awake函数里调用的
                    2、第二行的"UnityEngine.Debug:Log(Object)":是指该日志最底层是通过Debug.Log函数打印出来的
                    3、第三行的"DDebug:Log(String) (at Assets/Scripts/DDebug/DDebug.cs:13)":指第二行的函数调用在DDebug.cs的13行
                    4、第四行的"Test:Awake() (at Assets/Scripts/Test.cs:13)":指Test.cs脚本的Awake函数调用了第二行的DDebug.cs的Log函数,在第13行
                     */

                    //通过以上信息,不难得出双击该日志应该打开Test.cs文件,并定位到第13行
                    //以换行分割堆栈信息
                    var fileNames = statckTrack.Split('\n');
                    //定位到调用自定义日志函数的那一行:"Test:Awake() (at Assets/Scripts/Test.cs:13)"
                    var fileName = GetCurrentFullFileName(fileNames);
                    //定位到上例的行数:13
                    var fileLine = LogFileNameToFileLine(fileName);
                    //得到调用自定义日志函数的脚本:"Assets/Scripts/Test.cs"
                    fileName = GetRealFileName(fileName);

                    //根据脚本名和行数,打开脚本
                    //"Assets/Scripts/Test.cs"
                    //13
                    AssetDatabase.OpenAsset(AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(fileName), fileLine);
                    return true;
                }
                break;
            }
        }

        return false;
    }

    /// <summary>
    /// 反射出日志堆栈
    /// </summary>
    /// <returns></returns>
    private static string GetStackTrace()
    {
        var consoleWindowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
        var fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);
        var consoleWindowInstance = fieldInfo.GetValue(null);

        if (null != consoleWindowInstance)
        {
            if ((object)EditorWindow.focusedWindow == consoleWindowInstance)
            {
                fieldInfo = consoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);
                string activeText = fieldInfo.GetValue(consoleWindowInstance).ToString();
                return activeText;
            }
        }
        return "";
    }

    private static void UpdateLogInstanceID(LogEditorConfig config)
    {
        if (config.instanceID > 0)
        {
            return;
        }

        var assetLoadTmp = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(config.logScriptPath);
        if (null == assetLoadTmp)
        {
            throw new System.Exception("not find asset by path=" + config.logScriptPath);
        }
        config.instanceID = assetLoadTmp.GetInstanceID();
    }

    private static string GetCurrentFullFileName(string[] fileNames)
    {
        string retValue = "";
        int findIndex = -1;

        for (int i = fileNames.Length - 1; i >= 0; --i)
        {
            bool isCustomLog = false;
            for (int j = _logEditorConfig.Length - 1; j >= 0; --j)
            {
                if (fileNames[i].Contains(_logEditorConfig[j].logTypeName))
                {
                    isCustomLog = true;
                    break;
                }
            }
            if (isCustomLog)
            {
                findIndex = i;
                break;
            }
        }

        if (findIndex >= 0 && findIndex < fileNames.Length - 1)
        {
            retValue = fileNames[findIndex + 1];
        }

        return retValue;
    }

    private static string GetRealFileName(string fileName)
    {
        int indexStart = fileName.IndexOf("(at ") + "(at ".Length;
        int indexEnd = ParseFileLineStartIndex(fileName) - 1;

        fileName = fileName.Substring(indexStart, indexEnd - indexStart);
        return fileName;
    }

    private static int LogFileNameToFileLine(string fileName)
    {
        int findIndex = ParseFileLineStartIndex(fileName);
        string stringParseLine = "";
        for (int i = findIndex; i < fileName.Length; ++i)
        {
            var charCheck = fileName[i];
            if (!IsNumber(charCheck))
            {
                break;
            }
            else
            {
                stringParseLine += charCheck;
            }
        }

        return int.Parse(stringParseLine);
    }

    private static int ParseFileLineStartIndex(string fileName)
    {
        int retValue = -1;
        for (int i = fileName.Length - 1; i >= 0; --i)
        {
            var charCheck = fileName[i];
            bool isNumber = IsNumber(charCheck);
            if (isNumber)
            {
                retValue = i;
            }
            else
            {
                if (retValue != -1)
                {
                    break;
                }
            }
        }
        return retValue;
    }

    private static bool IsNumber(char c)
    {
        return c >= '0' && c <= '9';
    }
}

该脚本放置在工程目录下的Editor文件夹中。

接着实现自己的日志类,我这里取名为DDebug类。
我之前也介绍过一篇文章,实现自己的Debug类
地址如下:http://blog.csdn.net/qq_26999509/article/details/53643564

当初写的不够成熟,大家可以按照自己意愿进行编写。
本次主要是为了实现双击日志定位的功能。

下面附上本次测试写的DDebug脚本:

using UnityEngine;

public class DDebug 
{
    static private string errorColor = "red";

    static private string warningColor = "yellow";

    static private string logColor = "white";

    public static void Log(string info)
    {
        info = string.Concat("<color=", logColor, ">", info, "</color>");
        Debug.Log(info);
    }

    public static void LogError(string info)
    {
        info = string.Concat("<color=", errorColor, ">", info, "</color>");
        Debug.LogError(info);
    }

    public static void LogWarning(string info)
    {
        info = string.Concat("<color=", warningColor, ">", info, "</color>");
        Debug.LogWarning(info);
    }
}

这里只是加了颜色而已,其实还有不少功能可以拓展。

接下来写个Test测试脚本,进行代码测试。

using UnityEngine;

public class Test : MonoBehaviour 
{
    private void OnEnable()
    {
        DDebug.LogWarning("OnEnable");
    }

    void Awake()
    {
        DDebug.Log("Awake");
    }

    void Start () 
    {
        DDebug.LogError("Start");
    }
}

将该脚本挂在场景中。

测试如下:

这里写图片描述

双击日志也能成功跳转到日志函数调用的地方。。测试通过。

最后附上工程地址:
链接:http://pan.baidu.com/s/1c2La0hy 密码:ibqb

再次补充,原文地址:http://blog.csdn.net/l449612236/article/details/76087616

以上知识分享,如有错误,欢迎指出,共同学习,共同进步。

猜你喜欢

转载自blog.csdn.net/qq_26999509/article/details/78516237