效果
双击便弹出lua堆栈
点击即可跳转到对应位置
原cs日志跳转不影响
LuaLogLocate.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
public class PathLine
{
public PathLine(string s, int l)
{
path = s;
line = l;
}
public string path { get; set; }
public int line { get; set; }
public override string ToString()
{
return $"{path.Substring(path.LastIndexOf('/') + 1)}:{line}";
}
};
public class LuaLogLocate
{
private static string[] luaRootPaths =
{
"Assets/LuaFramework/Lua/",
"Assets/LuaFramework/ToLua/Lua/",
};
// 处理asset打开的callback函数
[UnityEditor.Callbacks.OnOpenAssetAttribute(0)]
static bool OnOpenAsset(int instance, int line)
{
//获取资源根目录
string assetsPath = Application.dataPath.Substring(0,
Application.dataPath.LastIndexOf("Assets", StringComparison.Ordinal));
StringBuilder fileFullPathBuilder = new StringBuilder();
//获取Console中的tolua调用堆栈
string stackTrace = GetStackTrace();
string name = EditorUtility.InstanceIDToObject(instance).name;
if (stackTrace == null || !stackTrace.Contains("stack traceback:")) return false;
MatchCollection csharpMatchs = Regex.Matches(stackTrace, @"\(at.*\)");
if (!csharpMatchs[0].Value.Contains(name)) return false;
MatchCollection luaMatchs = Regex.Matches(stackTrace, @"\t.*:\d+:");
List<PathLine> pathLines = new List<PathLine>();
foreach (Match luaMatch in luaMatchs)
{
string[] strs;
if (luaMatch.Value.Contains("\t[string "))
{
//例如如 [string "UI/newSecretary2/WndMiShuXiangQing"]:165: in function 'func'
string temp = luaMatch.Value
.Replace("\t[string ", "")
.Replace("\"]", "");
strs = temp.Substring(1, temp.Length - 1).Split(':');
}
else
{
strs = luaMatch.Value.Substring(1, luaMatch.Value.Length - 1).Split(':');
}
foreach (string luaRootPath in luaRootPaths)
{
fileFullPathBuilder.Clear();
fileFullPathBuilder.Append(assetsPath);
fileFullPathBuilder.Append(luaRootPath);
fileFullPathBuilder.Append(strs[0]);
if (!strs[0].EndsWith(".lua")) fileFullPathBuilder.Append(".lua");
if (File.Exists(fileFullPathBuilder.ToString()))
{
pathLines.Add(new PathLine(fileFullPathBuilder.ToString(), Int32.Parse(strs[1])));
break;
}
}
}
//最顶层报错cs文件
var strs2 = csharpMatchs[0].Value.Substring(4, csharpMatchs[0].Value.Length - 5).Split(':');
fileFullPathBuilder.Clear();
fileFullPathBuilder.Append(assetsPath);
fileFullPathBuilder.Append(strs2[0]);
if (!strs2[0].EndsWith(".cs")) fileFullPathBuilder.Append(".cs");
pathLines.Add(new PathLine(fileFullPathBuilder.ToString(), Int32.Parse(strs2[1])));
//可跳转路径
Vector2 currentMousePosition = Event.current.mousePosition;
Rect contextRect = new Rect(currentMousePosition.x, currentMousePosition.y, 0, 0);
List<GUIContent> options = new List<GUIContent>();
foreach (PathLine pathLine in pathLines)
{
options.Add(new GUIContent(pathLine.ToString()));
}
EditorUtility.DisplayCustomMenu(contextRect, options.ToArray(), -1, Select, pathLines);
return true;
}
private static void OpenFileLine(PathLine pathLine)
{
UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(pathLine.path, pathLine.line);
}
private static void Select(object userData, string[] options, int selected)
{
List<PathLine> pathLines = (List<PathLine>) userData;
PathLine selectedPathLine = pathLines[selected];
OpenFileLine(selectedPathLine);
}
static string GetStackTrace()
{
// 找到UnityEditor.EditorWindow的assembly
var assembly_unity_editor = Assembly.GetAssembly(typeof(UnityEditor.EditorWindow));
if (assembly_unity_editor == null) return null;
// 找到类UnityEditor.ConsoleWindow
var type_console_window = assembly_unity_editor.GetType("UnityEditor.ConsoleWindow");
if (type_console_window == null) return null;
// 找到UnityEditor.ConsoleWindow中的成员ms_ConsoleWindow
var field_console_window = type_console_window.GetField("ms_ConsoleWindow",
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
if (field_console_window == null) return null;
// 获取ms_ConsoleWindow的值
var instance_console_window = field_console_window.GetValue(null);
if (instance_console_window == null) return null;
// 如果console窗口时焦点窗口的话,获取stacktrace
if (EditorWindow.focusedWindow == instance_console_window)
{
// 通过assembly获取类ListViewState
var type_list_view_state = assembly_unity_editor.GetType("UnityEditor.ListViewState");
if (type_list_view_state == null) return null;
// 找到类UnityEditor.ConsoleWindow中的成员m_ListView
var field_list_view = type_console_window.GetField("m_ListView",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (field_list_view == null) return null;
// 获取m_ListView的值
var value_list_view = field_list_view.GetValue(instance_console_window);
if (value_list_view == null) return null;
// 找到类UnityEditor.ConsoleWindow中的成员m_ActiveText
var field_active_text = type_console_window.GetField("m_ActiveText",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (field_active_text == null) return null;
// 获得m_ActiveText的值,就是我们需要的stacktrace
string value_active_text = field_active_text.GetValue(instance_console_window).ToString();
return value_active_text;
}
return null;
}
}
Unity菜单->Edit->Preferences
VSCode需修改External Tools,保存当前Vscode 项目工作区,替换路径
添加lua类型至Extentions Handler