////////////////////////////////////////////////////////////////////////// // // 文件:Assets/Editor/Tools/EditorUtil.cs // 作者:Xoen Xie // 时间:2023/07/18 // 描述:工具通用函数集 // 说明: // ////////////////////////////////////////////////////////////////////////// using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; using System.IO; using System.Diagnostics; using System.Linq; using System.Xml; using TGS; using UnityEditor.SceneManagement; using UnityEditorInternal; using UnityEngine.SceneManagement; using Debug = UnityEngine.Debug; using Object = System.Object; using Random = UnityEngine.Random; public static class EditorUtil { public static bool StartProcess(string sFilename, string sArgs, string sWorkDir) { Process ps = null; bool fail = false; try { ProcessStartInfo StartInfo = new ProcessStartInfo(); StartInfo.FileName = sFilename; StartInfo.Arguments = sArgs; StartInfo.CreateNoWindow = true; StartInfo.UseShellExecute = false; if (!string.IsNullOrEmpty(sWorkDir)) StartInfo.WorkingDirectory = sWorkDir; StartInfo.RedirectStandardError = true; StartInfo.RedirectStandardOutput = true; ps = new Process(); ps.StartInfo = StartInfo; ps.Start(); StreamReader readerErr = ps.StandardError; //截取错误流 string line = readerErr.ReadLine(); while (!readerErr.EndOfStream) { line = readerErr.ReadLine(); line = line + "\r\n"; } ps.WaitForExit(); var strOut = ps.StandardOutput.ReadToEnd(); UnityEngine.Debug.Log(strOut); string fileName = null; string arguments = null; if (ps.ExitCode != 0 && !fail) { fail = true; fileName = ps.StartInfo.FileName; arguments = ps.StartInfo.Arguments; } if (fail) { throw new Exception(string.Format("ExitCode:{0}]\nStartProcess Fail.FileName=[{1}]\nArg=[{2}\n{3}", ps.ExitCode, fileName, arguments, line)); } } catch (Exception e) { UnityEngine.Debug.LogError(e.Message); } finally { ps.Dispose(); } return !fail; } enum FindOp { None = 0, Start, End, } public static bool ReplaceContentByTag(string sFilename, string sTagStart, string sTagEnd, string sNewContent) { bool bFindStart = false; bool bFindEnd = false; string[] lines = File.ReadAllLines(sFilename); string sSrcContent = ""; FindOp nFindFlag = FindOp.Start; for (uint i = 0; i < lines.Length; ++i) { switch (nFindFlag) { case FindOp.Start: { sSrcContent += lines[i] + "\n"; if (lines[i].Contains(sTagStart)) { nFindFlag = FindOp.End; sSrcContent += sNewContent; bFindStart = true; } } break; case FindOp.End: { if (lines[i].Contains(sTagEnd)) { nFindFlag = FindOp.None; sSrcContent += lines[i] + "\n"; bFindEnd = true; } } break; default: sSrcContent += lines[i] + "\n"; break; } } if (bFindStart && bFindEnd) { File.WriteAllText(sFilename, sSrcContent); return true; } return false; } public static bool AppendFileByTag(string sFilename, string sTag, string sNewContent) { if (string.IsNullOrEmpty(sNewContent)) { return false; } bool bFindTag = false; string[] lines = File.ReadAllLines(sFilename); string sSrcContent = ""; FindOp nFindFlag = FindOp.Start; for (uint i = 0; i < lines.Length; ++i) { switch (nFindFlag) { case FindOp.Start: { if (lines[i].Contains(sTag)) { nFindFlag = FindOp.None; sSrcContent += "\n"; sSrcContent += sNewContent; bFindTag = true; } sSrcContent += lines[i] + "\n"; } break; default: sSrcContent += lines[i] + "\n"; break; } } if (bFindTag) { File.WriteAllText(sFilename, sSrcContent); return true; } return false; } public static string FirstLetterToUpper(string str) { if (str.Length > 0) return char.ToUpper(str[0]) + str.Substring(1); return str.ToUpper(); } public static int GetInt(XmlElement e, string attribute = null) { string tmp = null; if (string.IsNullOrEmpty(attribute)) tmp = e.InnerText; else tmp = e.GetAttribute(attribute); if (string.IsNullOrEmpty(tmp)) { return 0; } try { return Convert.ToInt32(tmp); } catch (Exception) { throw; } } public static uint GetUInt(XmlElement e, string attribute = null) { string tmp = null; if (string.IsNullOrEmpty(attribute)) tmp = e.InnerText; else tmp = e.GetAttribute(attribute); if (string.IsNullOrEmpty(tmp)) { return 0; } try { return Convert.ToUInt32(tmp); } catch (Exception) { throw; } } public static long GetLong(XmlElement e, string attribute = null) { string tmp = null; if (string.IsNullOrEmpty(attribute)) tmp = e.InnerText; else tmp = e.GetAttribute(attribute); if (string.IsNullOrEmpty(tmp)) { return 0; } try { return Convert.ToInt64(tmp); } catch (Exception) { throw; } } public static ulong GetULong(XmlElement e, string attribute = null) { string tmp = null; if (string.IsNullOrEmpty(attribute)) tmp = e.InnerText; else tmp = e.GetAttribute(attribute); if (string.IsNullOrEmpty(tmp)) { return 0; } try { return Convert.ToUInt64(tmp); } catch (Exception) { throw; } } public static float GetFloat(XmlElement e, string attribute = null) { string tmp = null; if (string.IsNullOrEmpty(attribute)) tmp = e.InnerText; else tmp = e.GetAttribute(attribute); if (string.IsNullOrEmpty(tmp)) { return 0; } try { return Convert.ToSingle(tmp); } catch (Exception) { throw; } } public static string GetString(XmlElement e, string attribute = null) { if (string.IsNullOrEmpty(attribute)) return e.InnerText; return e.GetAttribute(attribute); } public static bool GetBoolean(XmlElement e, string attribute = null) { string tmp = null; if (string.IsNullOrEmpty(attribute)) tmp = e.InnerText; else tmp = e.GetAttribute(attribute); if (string.IsNullOrEmpty(tmp)) return false; tmp = tmp.ToLower(); if (tmp.Equals("true") || tmp.Equals("1")) return true; return false; } public static string GetRealPath(string savePath) { var finalPath = savePath; if (finalPath.StartsWith("/")) { finalPath = Application.dataPath + finalPath; } { var dirInfo = new DirectoryInfo(finalPath); if (dirInfo.Exists) { return dirInfo.FullName; } } { var fileInfo = new FileInfo(finalPath); if (fileInfo.Exists) { return fileInfo.FullName; } } return finalPath; } //调用此函数可以保证你保存的asset的路径不会覆盖到其他asset,此函数会自动给asset重命名,所以限制是你不能将asset的名字作为你逻辑的数据 public static void CreateAnUniqueAsset(UnityEngine.Object asset, string path) { if (asset == null || string.IsNullOrEmpty(path)) { Debug.LogError("输入参数不合法!"); return; } var savePath = GetAssetUniquePath(path, out var assetName); asset.name = assetName; AssetDatabase.CreateAsset(asset, savePath); } /// /// 获取一个唯一的路径用于保存asset,假设传入的路径已经存在,则自动生成一个新的 /// /// 尝试保存的路径 /// 第一个string是唯一路径,第二个string是asset的新名字 public static string GetAssetUniquePath(string assetPath, out string assetName) { assetName = Path.GetFileNameWithoutExtension(assetPath); var extension = Path.GetExtension(assetPath); var dir = Path.GetDirectoryName(assetPath); var savedAsset = AssetDatabase.LoadAssetAtPath(assetPath); if (savedAsset == null) { return assetPath; } assetName = $"{assetName}_{Random.Range(0, 10000)}"; var newAssetPath = $"{dir}/{assetName}{extension}"; return GetAssetUniquePath(newAssetPath, out assetName); } public static void LockInspector(bool isLock) { Type t = typeof(EditorWindow).Assembly.GetType("UnityEditor.InspectorWindow"); var window = EditorWindow.GetWindow(t); t.GetProperty("isLocked").SetValue(window, isLock); } public static bool IsStringContains(this string str1, string str2) { return str1.ToLower().Contains(str2.ToLower()); } public static Bounds? GetMaxBounds(this GameObject go) { var meshRenderers = go.GetComponentsInChildren(); if (meshRenderers == null || meshRenderers.Length == 0) return null; Bounds result = new(); bool hasSetFirst = false; foreach (var msr in meshRenderers) { if (!hasSetFirst) { result = msr.bounds; hasSetFirst = true; } else { result.Encapsulate(msr.bounds); } } return result; } //[MenuItem("Tools/批量修改场景")] public static void CorrectLevels() { var guids = AssetDatabase.FindAssets("t:SceneAsset", new[] { "Assets/Scenes/Maps" }); var ArtPrefab = AssetDatabase.LoadAssetAtPath("Assets/Scenes/Maps/Template/MapDefault/Art.prefab"); var tgsMat = AssetDatabase.LoadAssetAtPath("Assets/Scenes/Maps/Template/MapDefault/TGS_Default.mat"); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); if (string.IsNullOrEmpty(path) || path.Contains("LevelDefault")) continue; EditorSceneManager.OpenScene(path, OpenSceneMode.Single); // var Art = GameObject.Find("Art"); // if (Art != null && !PrefabUtility.IsAnyPrefabInstanceRoot(Art)) // { // var convertSetting = new ConvertToPrefabInstanceSettings // { // objectMatchMode = ObjectMatchMode.ByName, // componentsNotMatchedBecomesOverride = true, // recordPropertyOverridesOfMatches = true, // gameObjectsNotMatchedBecomesOverride = true // }; // PrefabUtility.ConvertToPrefabInstance(Art, ArtPrefab, convertSetting, InteractionMode.AutomatedAction); // } // // if (UnityEngine.Object.FindObjectOfType() == null) // { // GameObject mapEditor = new("MapEditor"); // mapEditor.AddComponent(); // } // // if (UnityEngine.Object.FindObjectOfType() == null) // { // GameObject levelEditor = new("LevelEditor"); // levelEditor.AddComponent(); // } var tgs = UnityEngine.Object.FindObjectOfType(); var meshRenderer = tgs.GetComponent(); meshRenderer.sharedMaterial = tgsMat; EditorSceneManager.MarkAllScenesDirty(); EditorSceneManager.SaveOpenScenes(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } } //[MenuItem("Tools/修改level为map")] private static void ChangeStringLevelToMap() { var guids = AssetDatabase.FindAssets("t:Object", new[] { "Assets/Scenes/Maps" }); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); if (string.IsNullOrEmpty(path)) continue; var oldName = Path.GetFileName(path); var levelName = "Level"; var mapName = "Map"; if (oldName.Contains(levelName)) { AssetDatabase.RenameAsset(path, oldName.Replace(levelName, mapName)); } } } public static void DrawArrowInScene(Vector3 start,Vector3 end,float arrowLength = 1) { Handles.DrawLine(start, end); Vector3 direction = (end - start).normalized; Vector3 arrowEnd = end - (direction * arrowLength); float arrowHeadAngle = 20f; Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * Vector3.forward; Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * Vector3.forward; Handles.DrawLine(end, arrowEnd + right * (arrowLength * 0.4f)); Handles.DrawLine(end, arrowEnd + left * (arrowLength * 0.4f)); Handles.DrawLine(arrowEnd + right * (arrowLength * 0.4f), arrowEnd); Handles.DrawLine(arrowEnd + left * (arrowLength * 0.4f), arrowEnd); } public static string GetCurrentAssetDirectory() { foreach (UnityEngine.Object obj in Selection.GetFiltered(SelectionMode.Assets)) { var path = AssetDatabase.GetAssetPath(obj); if (string.IsNullOrEmpty(path)) continue; if (System.IO.Directory.Exists(path)) return path; else if (System.IO.File.Exists(path)) return System.IO.Path.GetDirectoryName(path); } return "Assets"; } public static void AddPathToAddressable(string assetPath,string groupName) { #if USE_ADDRESSABLES // 获取 AddressableAssetSettings var settings = AddressableAssetSettingsDefaultObject.Settings; // 获取或创建一个资源组 var group = settings.FindGroup(groupName); if (group == null) { Debug.LogError($"找不到资源组:{groupName}"); return; } // 创建或移动条目 var guid = AssetDatabase.AssetPathToGUID(assetPath); var entry = settings.CreateOrMoveEntry(guid, group); //为了防止出现增量更新bug,需要将场景放在group的根下 List sceneEntries = new(); entry.GatherAllAssets(sceneEntries, true, true, false, e => e.MainAssetType == typeof(SceneAsset) || e.IsFolder); sceneEntries = sceneEntries.Where(e => e.MainAssetType == typeof(SceneAsset)).ToList(); if (sceneEntries.Count > 0) { EditorUtility.SetDirty(group); foreach (var sceneEntry in sceneEntries) { settings.MoveEntry(sceneEntry, group); } } #endif } }