[Unity]console中定位lua文件并在sublime中打开

在雨松MOMO的微博上发现可以传入lua文件路径和行数,在sublime中直接定位
[Unity]console中定位lua文件并在sublime中打开_第1张图片

[Unity]console中定位lua文件并在sublime中打开_第2张图片

由于最近一直在写lua, 也苦于开发工具的匮乏,所以就捣鼓了一下:
下面可以看到lua代码输出的内容,双击这一条log,就根据我的配置打开sublime跳转到对应的文件以及行:

[Unity]console中定位lua文件并在sublime中打开_第3张图片
[Unity]console中定位lua文件并在sublime中打开_第4张图片

使用方法:将下面的C#代码放到工程Editor目录下面,然后在菜单栏Tools下面有两个按钮,分别配置sublime可执行文件的路径和lua工程的根目录。

另外需要注意的是,lua层的输出有格式规范,从上面的示例图片中可以考到,lua文件的相对路径信息在和中间,行号也在和之间。在lua层我是这么做的:
在lua执行的最开始,加入这么一段代码以替换全局的print函数,然后由外部调用该函数,会得到调用处的文件信息和行号,输出到console中,之后就会根据这些信息来定位到相应的lua文件和行。

local tempPrint = print
function print(...)
    local tempLog = ""
    for _, singleLog in ipairs({...}) do
        tempLog = tempLog..singleLog
    end
    tempPrint(tempLog, "", debug.getinfo(2).short_src ,"" ,debug.getinfo(2).currentline ,"")
end

下面是C#代码:

//  http://blog.csdn.net/rickshaozhiheng
//  OpenLuaHelper.cs
//  Created by zhiheng.shao
//  Copyright  2017年 zhiheng.shao. All rights reserved.
//
//  Description

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

public class OpenLuaHelper : Editor
{
    private const string EXTERNAL_EDITOR_PATH_KEY = "mTv8";
    private const string LUA_PROJECT_ROOT_FOLDER_PATH_KEY = "obUd";

    //[UnityEditor.Callbacks.OnOpenAssetAttribute(0)]
    //static bool OnOpenAsset(int instanceID, int line)
    //{
    //    string name = EditorUtility.InstanceIDToObject(instanceID).name;
    //    Debug.Log("Open Asset Step1,asset name=>" + name);
    //    return true;
    //}

    [UnityEditor.Callbacks.OnOpenAssetAttribute(2)]
    public static bool OnOpenAsset2(int instanceID, int line)
    {
        string logText = GetLogText();
        if (string.IsNullOrEmpty(logText))
            return false;
        // get filePath
        Regex regex = new Regex(@".*<\/filePath>");
        Match match = regex.Match(logText);
        string filePath = match.Groups[0].Value.Trim();
        int length = filePath.Length - 10 - 12;
        filePath = filePath.Substring(10, length);
        filePath = filePath.Replace(".", "/");

        string luaFolderRoot = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);
        if (string.IsNullOrEmpty(luaFolderRoot))
        {
            SetLuaProjectRoot();
            luaFolderRoot = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);
        }
        filePath = luaFolderRoot.Trim() + "/" + filePath.Trim() + ".lua";

        // get line number
        Regex lineRegex = new Regex(@".*<\/line>");
        match = lineRegex.Match(logText);
        string luaLineString = match.Groups[0].Value;
        luaLineString.Trim();
        length = luaLineString.Length - 6 -11;
        luaLineString = luaLineString.Substring(10, length);
        int luaLine = int.Parse(luaLineString.Trim());

        return OpenFileAtLineExternal(filePath, luaLine);
    }


    static bool OpenFileAtLineExternal(string fileName, int line)
    {
        string editorPath = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);
        if (string.IsNullOrEmpty(editorPath) || !File.Exists(editorPath))
        {   // 没有path就弹出面板设置
            SetExternalEditorPath();
        }
        OpenFileWith(fileName, line);
        return true;
    }

    static void OpenFileWith(string fileName, int line)
    {
        string editorPath = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);
        System.Diagnostics.Process proc = new System.Diagnostics.Process();
        proc.StartInfo.FileName = editorPath;
        proc.StartInfo.Arguments = string.Format("{0}:{1}:0", fileName, line);
        proc.Start();
    }

    [MenuItem("Tools/SetExternalEditorPath")]
    static void SetExternalEditorPath()
    {
        string path = EditorUserSettings.GetConfigValue(EXTERNAL_EDITOR_PATH_KEY);
        path = EditorUtility.OpenFilePanel(
                    "SetExternalEditorPath",
                    path,
                    "exe");

        if (path != "")
        {
            EditorUserSettings.SetConfigValue(EXTERNAL_EDITOR_PATH_KEY, path);
            Debug.Log("Set Editor Path: " + path);
        }
    }

    [MenuItem("Tools/SetLuaProjectRoot")]
    static void SetLuaProjectRoot()
    {
        string path = EditorUserSettings.GetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY);
        path = EditorUtility.OpenFolderPanel(
                    "SetLuaProjectRoot",
                    path,
                    "");

        if (path != "")
        {
            EditorUserSettings.SetConfigValue(LUA_PROJECT_ROOT_FOLDER_PATH_KEY, path);
            Debug.Log("Set Editor Path: " + path);
        }
    }

    static string GetLogText()
    {
        // 找到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 ((object)UnityEditor.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;

            // 下面是stacktrace中一些可能有用的数据、函数和使用方法,这里就不一一说明了,我们这里暂时还用不到
            /*
            var field_row = type_list_view_state.GetField("row", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            if (field_row == null) return null;

            var field_total_rows = type_list_view_state.GetField("totalRows", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            if (field_total_rows == null) return null;

            var type_log_entries = assembly_unity_editor.GetType("UnityEditorInternal.LogEntries");
            if (type_log_entries == null) return null;

            var method_get_entry = type_log_entries.GetMethod("GetEntryInternal", BindingFlags.Static | BindingFlags.Public);
            if (method_get_entry == null) return null;

            var type_log_entry = assembly_unity_editor.GetType("UnityEditorInternal.LogEntry");
            if (type_log_entry == null) return null;

            var field_instance_id = type_log_entry.GetField("instanceID", BindingFlags.Instance | BindingFlags.Public);
            if (field_instance_id == null) return null;

            var field_line = type_log_entry.GetField("line", BindingFlags.Instance | BindingFlags.Public);
            if (field_line == null) return null;

            var field_condition = type_log_entry.GetField("condition", BindingFlags.Instance | BindingFlags.Public);
            if (field_condition == null) return null;

            object instance_log_entry = Activator.CreateInstance(type_log_entry);
            int value_row = (int)field_row.GetValue(value_list_view);
            int value_total_rows = (int)field_total_rows.GetValue(value_list_view);
            int log_by_this_count = 0;
            for (int i = value_total_rows – 1; i > value_row; i–) {
            method_get_entry.Invoke(null, new object[] { i, instance_log_entry });
            string value_condition = field_condition.GetValue(instance_log_entry) as string;
            if (value_condition.Contains("[SDebug]")) {
            log_by_this_count++;
            }
            }
            */

            // 找到类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;
    }
}

参考:
1. http://weibo.com/2332920021/ExWUWBjrU?filter=hot&root_comment_id=0&type=comment#_rnd1488980161004
2. http://www.wy182000.com/2017/02/09/unity-%E8%87%AA%E5%AE%9A%E4%B9%89log%E7%B1%BB%E5%9C%A8%E7%BC%96%E8%BE%91%E5%99%A8%E4%B8%AD%E4%BB%A3%E7%A0%81%E5%AE%9A%E4%BD%8D%EF%BC%88unityeditor-callbacks-onopenassetattribute%EF%BC%89/

你可能感兴趣的:(unity,unity,lua,开发工具)