unity 运用设置的ide翻开对应的脚本文件

前语

之前在一家比较大的游戏公司上班,进入项目组的时候已经完成了项目框架建立,基本的功用都有了,其中就包括lua traceback的跳转,换了一家公司发现没有这个功用,于是自己上手处理了一下这个部分,目前看起来网上的计划不是许多,我的计划也比较具有适用性; 相关完成:

  • www.bilibili.com/read/cv1546… 不太行,at计划在unity2019.4上不很好使,会报错
  • weibo.com/2332920021/… 这个计划可行,可是个人感觉还是不够实用,关于调用层次很深的报错来说基本不可用;
  • stackoverflow.com/questions/5… hyperlink 计划,最终也是运用该计划;

完成

完成思路:

  • 通过字符串替换将lua traceback转换成hyperlink的办法;
  • 点击hyperlink 触发翻开资源的流程,完成自定义的跳转;

具体完成

lua traceback 实例:

stdin:1: attempt to index a nil value (global 't')
stack traceback:
        stdin:1: in main chunk
        [C]: in ?

lua traceback 格式如下:\t file:line: in XXX 运用c# regex 替换字符串完成如下: 运用正则表达式识别\t\w+(.{1}\w+)+:\d+: 然后获取path和line,运用path生成当前项目相对Assets的途径,并生成<a href=\"{0}.lua\" line=\"{1}\">{2}</a>这种超链接办法的文本;

#if UNITY_EDITOR
    // 写死luapath
    private static string luaPath = "Assets/Scripts/Lua";
    static readonly string pattern = @"\t\w+(.{1}\w+)+:\d+: ";
    static readonly string template = "<a href=\"{0}.lua\" line=\"{1}\">{2}</a>";
#endif
    public static string ReplaceTraceBack(string msg)
    {
#if UNITY_EDITOR
        return Regex.Replace(msg, pattern, (Match match) =>
        {
            string[] infos = match.Value.TrimStart('\t').Split(':');
            if (infos.Length < 2)
            {
                return match.Value;
            }
            string filePath = luaPath + infos[0];
            string line = infos[1];
            return string.Format(template, filePath.Replace(".", "/"), line, match.Value);
        }
            );
#else
        return msg;
#endif
    }

因为我不是正则高手,所以或许表达式不是很高雅,测验下来全体时间开支在电脑上还行,可是线上应该不太可行,如果有更加高雅的计划,请和我联系

这样子咱们就完成了lua traceback的超链接完成,点击会翻开默认编辑器翻开lua文件:

unity lua ts等脚本文件 traceback 跳转

这儿咱们完成了tracebackunity Asset的改动,其他类型的脚本的堆栈报错都可以运用该办法完成转换为点击跳转对应Asset,后面咱们需求运用ide翻开特定的lua代码;

指定对应的luaIDE翻开对应的Lua代码

这儿直接借用luaProfile的完成:

static string LUA_IDE_KEY = "LuaIDE";
[UnityEditor.Callbacks.OnOpenAssetAttribute(1)]
static bool OnOpenLuaAsset(int instance, int line)
{
    string path = AssetDatabase.GetAssetPath(instance);
    if (path.EndsWith(".lua") || path.EndsWith(".lua.txt"))
    {
        return OpenFileAtLineExternal(path, line);
    }
    return false;
}
static bool OpenFileAtLineExternal(string filePath, int line)
{
    string editorPath = EditorPrefs.GetString(LUA_IDE_KEY);
    // 没有path就弹出面板设置
    if (string.IsNullOrEmpty(editorPath) || !File.Exists(editorPath))
    {
        SetExternalEditorPath();
        editorPath = EditorPrefs.GetString(LUA_IDE_KEY);
    }
    if (string.IsNullOrEmpty(editorPath) || !File.Exists(editorPath))
    {
        return false;
    }
    System.Diagnostics.Process proc = new System.Diagnostics.Process();
    proc.StartInfo.FileName = editorPath;
    string procArgument = "";
    if (editorPath.IndexOf("Code") != -1 || editorPath.IndexOf("code") != -1)
    {
        procArgument = string.Format("-g {0}:{1}", filePath, line > 0 ? line : 0);
    }
    else if (editorPath.IndexOf("idea") != -1 || editorPath.IndexOf("clion") != -1 || editorPath.IndexOf("rider") != -1)
    {
        procArgument = string.Format("--line {0} {1}", line, filePath);
        Debug.Log(procArgument);
    }
    else
    {
        procArgument = string.Format("{0}:{1}", filePath, line);
    }
    proc.StartInfo.UseShellExecute = false;
    proc.StartInfo.CreateNoWindow = true;
    proc.StartInfo.Arguments = procArgument;
    proc.Start();
    return true;
}
[MenuItem("Tools/Select LuaIDE")]
public static void SetExternalEditorPath()
{
    string path = EditorPrefs.GetString(LUA_IDE_KEY);
    path = EditorUtility.OpenFilePanel("Select Lua IDE", path, "");
    if (path != "")
    {
        EditorPrefs.SetString(LUA_IDE_KEY, path);
        Debug.Log("Set Lua IDE Path: " + path);
    }
}

运用EditorPrefs存储luaide的可履行途径, 不要运用PlayerPrefs,避免和游戏的数据混淆。

更近一步

1、一起获取Lua本身的抛出的error

public void ThrowExceptionFromError(int oldTop)
{
#if THREAD_SAFE || HOTFIX_ENABLE
    lock (luaEnvLock)
    {
#endif
        object err = translator.GetObject(L, -1);
        LuaAPI.lua_settop(L, oldTop);
        // A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved)
        Exception ex = err as Exception;
        if (ex != null) throw ex;
        // A non-wrapped Lua error (best interpreted as a string) - wrap it and throw it
        if (err == null) err = "Unknown Lua Error";
#if UNITY_EDITOR
    err = StringHelper.ReplaceTraceBack(err.ToString());
#endif
    throw new LuaException(err.ToString());
#if THREAD_SAFE || HOTFIX_ENABLE
    }
#endif
}

关于lua手动抛出的反常只需求手动用ReplaceTraceBack包装一层就好了,一起需求注意避免线上运用,削减字符串正则消耗;

2、完成原理

该计划最主要的原理在于unity怎么处理hyperlink的,这儿我ILSpy了一下UnityEditor.dll看了一下完成:

privatevoidEditorGUI_HyperLinkClicked(objectsender,EventArgse)
{  
    EditorGUILayout.HyperLinkClickedEventArgshyperLinkClickedEventArgs=(EditorGUILayout.HyperLinkClickedEventArgs)e;  
    if(hyperLinkClickedEventArgs.hyperlinkInfos.TryGetValue("href",outvarvalue)&&hyperLinkClickedEventArgs.hyperlinkInfos.TryGetValue("line",outvarvalue2))  
    {  
        intline=int.Parse(value2);  
        stringvalue3=value.Replace('\\','/');  
        if(!string.IsNullOrEmpty(value3))  
        {  
            LogEntries.OpenFileOnSpecificLineAndColumn(value,line,-1);  
        }  
    }  
}

也就是说你的超链接文本带有<a href="https://juejin.im/post/7167186242235269128/path" line="1">这种相似文本就可以调用该办法。更进一步的,我发现unity官方的traceback计划实际上也是运用的这个办法:

internal static string StacktraceWithHyperlinks(string stacktraceText)
{
    StringBuilder stringBuilder = new StringBuilder();
    string[] array = stacktraceText.Split(new string[1] { "\n" }, StringSplitOptions.None);
    for (int i = 0; i < array.Length; i++)
    {
            string text = ") (at ";
            int num = array[i].IndexOf(text, StringComparison.Ordinal);
            if (num > 0)
            {
                    num += text.Length;
                    if (array[i][num] != '<')
                    {
                            string text2 = array[i].Substring(num);
                            int num2 = text2.LastIndexOf(":", StringComparison.Ordinal);
                            if (num2 > 0)
                            {
                                    int num3 = text2.LastIndexOf(")", StringComparison.Ordinal);
                                    if (num3 > 0)
                                    {
                                            string text3 = text2.Substring(num2 + 1, num3 - (num2 + 1));
                                            string text4 = text2.Substring(0, num2);
                                            stringBuilder.Append(array[i].Substring(0, num));
                                            stringBuilder.Append("<a href=\"" + text4 + "\" line=\"" + text3 + "\">");
                                            stringBuilder.Append(text4 + ":" + text3);
                                            stringBuilder.Append("</a>)\n");
                                            continue;
                                    }
                            }
                    }
            }
            stringBuilder.Append(array[i] + "\n");
    }
    if (stringBuilder.Length > 0)
    {
            stringBuilder.Remove(stringBuilder.Length - 1, 1);
    }
    return stringBuilder.ToString();
}

本质上也是字符串替换成为hyperLink办法,真的十分有意思;

工程中碰到的额定的问题

1、mac 编辑器挑选问题

mac 挑选可履行文件办法有点古怪,翻开finder找不到可履行文件,装置code只会显现app文件,可是文件系统里这个app文件会被当作文件夹,而非文件,可是挑选的界面里边又会是文件,所以需求额定的处理(有点反直觉):

unity lua ts等脚本文件 traceback 跳转
采取的计划是右键点击对应的app,然后挑选里边可履行文件:

unity lua ts等脚本文件 traceback 跳转

unity lua ts等脚本文件 traceback 跳转
对此我的点评是不知道哪里出了什么问题,可是只需求配置这一次就可以用了,所以也没有额定的修改; 如果有更好的计划供给,请私信或许留言,十分感谢~

2、unityEditor点击lua文件问题

这个解决计划重写了lua的翻开办法,会导致点击项目里边的lua文件或许lua.txt文件都会履行OnOpenAsset(filepath, -1),可是code -g filepath:-1会导致无法翻开文件,具体报错如下:

unity lua ts等脚本文件 traceback 跳转

所以改动代码加一个反常判断:

if (editorPath.IndexOf("Code") != -1 || editorPath.IndexOf("code") != -1)
{
    procArgument = string.Format("-g {0}:{1}", filePath, line > 0 ? line : 0);
}