EditorTools.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Reflection;
  5. using System.Linq;
  6. using System.IO;
  7. using System.Text;
  8. using UnityEngine;
  9. using UnityEditor;
  10. using UnityEditor.SceneManagement;
  11. namespace YooAsset.Editor
  12. {
  13. /// <summary>
  14. /// 编辑器工具类
  15. /// </summary>
  16. public static class EditorTools
  17. {
  18. static EditorTools()
  19. {
  20. InitAssembly();
  21. }
  22. #region Assembly
  23. #if UNITY_2019_4_OR_NEWER
  24. private static void InitAssembly()
  25. {
  26. }
  27. /// <summary>
  28. /// 获取带继承关系的所有类的类型
  29. /// </summary>
  30. public static List<Type> GetAssignableTypes(System.Type parentType)
  31. {
  32. TypeCache.TypeCollection collection = TypeCache.GetTypesDerivedFrom(parentType);
  33. return collection.ToList();
  34. }
  35. /// <summary>
  36. /// 获取带有指定属性的所有类的类型
  37. /// </summary>
  38. public static List<Type> GetTypesWithAttribute(System.Type attrType)
  39. {
  40. TypeCache.TypeCollection collection = TypeCache.GetTypesWithAttribute(attrType);
  41. return collection.ToList();
  42. }
  43. #else
  44. private static readonly List<Type> _cacheTypes = new List<Type>(10000);
  45. private static void InitAssembly()
  46. {
  47. _cacheTypes.Clear();
  48. Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
  49. foreach (Assembly assembly in assemblies)
  50. {
  51. List<Type> types = assembly.GetTypes().ToList();
  52. _cacheTypes.AddRange(types);
  53. }
  54. }
  55. /// <summary>
  56. /// 获取带继承关系的所有类的类型
  57. /// </summary>
  58. public static List<Type> GetAssignableTypes(System.Type parentType)
  59. {
  60. List<Type> result = new List<Type>();
  61. for (int i = 0; i < _cacheTypes.Count; i++)
  62. {
  63. Type type = _cacheTypes[i];
  64. if (parentType.IsAssignableFrom(type))
  65. {
  66. if (type.Name == parentType.Name)
  67. continue;
  68. result.Add(type);
  69. }
  70. }
  71. return result;
  72. }
  73. /// <summary>
  74. /// 获取带有指定属性的所有类的类型
  75. /// </summary>
  76. public static List<Type> GetTypesWithAttribute(System.Type attrType)
  77. {
  78. List<Type> result = new List<Type>();
  79. for (int i = 0; i < _cacheTypes.Count; i++)
  80. {
  81. Type type = _cacheTypes[i];
  82. if (type.GetCustomAttribute(attrType) != null)
  83. {
  84. result.Add(type);
  85. }
  86. }
  87. return result;
  88. }
  89. #endif
  90. /// <summary>
  91. /// 调用私有的静态方法
  92. /// </summary>
  93. /// <param name="type">类的类型</param>
  94. /// <param name="method">类里要调用的方法名</param>
  95. /// <param name="parameters">调用方法传入的参数</param>
  96. public static object InvokeNonPublicStaticMethod(System.Type type, string method, params object[] parameters)
  97. {
  98. var methodInfo = type.GetMethod(method, BindingFlags.NonPublic | BindingFlags.Static);
  99. if (methodInfo == null)
  100. {
  101. UnityEngine.Debug.LogError($"{type.FullName} not found method : {method}");
  102. return null;
  103. }
  104. return methodInfo.Invoke(null, parameters);
  105. }
  106. /// <summary>
  107. /// 调用公开的静态方法
  108. /// </summary>
  109. /// <param name="type">类的类型</param>
  110. /// <param name="method">类里要调用的方法名</param>
  111. /// <param name="parameters">调用方法传入的参数</param>
  112. public static object InvokePublicStaticMethod(System.Type type, string method, params object[] parameters)
  113. {
  114. var methodInfo = type.GetMethod(method, BindingFlags.Public | BindingFlags.Static);
  115. if (methodInfo == null)
  116. {
  117. UnityEngine.Debug.LogError($"{type.FullName} not found method : {method}");
  118. return null;
  119. }
  120. return methodInfo.Invoke(null, parameters);
  121. }
  122. #endregion
  123. #region EditorUtility
  124. /// <summary>
  125. /// 搜集资源
  126. /// </summary>
  127. /// <param name="searchType">搜集的资源类型</param>
  128. /// <param name="searchInFolders">指定搜索的文件夹列表</param>
  129. /// <returns>返回搜集到的资源路径列表</returns>
  130. public static string[] FindAssets(EAssetSearchType searchType, string[] searchInFolders)
  131. {
  132. // 注意:AssetDatabase.FindAssets()不支持末尾带分隔符的文件夹路径
  133. for (int i = 0; i < searchInFolders.Length; i++)
  134. {
  135. string folderPath = searchInFolders[i];
  136. searchInFolders[i] = folderPath.TrimEnd('/');
  137. }
  138. // 注意:获取指定目录下的所有资源对象(包括子文件夹)
  139. string[] guids;
  140. if (searchType == EAssetSearchType.All)
  141. guids = AssetDatabase.FindAssets(string.Empty, searchInFolders);
  142. else
  143. guids = AssetDatabase.FindAssets($"t:{searchType}", searchInFolders);
  144. // 注意:AssetDatabase.FindAssets()可能会获取到重复的资源
  145. List<string> result = new List<string>();
  146. for (int i = 0; i < guids.Length; i++)
  147. {
  148. string guid = guids[i];
  149. string assetPath = AssetDatabase.GUIDToAssetPath(guid);
  150. if (result.Contains(assetPath) == false)
  151. {
  152. result.Add(assetPath);
  153. }
  154. }
  155. // 返回结果
  156. return result.ToArray();
  157. }
  158. /// <summary>
  159. /// 搜集资源
  160. /// </summary>
  161. /// <param name="searchType">搜集的资源类型</param>
  162. /// <param name="searchInFolder">指定搜索的文件夹</param>
  163. /// <returns>返回搜集到的资源路径列表</returns>
  164. public static string[] FindAssets(EAssetSearchType searchType, string searchInFolder)
  165. {
  166. return FindAssets(searchType, new string[] { searchInFolder });
  167. }
  168. /// <summary>
  169. /// 打开搜索面板
  170. /// </summary>
  171. /// <param name="title">标题名称</param>
  172. /// <param name="defaultPath">默认搜索路径</param>
  173. /// <returns>返回选择的文件夹绝对路径,如果无效返回NULL</returns>
  174. public static string OpenFolderPanel(string title, string defaultPath, string defaultName = "")
  175. {
  176. string openPath = EditorUtility.OpenFolderPanel(title, defaultPath, defaultName);
  177. if (string.IsNullOrEmpty(openPath))
  178. return null;
  179. if (openPath.Contains("/Assets") == false)
  180. {
  181. Debug.LogWarning("Please select unity assets folder.");
  182. return null;
  183. }
  184. return openPath;
  185. }
  186. /// <summary>
  187. /// 打开搜索面板
  188. /// </summary>
  189. /// <param name="title">标题名称</param>
  190. /// <param name="defaultPath">默认搜索路径</param>
  191. /// <returns>返回选择的文件绝对路径,如果无效返回NULL</returns>
  192. public static string OpenFilePath(string title, string defaultPath, string extension = "")
  193. {
  194. string openPath = EditorUtility.OpenFilePanel(title, defaultPath, extension);
  195. if (string.IsNullOrEmpty(openPath))
  196. return null;
  197. if (openPath.Contains("/Assets") == false)
  198. {
  199. Debug.LogWarning("Please select unity assets file.");
  200. return null;
  201. }
  202. return openPath;
  203. }
  204. /// <summary>
  205. /// 显示进度框
  206. /// </summary>
  207. public static void DisplayProgressBar(string tips, int progressValue, int totalValue)
  208. {
  209. EditorUtility.DisplayProgressBar("进度", $"{tips} : {progressValue}/{totalValue}", (float)progressValue / totalValue);
  210. }
  211. /// <summary>
  212. /// 隐藏进度框
  213. /// </summary>
  214. public static void ClearProgressBar()
  215. {
  216. EditorUtility.ClearProgressBar();
  217. }
  218. #endregion
  219. #region EditorWindow
  220. public static void FocusUnitySceneWindow()
  221. {
  222. EditorWindow.FocusWindowIfItsOpen<SceneView>();
  223. }
  224. public static void CloseUnityGameWindow()
  225. {
  226. System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.GameView");
  227. EditorWindow.GetWindow(T, false, "GameView", true).Close();
  228. }
  229. public static void FocusUnityGameWindow()
  230. {
  231. System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.GameView");
  232. EditorWindow.GetWindow(T, false, "GameView", true);
  233. }
  234. public static void FocueUnityProjectWindow()
  235. {
  236. System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.ProjectBrowser");
  237. EditorWindow.GetWindow(T, false, "Project", true);
  238. }
  239. public static void FocusUnityHierarchyWindow()
  240. {
  241. System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.SceneHierarchyWindow");
  242. EditorWindow.GetWindow(T, false, "Hierarchy", true);
  243. }
  244. public static void FocusUnityInspectorWindow()
  245. {
  246. System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.InspectorWindow");
  247. EditorWindow.GetWindow(T, false, "Inspector", true);
  248. }
  249. public static void FocusUnityConsoleWindow()
  250. {
  251. System.Type T = Assembly.Load("UnityEditor").GetType("UnityEditor.ConsoleWindow");
  252. EditorWindow.GetWindow(T, false, "Console", true);
  253. }
  254. #endregion
  255. #region EditorConsole
  256. private static MethodInfo _clearConsoleMethod;
  257. private static MethodInfo ClearConsoleMethod
  258. {
  259. get
  260. {
  261. if (_clearConsoleMethod == null)
  262. {
  263. Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
  264. System.Type logEntries = assembly.GetType("UnityEditor.LogEntries");
  265. _clearConsoleMethod = logEntries.GetMethod("Clear");
  266. }
  267. return _clearConsoleMethod;
  268. }
  269. }
  270. /// <summary>
  271. /// 清空控制台
  272. /// </summary>
  273. public static void ClearUnityConsole()
  274. {
  275. ClearConsoleMethod.Invoke(new object(), null);
  276. }
  277. #endregion
  278. #region SceneUtility
  279. public static bool HasDirtyScenes()
  280. {
  281. var sceneCount = EditorSceneManager.sceneCount;
  282. for (var i = 0; i < sceneCount; ++i)
  283. {
  284. var scene = EditorSceneManager.GetSceneAt(i);
  285. if (scene.isDirty)
  286. return true;
  287. }
  288. return false;
  289. }
  290. #endregion
  291. #region StringUtility
  292. public static string RemoveFirstChar(string str)
  293. {
  294. if (string.IsNullOrEmpty(str))
  295. return str;
  296. return str.Substring(1);
  297. }
  298. public static string RemoveLastChar(string str)
  299. {
  300. if (string.IsNullOrEmpty(str))
  301. return str;
  302. return str.Substring(0, str.Length - 1);
  303. }
  304. public static List<string> StringToStringList(string str, char separator)
  305. {
  306. List<string> result = new List<string>();
  307. if (!String.IsNullOrEmpty(str))
  308. {
  309. string[] splits = str.Split(separator);
  310. foreach (string split in splits)
  311. {
  312. string value = split.Trim(); //移除首尾空格
  313. if (!String.IsNullOrEmpty(value))
  314. {
  315. result.Add(value);
  316. }
  317. }
  318. }
  319. return result;
  320. }
  321. public static T NameToEnum<T>(string name)
  322. {
  323. if (Enum.IsDefined(typeof(T), name) == false)
  324. {
  325. throw new ArgumentException($"Enum {typeof(T)} is not defined name {name}");
  326. }
  327. return (T)Enum.Parse(typeof(T), name);
  328. }
  329. #endregion
  330. #region 文件
  331. /// <summary>
  332. /// 创建文件所在的目录
  333. /// </summary>
  334. /// <param name="filePath">文件路径</param>
  335. public static void CreateFileDirectory(string filePath)
  336. {
  337. string destDirectory = Path.GetDirectoryName(filePath);
  338. CreateDirectory(destDirectory);
  339. }
  340. /// <summary>
  341. /// 创建文件夹
  342. /// </summary>
  343. public static bool CreateDirectory(string directory)
  344. {
  345. if (Directory.Exists(directory) == false)
  346. {
  347. Directory.CreateDirectory(directory);
  348. return true;
  349. }
  350. else
  351. {
  352. return false;
  353. }
  354. }
  355. /// <summary>
  356. /// 删除文件夹及子目录
  357. /// </summary>
  358. public static bool DeleteDirectory(string directory)
  359. {
  360. if (Directory.Exists(directory))
  361. {
  362. Directory.Delete(directory, true);
  363. return true;
  364. }
  365. else
  366. {
  367. return false;
  368. }
  369. }
  370. /// <summary>
  371. /// 文件重命名
  372. /// </summary>
  373. public static void FileRename(string filePath, string newName)
  374. {
  375. string dirPath = Path.GetDirectoryName(filePath);
  376. string destPath;
  377. if (Path.HasExtension(filePath))
  378. {
  379. string extentsion = Path.GetExtension(filePath);
  380. destPath = $"{dirPath}/{newName}{extentsion}";
  381. }
  382. else
  383. {
  384. destPath = $"{dirPath}/{newName}";
  385. }
  386. FileInfo fileInfo = new FileInfo(filePath);
  387. fileInfo.MoveTo(destPath);
  388. }
  389. /// <summary>
  390. /// 移动文件
  391. /// </summary>
  392. public static void MoveFile(string filePath, string destPath)
  393. {
  394. if (File.Exists(destPath))
  395. File.Delete(destPath);
  396. FileInfo fileInfo = new FileInfo(filePath);
  397. fileInfo.MoveTo(destPath);
  398. }
  399. /// <summary>
  400. /// 拷贝文件夹
  401. /// 注意:包括所有子目录的文件
  402. /// </summary>
  403. public static void CopyDirectory(string sourcePath, string destPath)
  404. {
  405. sourcePath = EditorTools.GetRegularPath(sourcePath);
  406. // If the destination directory doesn't exist, create it.
  407. if (Directory.Exists(destPath) == false)
  408. Directory.CreateDirectory(destPath);
  409. string[] fileList = Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories);
  410. foreach (string file in fileList)
  411. {
  412. string temp = EditorTools.GetRegularPath(file);
  413. string savePath = temp.Replace(sourcePath, destPath);
  414. CopyFile(file, savePath, true);
  415. }
  416. }
  417. /// <summary>
  418. /// 拷贝文件
  419. /// </summary>
  420. public static void CopyFile(string sourcePath, string destPath, bool overwrite)
  421. {
  422. if (File.Exists(sourcePath) == false)
  423. throw new FileNotFoundException(sourcePath);
  424. // 创建目录
  425. CreateFileDirectory(destPath);
  426. // 复制文件
  427. File.Copy(sourcePath, destPath, overwrite);
  428. }
  429. /// <summary>
  430. /// 清空文件夹
  431. /// </summary>
  432. /// <param name="folderPath">要清理的文件夹路径</param>
  433. public static void ClearFolder(string directoryPath)
  434. {
  435. if (Directory.Exists(directoryPath) == false)
  436. return;
  437. // 删除文件
  438. string[] allFiles = Directory.GetFiles(directoryPath);
  439. for (int i = 0; i < allFiles.Length; i++)
  440. {
  441. File.Delete(allFiles[i]);
  442. }
  443. // 删除文件夹
  444. string[] allFolders = Directory.GetDirectories(directoryPath);
  445. for (int i = 0; i < allFolders.Length; i++)
  446. {
  447. Directory.Delete(allFolders[i], true);
  448. }
  449. }
  450. /// <summary>
  451. /// 获取文件字节大小
  452. /// </summary>
  453. public static long GetFileSize(string filePath)
  454. {
  455. FileInfo fileInfo = new FileInfo(filePath);
  456. return fileInfo.Length;
  457. }
  458. /// <summary>
  459. /// 读取文件的所有文本内容
  460. /// </summary>
  461. public static string ReadFileAllText(string filePath)
  462. {
  463. if (File.Exists(filePath) == false)
  464. return string.Empty;
  465. return File.ReadAllText(filePath, Encoding.UTF8);
  466. }
  467. /// <summary>
  468. /// 读取文本的所有文本内容
  469. /// </summary>
  470. public static string[] ReadFileAllLine(string filePath)
  471. {
  472. if (File.Exists(filePath) == false)
  473. return null;
  474. return File.ReadAllLines(filePath, Encoding.UTF8);
  475. }
  476. /// <summary>
  477. /// 检测AssetBundle文件是否合法
  478. /// </summary>
  479. public static bool CheckBundleFileValid(byte[] fileData)
  480. {
  481. string signature = ReadStringToNull(fileData, 20);
  482. if (signature == "UnityFS" || signature == "UnityRaw" || signature == "UnityWeb" || signature == "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA")
  483. return true;
  484. else
  485. return false;
  486. }
  487. private static string ReadStringToNull(byte[] data, int maxLength)
  488. {
  489. List<byte> bytes = new List<byte>();
  490. for (int i = 0; i < data.Length; i++)
  491. {
  492. if (i >= maxLength)
  493. break;
  494. byte bt = data[i];
  495. if (bt == 0)
  496. break;
  497. bytes.Add(bt);
  498. }
  499. if (bytes.Count == 0)
  500. return string.Empty;
  501. else
  502. return Encoding.UTF8.GetString(bytes.ToArray());
  503. }
  504. #endregion
  505. #region 路径
  506. /// <summary>
  507. /// 获取规范的路径
  508. /// </summary>
  509. public static string GetRegularPath(string path)
  510. {
  511. return path.Replace('\\', '/').Replace("\\", "/"); //替换为Linux路径格式
  512. }
  513. /// <summary>
  514. /// 获取项目工程路径
  515. /// </summary>
  516. public static string GetProjectPath()
  517. {
  518. string projectPath = Path.GetDirectoryName(Application.dataPath);
  519. return GetRegularPath(projectPath);
  520. }
  521. /// <summary>
  522. /// 转换文件的绝对路径为Unity资源路径
  523. /// 例如 D:\\YourPorject\\Assets\\Works\\file.txt 替换为 Assets/Works/file.txt
  524. /// </summary>
  525. public static string AbsolutePathToAssetPath(string absolutePath)
  526. {
  527. string content = GetRegularPath(absolutePath);
  528. return Substring(content, "Assets/", true);
  529. }
  530. /// <summary>
  531. /// 转换Unity资源路径为文件的绝对路径
  532. /// 例如:Assets/Works/file.txt 替换为 D:\\YourPorject/Assets/Works/file.txt
  533. /// </summary>
  534. public static string AssetPathToAbsolutePath(string assetPath)
  535. {
  536. string projectPath = GetProjectPath();
  537. return $"{projectPath}/{assetPath}";
  538. }
  539. /// <summary>
  540. /// 递归查找目标文件夹路径
  541. /// </summary>
  542. /// <param name="root">搜索的根目录</param>
  543. /// <param name="folderName">目标文件夹名称</param>
  544. /// <returns>返回找到的文件夹路径,如果没有找到返回空字符串</returns>
  545. public static string FindFolder(string root, string folderName)
  546. {
  547. DirectoryInfo rootInfo = new DirectoryInfo(root);
  548. DirectoryInfo[] infoList = rootInfo.GetDirectories();
  549. for (int i = 0; i < infoList.Length; i++)
  550. {
  551. string fullPath = infoList[i].FullName;
  552. if (infoList[i].Name == folderName)
  553. return fullPath;
  554. string result = FindFolder(fullPath, folderName);
  555. if (string.IsNullOrEmpty(result) == false)
  556. return result;
  557. }
  558. return string.Empty;
  559. }
  560. /// <summary>
  561. /// 截取字符串
  562. /// 获取匹配到的后面内容
  563. /// </summary>
  564. /// <param name="content">内容</param>
  565. /// <param name="key">关键字</param>
  566. /// <param name="includeKey">分割的结果里是否包含关键字</param>
  567. /// <param name="searchBegin">是否使用初始匹配的位置,否则使用末尾匹配的位置</param>
  568. public static string Substring(string content, string key, bool includeKey, bool firstMatch = true)
  569. {
  570. if (string.IsNullOrEmpty(key))
  571. return content;
  572. int startIndex = -1;
  573. if (firstMatch)
  574. startIndex = content.IndexOf(key); //返回子字符串第一次出现位置
  575. else
  576. startIndex = content.LastIndexOf(key); //返回子字符串最后出现的位置
  577. // 如果没有找到匹配的关键字
  578. if (startIndex == -1)
  579. return content;
  580. if (includeKey)
  581. return content.Substring(startIndex);
  582. else
  583. return content.Substring(startIndex + key.Length);
  584. }
  585. #endregion
  586. }
  587. }