using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; using Debug = UnityEngine.Debug; namespace ET.Editor { /// 用于显示当前项目中的Proto public class ProtoViewWindow : EditorWindow { [MenuItem("ET/Proto/ProtoView")] static void Init() { GetWindow("Proto View"); } //显示 private bool togSort = false; private bool lastTogSort = false; private Vector2 scrollViewPos; private string vsCodePath = @"C:\Users\XXX\AppData\Local\Programs\Microsoft VS Code\Code.exe"; private string newVSCodePath = ""; // private Texture2D bgTexture; private bool togOpenFolder = false; //是否同时打开文件夹 //数据 private List protoItems = new List(); private string delDataPath; private string protoConbinePath; // private Process process; private void OnEnable() { #if UNITY_EDITOR_WIN delDataPath = Application.dataPath.Replace("Assets", "").Replace("/", "\\"); #elif UNITY_EDITOR_OSX delDataPath = Application.dataPath.Replace("Assets", ""); #endif protoConbinePath = this.delDataPath + "Temp"; RefreshDataList(); // bgTexture = CreateTexture(1,1, Color.clear); } // private void OnDestroy() // { // this.CloseVSCode(); // } public void OnGUI() { GUIStyle rightBtnStyle = new GUIStyle(GUI.skin.FindStyle("button")); rightBtnStyle.alignment = TextAnchor.MiddleRight; rightBtnStyle.normal.textColor = Color.white; //默认状态下字体颜色 // rightBtnStyle.normal.background = bgTexture; GUIStyle errorBtnStyle = new GUIStyle(GUI.skin.FindStyle("button")); errorBtnStyle.alignment = TextAnchor.MiddleRight; errorBtnStyle.normal.textColor = Color.red; //默认状态下字体颜色 errorBtnStyle.focused.textColor = Color.red; errorBtnStyle.hover.textColor = Color.red; // errorBtnStyle.normal.background = bgTexture; GUIStyle existBtnStyle = new GUIStyle(GUI.skin.FindStyle("button")); existBtnStyle.alignment = TextAnchor.MiddleRight; existBtnStyle.normal.textColor = Color.yellow; existBtnStyle.focused.textColor = Color.yellow; existBtnStyle.hover.textColor = Color.yellow; // existBtnStyle.normal.background = bgTexture; GUIStyle rightTxtStyle = new GUIStyle(GUI.skin.FindStyle("label")); GUIStyle errorTxtStyle = new GUIStyle(GUI.skin.FindStyle("label")); errorTxtStyle.normal.textColor = Color.red; GUIStyle existTxtStyle = new GUIStyle(GUI.skin.FindStyle("label")); existTxtStyle.normal.textColor = Color.yellow; GUIStyle greenTxtStyle = new GUIStyle(GUI.skin.FindStyle("label")); greenTxtStyle.normal.textColor = Color.green; GUILayout.Space(5); //顶部工具栏 GUILayout.BeginHorizontal(); if (GUILayout.Button("check", GUILayout.Width(100))) { RefreshDataList(); } GUILayout.Space(10); bool newTogSort = GUILayout.Toggle(togSort, "排序"); if (newTogSort != lastTogSort) { lastTogSort = newTogSort; togSort = newTogSort; RefreshDataList(); } GUILayout.Space(10); float tmpWidth = 420; var rectNormal = GUILayoutUtility.GetRect(tmpWidth, 20); Event curEvent = Event.current; if (Event.current.type == EventType.MouseUp) { if (rectNormal.Contains(curEvent.mousePosition)) { newVSCodePath = EditorUtility.OpenFilePanel("select VSCode Path", newVSCodePath, ""); } } if (string.IsNullOrEmpty(newVSCodePath)) { vsCodePath = "点击这里, 设置VSCode路径"; EditorGUI.LabelField(rectNormal, this.vsCodePath, greenTxtStyle); } else { vsCodePath = this.newVSCodePath; EditorGUI.LabelField(rectNormal, this.vsCodePath); } GUI.Box(new Rect(rectNormal.x-5, rectNormal.y, tmpWidth+10, rectNormal.height), GUIContent.none, EditorStyles.helpBox); GUILayout.FlexibleSpace (); if (GUILayout.Button("open in vscode", GUILayout.Width(100))) { try { CombineProtoOpenByVSCode(); } catch (System.Exception e) { Debug.LogError($"打开VSCode报错, VSCode路径错误, 请先点击 <{this.vsCodePath}> 设置VSCode路径 {e}"); // Debug.LogError($"打开VSCode报错, VSCode路径错误:<{this.vsCodePath}>, 具体错误:{e}"); } } togOpenFolder = GUILayout.Toggle(togOpenFolder, ""); GUILayout.Space(10); if (GUILayout.Button("save", GUILayout.Width(60))) { SeprateProtoAndSave(); } if (GUILayout.Button("Prot2CS", GUILayout.Width(60))) { EditorApplication.ExecuteMenuItem("ET/Proto/Proto2CS"); } GUILayout.EndHorizontal(); GUILayout.Space(10); //内容 scrollViewPos = GUILayout.BeginScrollView(scrollViewPos); for (int i = 0; i < protoItems.Count; i++) { GUILayout.BeginHorizontal(); var temBtnStyle = rightBtnStyle; var temTxtStyle = rightTxtStyle; switch (protoItems[i].errorCode) { case 1: temBtnStyle = errorBtnStyle; temTxtStyle = errorTxtStyle; break; case 2: temBtnStyle = existBtnStyle; temTxtStyle = existTxtStyle; break; case 3: temBtnStyle = errorBtnStyle; temTxtStyle = errorTxtStyle; break; } if (GUILayout.Button(protoItems[i].name, temBtnStyle, GUILayout.Width(250))) { UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(protoItems[i].fullPath.Replace(delDataPath, "")); if (obj != null) { EditorGUIUtility.PingObject(obj); Selection.activeObject = obj; } else { Debug.LogError("File not found at path: " + protoItems[i].fullPath.Replace(delDataPath, "") + ", \n" + delDataPath + ", \n" + Application.dataPath); } } GUILayout.Label(protoItems[i].fullPath, temTxtStyle); GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private List FindProtoFilesInPackagesDirectory() { List protoFiles = new List(); string packagesPath = Path.Combine(Directory.GetCurrentDirectory(), "Packages"); if (Directory.Exists(packagesPath)) { SearchDirectoryForProtoFiles(packagesPath, protoFiles); } else { Debug.LogWarning("Packages directory not found at: " + packagesPath); } return protoFiles; } private void SearchDirectoryForProtoFiles(string directoryPath, List protoFiles) { try { string[] filePaths = Directory.GetFiles(directoryPath, "*.proto", SearchOption.AllDirectories); protoFiles.AddRange(filePaths); } catch (System.Exception ex) { Debug.LogError("Error searching directory for .proto files: " + ex.Message); } } private void CombineProtoOpenByVSCode() { string folderPath = ""; string subFolderPath = ""; #if UNITY_EDITOR_WIN folderPath = protoConbinePath + "\\TmpProto"; subFolderPath = folderPath + "\\"; #elif UNITY_EDITOR_OSX folderPath = protoConbinePath + "/TmpProto"; subFolderPath = folderPath + "/"; #endif //先移除所有之前的 DirectoryInfo directoryInfo = new DirectoryInfo(subFolderPath); if (directoryInfo.Exists) { FileInfo[] files = directoryInfo.GetFiles(); foreach (FileInfo file in files) { try { file.Delete(); } catch (System.Exception ex) { Debug.LogError("Failed to delete file: " + file.FullName + ". Error: " + ex.Message); } } } //增加现有的 foreach (var item in protoItems) { if (File.Exists(item.fullPath)) { if (!Directory.Exists(subFolderPath)) { Directory.CreateDirectory(subFolderPath); } File.Copy(item.fullPath, subFolderPath + item.simpPath, true); } } if (togOpenFolder) { EditorUtility.RevealInFinder(folderPath); } OpenInVSCode(folderPath); } private void SeprateProtoAndSave() { // if (!CloseVSCode()) return; string folderPath = ""; string subFolderPath = ""; #if UNITY_EDITOR_WIN folderPath = protoConbinePath + "\\TmpProto"; subFolderPath = folderPath + "\\"; #elif UNITY_EDITOR_OSX folderPath = protoConbinePath + "/TmpProto"; subFolderPath = folderPath + "/"; #endif foreach (var item in protoItems) { string tmpFilePath = subFolderPath + item.simpPath; if (!File.Exists(tmpFilePath)) { Debug.LogError($"路径不存在: {tmpFilePath}"); return; } File.Copy(tmpFilePath, item.fullPath, true); } AssetDatabase.Refresh(); //移除临时文件中的所有proto if (Directory.Exists(subFolderPath)) { foreach (var item in protoItems) { string tmpFilePath = subFolderPath + item.simpPath; if (File.Exists(tmpFilePath)) { File.Delete(tmpFilePath); } } } } private void RefreshDataList() { protoItems.Clear(); List tmpProtoFiles = FindProtoFilesInPackagesDirectory(); // Debug.Log($"当前项目中所有的Proto -----------> {tmpProtoFiles.Count}"); // foreach (string protoFile in tmpProtoFiles) // { // Debug.Log(protoFile); // } foreach (string fullPath in tmpProtoFiles) { string[] tmpStrs = null; #if UNITY_EDITOR_WIN tmpStrs = fullPath.Split("\\"); #elif UNITY_EDITOR_OSX tmpStrs = fullPath.Split("/"); #endif string simpPath = tmpStrs[^1]; string name = simpPath.Split(".")[0]; string numStr = name.Split("_")[^1]; int num = -1; short errorCode = 0; if (ProtoStyleIsError(name)) { Debug.LogError($"Proto文件名 格式有误: {simpPath}"); errorCode = 1; protoItems.Add(new ItemData(name, num, simpPath, fullPath, errorCode)); } else { if (int.TryParse(numStr, out num)) { foreach (var item in protoItems) { if (item.num == num) { errorCode = 2; item.errorCode = errorCode; Debug.LogError($"Proto文件名 数值相同: {num}, {simpPath}, {item.simpPath}"); } } protoItems.Add(new ItemData(name, num, simpPath, fullPath, errorCode)); } else { Debug.LogError($"Proto文件名 格式错误: {simpPath}"); errorCode = 3; protoItems.Add(new ItemData(name, num, simpPath, fullPath, errorCode)); } } } if (togSort) { protoItems.Sort((p1, p2) => p1.num.CompareTo(p2.num)); } } private void OpenInVSCode(string dirPath) { Debug.Log($"tackor-----> {dirPath}"); // 创建一个新的进程启动信息 ProcessStartInfo startInfo = new ProcessStartInfo { FileName = vsCodePath, // 如果需要传递参数,可以在这里添加 Arguments = dirPath, UseShellExecute = true, // 允许操作系统使用 shell 来启动进程 RedirectStandardOutput = false, // 通常不需要重定向输出 RedirectStandardError = false, // 通常不需要重定向错误 CreateNoWindow = false // 是否创建新窗口,取决于你的需求 }; // process = Process.Start(startInfo); using (Process process = Process.Start(startInfo)) { // 如果需要等待进程结束,可以使用 process.WaitForExit() // 在这个例子中,我们立即返回 // process.WaitForExit(); } } // private bool CloseVSCode() // { // bool haveClosedVSCode = false; // try // { // Log.Debug($"tackor--------> {this.process != null}, {!this.process.HasExited}"); // if (process != null && !process.HasExited) // { // process.Kill(); // process.WaitForExit(); // 可选:等待进程完全退出 // // Debug.Log($"VSCode 关闭了 {this.process.Id}"); // // haveClosedVSCode = true; // } // } // catch (Exception e) // { // Console.WriteLine(e); // } // // return false; // } private bool ProtoStyleIsError(string input) { Regex regexC = new Regex("_C_"); Regex regexS = new Regex("_S_"); bool isC = regexC.IsMatch(input); bool isS = regexS.IsMatch(input); bool isCorrect = (isC && !isS) || (!isC && isS); return !isCorrect; } private Texture2D CreateTexture(int width, int height, Color color) { Color[] pix = new Color[width * height]; for (int i = 0; i < pix.Length; i++) { pix[i] = color; } Texture2D tex = new Texture2D(width, height); tex.SetPixels(pix); tex.Apply(); return tex; } class ItemData { public string name; public int num; public string simpPath; public string fullPath; public short errorCode; public ItemData(string name, int num, string simpPath, string fullPath, short errorCode) { this.name = name; this.num = num; this.simpPath = simpPath; this.fullPath = fullPath; this.errorCode = errorCode; } } } }