您的位置:首页 > 移动开发 > Unity3D

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

2017-11-12 23:21 537 查看
原文地址: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))
};

//处理从
4000
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

以上知识分享,如有错误,欢迎指出,共同学习,共同进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: