# unity-puerts-stracktrace **Repository Path**: abcdzxcv/unity-puerts-stracktrace ## Basic Information - **Project Name**: unity-puerts-stracktrace - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-11-04 - **Last Updated**: 2022-01-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 【unity】如何让puerts打印出来的堆栈支持vscode跳转
unity的console窗口有个特性,当某行日志符合下方格式时,将转为链接 ```html ``` 当用鼠标单击该链接后,将在c#中触发`OnOpenAsset`函数 ```c# [UnityEditor.Callbacks.OnOpenAssetAttribute(-1)] static bool OnOpenAsset(int instanceID, int line) ``` 而这个函数的内容是可以自定义的 借用这个特性,对日志稍加处理,就可以做到,在unity的console窗口中,直接点击typescript堆栈中的ts文件链接,通过`vscode`自动打开.ts文件并跳转到指定行 最终的效果截图如下: 1. 这是不作任何处理打印出来的堆栈 ![image](Image/1.png) 2. 这是经过改写后的堆栈,支持点击链接后自动打开vscode,并跳转到对应的.ts文件的指定行(需要安装vscode) ![image](Image/2.png) 3. 这是点击后的效果,点击链接后,将打开vscode,并自动跳转到对应文件的指定行 ![image](Image/3.png) ## 源码
1. c#文件`PuertsStacktraceUtil.cs`(放在`Assets\Editor`路径下),用于处理点击链接的逻辑 ```c# using UnityEditor; using UnityEngine; using System.Runtime.InteropServices; public static class PuertsStacktraceUtil { public enum VirtualKeyCode { Ctrl = 17, Enter = 13, F9 = 120, Shift = 16, End = 35, LSHIFT = 0xA0, RSHIFT = 0xA1, } public enum VirtualKeyFlag { KeyDown = 0, Pressing = 1, KeyUp = 2, } public static string ExecuteSystemCmd(string cmdline) { using (var process = new System.Diagnostics.Process()) { process.StartInfo.FileName = "cmd.exe"; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardInput = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.CreateNoWindow = true; process.Start(); process.StandardInput.AutoFlush = true; process.StandardInput.WriteLine(cmdline + "&exit"); //获取cmd窗口的输出信息 string output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); process.Close(); return output; } } [DllImport("user32.dll", EntryPoint = "keybd_event")] static extern void keybd_event( byte bVk, //虚拟键值 对应按键的ascll码十进制值 byte bScan, //0 int dwFlags, //0 为按下,1按住,2为释放 int dwExtraInfo //0 ); // 处理asset打开的callback函数 [UnityEditor.Callbacks.OnOpenAssetAttribute(-1)] static bool OnOpenAsset(int instanceID, int line) { string fileFullPath = System.IO.Directory.GetParent(Application.dataPath) + "/" + AssetDatabase.GetAssetPath(EditorUtility.InstanceIDToObject(instanceID)); // Log("打开路径=" + fileFullPath + ",行=" + line); if (fileFullPath.EndsWith(".ts") || fileFullPath.EndsWith(".js")) //文件扩展名类型 { string tsFilePath = fileFullPath; bool isDiff = (line == 999999); bool isOpen = !isDiff; if (isOpen) { int row = line / 100000; int col = line % 100000; // 打开vscode,跳转到指定文件的指定行 ExecuteSystemCmd($"Code --goto {tsFilePath}:{row}:{col}"); // Shift+End快捷键选中到行尾,便于高亮显示在哪一行 keybd_event((byte)VirtualKeyCode.LSHIFT, 0, (int)VirtualKeyFlag.Pressing, 0); keybd_event((byte)VirtualKeyCode.End, 0, (int)VirtualKeyFlag.KeyDown, 0); keybd_event((byte)VirtualKeyCode.End, 0, (int)VirtualKeyFlag.KeyUp, 0); keybd_event((byte)VirtualKeyCode.LSHIFT, 0, (int)VirtualKeyFlag.KeyUp, 0); // 释放shift键 keybd_event((byte)VirtualKeyCode.LSHIFT, 0, (int)VirtualKeyFlag.KeyDown, 0); keybd_event((byte)VirtualKeyCode.LSHIFT, 0, (int)VirtualKeyFlag.KeyUp, 0); keybd_event((byte)VirtualKeyCode.RSHIFT, 0, (int)VirtualKeyFlag.KeyDown, 0); keybd_event((byte)VirtualKeyCode.RSHIFT, 0, (int)VirtualKeyFlag.KeyUp, 0); } else if (isDiff) { ExecuteSystemCmd($@"START """" D:\TortoiseGit\bin\TortoiseGitProc.exe /command:diff /path:""{ fileFullPath }"""); } return true; } else if(fileFullPath.EndsWith(".cs")) { return false; } else if(fileFullPath.EndsWith(".prefab")) { GameObject prefab = EditorUtil.LoadAssetGameObject(fileFullPath); UnityEditor.Selection.activeGameObject = prefab; EditorGUIUtility.PingObject(prefab); } return false; } } ``` 2. c#文件`LogUtil.cs`(放在`Assets\Script`路径下),用于将转换堆栈文本的格式 ```c# using UnityEngine; using System.Text.RegularExpressions; public class LogUtil { /* 将原始的ts堆栈文本改写成unity可识别的带链接的文本 传入参数: Error: at CheatUI.InitUI (Assets\Resources\Ts\UI\CheatUI.ts:246:16) at CheatUI.Awake (Assets\Resources\Ts\UI\CheatUI.ts:129:8) at chunk:1:3 at Assets\Resources\Ts\UI\FpsUI.ts:117:13 返回值: Error: at CheatUI.InitUI (Assets/Resources/Ts/UI/CheatUI.ts:行246:列16) at CheatUI.Awake (Assets/Resources/Ts/UI/CheatUI.ts:行129:列8) at chunk:1:3 at Assets/Resources/Ts/UI/FpsUI.ts:行117:列13 */ public static string StacktraceWithHyperlinks(string stacktraceText) { Regex reg1 = new Regex(@"^(?[\s]+at .+ \()(?.+\..+):(?[\d]+):(?[\d]+)(?\))$"); Regex reg2 = new Regex(@"^(?[\s]+at )(?.+\..+):(?[\d]+):(?[\d]+)(?)$"); string stringBuilder = ""; string[] lines = stacktraceText.Replace("\r\n", "\n").Split('\n'); for (int i = 0; i < lines.Length; i++) { string line = lines[i]; if (line.IndexOf("= 0) { file = file.Substring(index); } // string json = $"{{ \"file\":\"{file }\", \"row\":\"{row}\", \"col\":\"{col}\" }}"; int lineNumber = (System.Convert.ToInt32(row)) * 100000 + (System.Convert.ToInt32(col)); string convertLine = $"{begin}{file}:行{row}:列{col}{end}"; string diff = $" 预览差异"; stringBuilder += convertLine + diff; } else { stringBuilder += line; } if (i + 1 < lines.Length) { stringBuilder += "\n"; } } return stringBuilder; } } ``` 3. typescript源码示例 ```typescript import { LogUtil } from "csharp" function Example() { const stack: string = (new Error()).stack console.log("原始堆栈内容=" + stack) console.log("支持vscode跳转的堆栈=" + LogUtil.StacktraceWithHyperlinks(stack)) } ```