ProtoViewWindow.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Text.RegularExpressions;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using Debug = UnityEngine.Debug;
  9. namespace ET.Editor
  10. {
  11. /// <summary> 用于显示当前项目中的Proto </summary>
  12. public class ProtoViewWindow : EditorWindow
  13. {
  14. [MenuItem("ET/Proto/ProtoView")]
  15. static void Init()
  16. {
  17. GetWindow<ProtoViewWindow>("Proto View");
  18. }
  19. //显示
  20. private bool togSort = false;
  21. private bool lastTogSort = false;
  22. private Vector2 scrollViewPos;
  23. private string vsCodePath = @"C:\Users\XXX\AppData\Local\Programs\Microsoft VS Code\Code.exe";
  24. private string newVSCodePath = "";
  25. // private Texture2D bgTexture;
  26. private bool togOpenFolder = false; //是否同时打开文件夹
  27. //数据
  28. private List<ItemData> protoItems = new List<ItemData>();
  29. private string delDataPath;
  30. private string protoConbinePath;
  31. // private Process process;
  32. private void OnEnable()
  33. {
  34. #if UNITY_EDITOR_WIN
  35. delDataPath = Application.dataPath.Replace("Assets", "").Replace("/", "\\");
  36. #elif UNITY_EDITOR_OSX
  37. delDataPath = Application.dataPath.Replace("Assets", "");
  38. #endif
  39. protoConbinePath = this.delDataPath + "Temp";
  40. RefreshDataList();
  41. // bgTexture = CreateTexture(1,1, Color.clear);
  42. }
  43. // private void OnDestroy()
  44. // {
  45. // this.CloseVSCode();
  46. // }
  47. public void OnGUI()
  48. {
  49. GUIStyle rightBtnStyle = new GUIStyle(GUI.skin.FindStyle("button"));
  50. rightBtnStyle.alignment = TextAnchor.MiddleRight;
  51. rightBtnStyle.normal.textColor = Color.white; //默认状态下字体颜色
  52. // rightBtnStyle.normal.background = bgTexture;
  53. GUIStyle errorBtnStyle = new GUIStyle(GUI.skin.FindStyle("button"));
  54. errorBtnStyle.alignment = TextAnchor.MiddleRight;
  55. errorBtnStyle.normal.textColor = Color.red; //默认状态下字体颜色
  56. errorBtnStyle.focused.textColor = Color.red;
  57. errorBtnStyle.hover.textColor = Color.red;
  58. // errorBtnStyle.normal.background = bgTexture;
  59. GUIStyle existBtnStyle = new GUIStyle(GUI.skin.FindStyle("button"));
  60. existBtnStyle.alignment = TextAnchor.MiddleRight;
  61. existBtnStyle.normal.textColor = Color.yellow;
  62. existBtnStyle.focused.textColor = Color.yellow;
  63. existBtnStyle.hover.textColor = Color.yellow;
  64. // existBtnStyle.normal.background = bgTexture;
  65. GUIStyle rightTxtStyle = new GUIStyle(GUI.skin.FindStyle("label"));
  66. GUIStyle errorTxtStyle = new GUIStyle(GUI.skin.FindStyle("label"));
  67. errorTxtStyle.normal.textColor = Color.red;
  68. GUIStyle existTxtStyle = new GUIStyle(GUI.skin.FindStyle("label"));
  69. existTxtStyle.normal.textColor = Color.yellow;
  70. GUIStyle greenTxtStyle = new GUIStyle(GUI.skin.FindStyle("label"));
  71. greenTxtStyle.normal.textColor = Color.green;
  72. GUILayout.Space(5);
  73. //顶部工具栏
  74. GUILayout.BeginHorizontal();
  75. if (GUILayout.Button("check", GUILayout.Width(100)))
  76. {
  77. RefreshDataList();
  78. }
  79. GUILayout.Space(10);
  80. bool newTogSort = GUILayout.Toggle(togSort, "排序");
  81. if (newTogSort != lastTogSort)
  82. {
  83. lastTogSort = newTogSort;
  84. togSort = newTogSort;
  85. RefreshDataList();
  86. }
  87. GUILayout.Space(10);
  88. float tmpWidth = 420;
  89. var rectNormal = GUILayoutUtility.GetRect(tmpWidth, 20);
  90. Event curEvent = Event.current;
  91. if (Event.current.type == EventType.MouseUp)
  92. {
  93. if (rectNormal.Contains(curEvent.mousePosition))
  94. {
  95. newVSCodePath = EditorUtility.OpenFilePanel("select VSCode Path", newVSCodePath, "");
  96. }
  97. }
  98. if (string.IsNullOrEmpty(newVSCodePath))
  99. {
  100. vsCodePath = "点击这里, 设置VSCode路径";
  101. EditorGUI.LabelField(rectNormal, this.vsCodePath, greenTxtStyle);
  102. }
  103. else
  104. {
  105. vsCodePath = this.newVSCodePath;
  106. EditorGUI.LabelField(rectNormal, this.vsCodePath);
  107. }
  108. GUI.Box(new Rect(rectNormal.x-5, rectNormal.y, tmpWidth+10, rectNormal.height), GUIContent.none, EditorStyles.helpBox);
  109. GUILayout.FlexibleSpace ();
  110. if (GUILayout.Button("open in vscode", GUILayout.Width(100)))
  111. {
  112. try
  113. {
  114. CombineProtoOpenByVSCode();
  115. }
  116. catch (System.Exception e)
  117. {
  118. Debug.LogError($"打开VSCode报错, VSCode路径错误, 请先点击 <{this.vsCodePath}> 设置VSCode路径 {e}");
  119. // Debug.LogError($"打开VSCode报错, VSCode路径错误:<{this.vsCodePath}>, 具体错误:{e}");
  120. }
  121. }
  122. togOpenFolder = GUILayout.Toggle(togOpenFolder, "");
  123. GUILayout.Space(10);
  124. if (GUILayout.Button("save", GUILayout.Width(60)))
  125. {
  126. SeprateProtoAndSave();
  127. }
  128. if (GUILayout.Button("Prot2CS", GUILayout.Width(60)))
  129. {
  130. EditorApplication.ExecuteMenuItem("ET/Proto/Proto2CS");
  131. }
  132. GUILayout.EndHorizontal();
  133. GUILayout.Space(10);
  134. //内容
  135. scrollViewPos = GUILayout.BeginScrollView(scrollViewPos);
  136. for (int i = 0; i < protoItems.Count; i++)
  137. {
  138. GUILayout.BeginHorizontal();
  139. var temBtnStyle = rightBtnStyle;
  140. var temTxtStyle = rightTxtStyle;
  141. switch (protoItems[i].errorCode)
  142. {
  143. case 1:
  144. temBtnStyle = errorBtnStyle;
  145. temTxtStyle = errorTxtStyle;
  146. break;
  147. case 2:
  148. temBtnStyle = existBtnStyle;
  149. temTxtStyle = existTxtStyle;
  150. break;
  151. case 3:
  152. temBtnStyle = errorBtnStyle;
  153. temTxtStyle = errorTxtStyle;
  154. break;
  155. }
  156. if (GUILayout.Button(protoItems[i].name, temBtnStyle, GUILayout.Width(250)))
  157. {
  158. UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(protoItems[i].fullPath.Replace(delDataPath, ""));
  159. if (obj != null)
  160. {
  161. EditorGUIUtility.PingObject(obj);
  162. Selection.activeObject = obj;
  163. }
  164. else
  165. {
  166. Debug.LogError("File not found at path: " + protoItems[i].fullPath.Replace(delDataPath, "") + ", \n" + delDataPath + ", \n" + Application.dataPath);
  167. }
  168. }
  169. GUILayout.Label(protoItems[i].fullPath, temTxtStyle);
  170. GUILayout.EndHorizontal();
  171. }
  172. GUILayout.EndScrollView();
  173. }
  174. private List<string> FindProtoFilesInPackagesDirectory()
  175. {
  176. List<string> protoFiles = new List<string>();
  177. string packagesPath = Path.Combine(Directory.GetCurrentDirectory(), "Packages");
  178. if (Directory.Exists(packagesPath))
  179. {
  180. SearchDirectoryForProtoFiles(packagesPath, protoFiles);
  181. }
  182. else
  183. {
  184. Debug.LogWarning("Packages directory not found at: " + packagesPath);
  185. }
  186. return protoFiles;
  187. }
  188. private void SearchDirectoryForProtoFiles(string directoryPath, List<string> protoFiles)
  189. {
  190. try
  191. {
  192. string[] filePaths = Directory.GetFiles(directoryPath, "*.proto", SearchOption.AllDirectories);
  193. protoFiles.AddRange(filePaths);
  194. }
  195. catch (System.Exception ex)
  196. {
  197. Debug.LogError("Error searching directory for .proto files: " + ex.Message);
  198. }
  199. }
  200. private void CombineProtoOpenByVSCode()
  201. {
  202. string folderPath = "";
  203. string subFolderPath = "";
  204. #if UNITY_EDITOR_WIN
  205. folderPath = protoConbinePath + "\\TmpProto";
  206. subFolderPath = folderPath + "\\";
  207. #elif UNITY_EDITOR_OSX
  208. folderPath = protoConbinePath + "/TmpProto";
  209. subFolderPath = folderPath + "/";
  210. #endif
  211. //先移除所有之前的
  212. DirectoryInfo directoryInfo = new DirectoryInfo(subFolderPath);
  213. if (directoryInfo.Exists)
  214. {
  215. FileInfo[] files = directoryInfo.GetFiles();
  216. foreach (FileInfo file in files)
  217. {
  218. try
  219. {
  220. file.Delete();
  221. }
  222. catch (System.Exception ex)
  223. {
  224. Debug.LogError("Failed to delete file: " + file.FullName + ". Error: " + ex.Message);
  225. }
  226. }
  227. }
  228. //增加现有的
  229. foreach (var item in protoItems)
  230. {
  231. if (File.Exists(item.fullPath))
  232. {
  233. if (!Directory.Exists(subFolderPath))
  234. {
  235. Directory.CreateDirectory(subFolderPath);
  236. }
  237. File.Copy(item.fullPath, subFolderPath + item.simpPath, true);
  238. }
  239. }
  240. if (togOpenFolder)
  241. {
  242. EditorUtility.RevealInFinder(folderPath);
  243. }
  244. OpenInVSCode(folderPath);
  245. }
  246. private void SeprateProtoAndSave()
  247. {
  248. // if (!CloseVSCode()) return;
  249. string folderPath = "";
  250. string subFolderPath = "";
  251. #if UNITY_EDITOR_WIN
  252. folderPath = protoConbinePath + "\\TmpProto";
  253. subFolderPath = folderPath + "\\";
  254. #elif UNITY_EDITOR_OSX
  255. folderPath = protoConbinePath + "/TmpProto";
  256. subFolderPath = folderPath + "/";
  257. #endif
  258. foreach (var item in protoItems)
  259. {
  260. string tmpFilePath = subFolderPath + item.simpPath;
  261. if (!File.Exists(tmpFilePath))
  262. {
  263. Debug.LogError($"路径不存在: {tmpFilePath}");
  264. return;
  265. }
  266. File.Copy(tmpFilePath, item.fullPath, true);
  267. }
  268. AssetDatabase.Refresh();
  269. //移除临时文件中的所有proto
  270. if (Directory.Exists(subFolderPath))
  271. {
  272. foreach (var item in protoItems)
  273. {
  274. string tmpFilePath = subFolderPath + item.simpPath;
  275. if (File.Exists(tmpFilePath))
  276. {
  277. File.Delete(tmpFilePath);
  278. }
  279. }
  280. }
  281. }
  282. private void RefreshDataList()
  283. {
  284. protoItems.Clear();
  285. List<string> tmpProtoFiles = FindProtoFilesInPackagesDirectory();
  286. // Debug.Log($"当前项目中所有的Proto -----------> {tmpProtoFiles.Count}");
  287. // foreach (string protoFile in tmpProtoFiles)
  288. // {
  289. // Debug.Log(protoFile);
  290. // }
  291. foreach (string fullPath in tmpProtoFiles)
  292. {
  293. string[] tmpStrs = null;
  294. #if UNITY_EDITOR_WIN
  295. tmpStrs = fullPath.Split("\\");
  296. #elif UNITY_EDITOR_OSX
  297. tmpStrs = fullPath.Split("/");
  298. #endif
  299. string simpPath = tmpStrs[^1];
  300. string name = simpPath.Split(".")[0];
  301. string numStr = name.Split("_")[^1];
  302. int num = -1;
  303. short errorCode = 0;
  304. if (ProtoStyleIsError(name))
  305. {
  306. Debug.LogError($"Proto文件名 格式有误: {simpPath}");
  307. errorCode = 1;
  308. protoItems.Add(new ItemData(name, num, simpPath, fullPath, errorCode));
  309. }
  310. else
  311. {
  312. if (int.TryParse(numStr, out num))
  313. {
  314. foreach (var item in protoItems)
  315. {
  316. if (item.num == num)
  317. {
  318. errorCode = 2;
  319. item.errorCode = errorCode;
  320. Debug.LogError($"Proto文件名 数值相同: {num}, {simpPath}, {item.simpPath}");
  321. }
  322. }
  323. protoItems.Add(new ItemData(name, num, simpPath, fullPath, errorCode));
  324. }
  325. else
  326. {
  327. Debug.LogError($"Proto文件名 格式错误: {simpPath}");
  328. errorCode = 3;
  329. protoItems.Add(new ItemData(name, num, simpPath, fullPath, errorCode));
  330. }
  331. }
  332. }
  333. if (togSort)
  334. {
  335. protoItems.Sort((p1, p2) => p1.num.CompareTo(p2.num));
  336. }
  337. }
  338. private void OpenInVSCode(string dirPath)
  339. {
  340. Debug.Log($"tackor-----> {dirPath}");
  341. // 创建一个新的进程启动信息
  342. ProcessStartInfo startInfo = new ProcessStartInfo
  343. {
  344. FileName = vsCodePath,
  345. // 如果需要传递参数,可以在这里添加
  346. Arguments = dirPath,
  347. UseShellExecute = true, // 允许操作系统使用 shell 来启动进程
  348. RedirectStandardOutput = false, // 通常不需要重定向输出
  349. RedirectStandardError = false, // 通常不需要重定向错误
  350. CreateNoWindow = false // 是否创建新窗口,取决于你的需求
  351. };
  352. // process = Process.Start(startInfo);
  353. using (Process process = Process.Start(startInfo))
  354. {
  355. // 如果需要等待进程结束,可以使用 process.WaitForExit()
  356. // 在这个例子中,我们立即返回
  357. // process.WaitForExit();
  358. }
  359. }
  360. // private bool CloseVSCode()
  361. // {
  362. // bool haveClosedVSCode = false;
  363. // try
  364. // {
  365. // Log.Debug($"tackor--------> {this.process != null}, {!this.process.HasExited}");
  366. // if (process != null && !process.HasExited)
  367. // {
  368. // process.Kill();
  369. // process.WaitForExit(); // 可选:等待进程完全退出
  370. // // Debug.Log($"VSCode 关闭了 {this.process.Id}");
  371. //
  372. // haveClosedVSCode = true;
  373. // }
  374. // }
  375. // catch (Exception e)
  376. // {
  377. // Console.WriteLine(e);
  378. // }
  379. //
  380. // return false;
  381. // }
  382. private bool ProtoStyleIsError(string input)
  383. {
  384. Regex regexC = new Regex("_C_");
  385. Regex regexS = new Regex("_S_");
  386. bool isC = regexC.IsMatch(input);
  387. bool isS = regexS.IsMatch(input);
  388. bool isCorrect = (isC && !isS) || (!isC && isS);
  389. return !isCorrect;
  390. }
  391. private Texture2D CreateTexture(int width, int height, Color color)
  392. {
  393. Color[] pix = new Color[width * height];
  394. for (int i = 0; i < pix.Length; i++)
  395. {
  396. pix[i] = color;
  397. }
  398. Texture2D tex = new Texture2D(width, height);
  399. tex.SetPixels(pix);
  400. tex.Apply();
  401. return tex;
  402. }
  403. class ItemData
  404. {
  405. public string name;
  406. public int num;
  407. public string simpPath;
  408. public string fullPath;
  409. public short errorCode;
  410. public ItemData(string name, int num, string simpPath, string fullPath, short errorCode)
  411. {
  412. this.name = name;
  413. this.num = num;
  414. this.simpPath = simpPath;
  415. this.fullPath = fullPath;
  416. this.errorCode = errorCode;
  417. }
  418. }
  419. }
  420. }