using System; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using TyphoonFormatCSharp; using UnityEditor; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using Object = UnityEngine.Object; namespace TyphoonUI { public class TyphoonUIEditor { /*加载编辑器图片*/ public static Texture GetOrLoadEditorTexture(string textureName, ref Texture tex) { if (tex != null) { return tex; } var path = $"{Paths.RootPath}/Editor/Texture/{textureName}"; tex = AssetDatabase.LoadAssetAtPath(path); return tex; } /*创建或加载配置*/ public static T CreateOrLoadScriptableObject(string path, ref T t) where T : ScriptableObject { if (t != null) { return t; } t = AssetDatabase.LoadAssetAtPath(path); if (t == null) { t = ScriptableObject.CreateInstance(); var dir = Path.GetDirectoryName(path); CreateDirectoryIfNotExist(dir); AssetDatabase.CreateAsset(t, path); AssetDatabase.Refresh(); } return t; } /*补充文件夹*/ public static void CreateDirectoryIfNotExist(string folder) { if (!Directory.Exists(folder)) { Directory.CreateDirectory(folder); AssetDatabase.Refresh(); } } /*补充文件夹*/ public static void CreateFileDirectoryIfNotExist(string file) { CreateDirectoryIfNotExist(Path.GetDirectoryName(file)); } /*导出节点*/ public static void ExportNodes() { var select = Selection.activeGameObject; if (select == null) { var menu = new GenericMenu(); menu.AddItem(new GUIContent("无法导出,请选择一个有效节点"), false, null); menu.ShowAsContext(); return; } var obj = GetComponentInParentIncludeInActive(select); if (obj == null) { var menu = new GenericMenu(); menu.AddItem(new GUIContent($"无法导出,夫级节点不存在{nameof(UIObject)}"), false, null); menu.ShowAsContext(); return; } //弹出导出窗口 ExportNodesWindow.Open(select); } [MenuItem("TyphoonUI/安装", priority = -1000)] public static void Install() { //生成_Editor.cs Generate_EditorCsFile(); //生成UIBinding.cs GenerateBindingCsFile(); //打开安装窗口 InstallerWindow.Open(); } [MenuItem("TyphoonUI/导入第三方组件库")] public static void ImportThirdComponentPackage() { AssetDatabase.ImportPackage(Paths.ThirdPackagePath, true); } [MenuItem("TyphoonUI/开发者/导出 Third.unitypackage")] private static void ExportThirdPackages() { var folder = $"Assets/Typhoon_Gen/TyphoonUI/Third"; AssetDatabase.ExportPackage(new[] { folder }, Paths.ThirdPackagePath, ExportPackageOptions.Recurse); AssetDatabase.Refresh(); EditorUtility.RevealInFinder(Paths.ThirdPackagePath); } [MenuItem("TyphoonUI/开发者/导出 Third.unitypackage", true)] private static bool ExportThirdPackagesFunc() { return Directory.Exists("Assets/typhoon.unity.ui"); } [MenuItem("GameObject/关闭 RaycastTarget (TyphoonUI)")] public static void DisableRaycastTarget() { var graphic = new HashSet(); var targets = Selection.gameObjects; foreach (var target in targets) { var arr = target.GetComponentsInChildren(); foreach (var element in arr) { graphic.Add(element); } } foreach (var element in graphic) { element.raycastTarget = false; EditorUtility.SetDirty(element); EditorUtility.SetDirty(element.gameObject); Debug.Log($"[关闭 RaycastTarget] {element}"); } } [MenuItem("GameObject/关闭 Maskable (TyphoonUI)")] public static void DisableMaskable() { var graphic = new HashSet(); var targets = Selection.gameObjects; foreach (var target in targets) { var arr = target.GetComponentsInChildren(); foreach (var element in arr) { graphic.Add(element); } } foreach (var element in graphic) { element.maskable = false; EditorUtility.SetDirty(element); EditorUtility.SetDirty(element.gameObject); Debug.Log($"[关闭 Maskable] {element}"); } } [MenuItem("GameObject/开启 Maskable (TyphoonUI)")] public static void EnableMaskable() { var graphic = new HashSet(); var targets = Selection.gameObjects; foreach (var target in targets) { var arr = target.GetComponentsInChildren(); foreach (var element in arr) { graphic.Add(element); } } foreach (var element in graphic) { element.maskable = true; EditorUtility.SetDirty(element); EditorUtility.SetDirty(element.gameObject); Debug.Log($"[开启 Maskable] {element}"); } } //生成_Editor.cs public static void Generate_EditorCsFile() { var path = $"Assets/Typhoon_Gen/TyphoonUI/Editor/_Editor.cs"; var dir = Path.GetDirectoryName(path); CreateDirectoryIfNotExist(dir); var code = $@"using UnityEditor; namespace TyphoonUI {{ public class _Editor {{ [MenuItem(""GameObject/导出节点 {Preferences.Default.Keymap}"")] public static void ExportNodes() {{ TyphoonUIEditor.ExportNodes(); }} }} }} "; Debug.Log($"创建:{path}"); File.WriteAllText(path, code); FormatCSharp.Format(path, () => { AssetDatabase.Refresh(); }); } /*获取层级结构*/ public static List GetSiblingIndexes(Transform node, List list = null) { if (list == null) { list = new List(); } var index = node.GetSiblingIndex(); list.Insert(0, index); if (node.parent == null) { return list; } else { return GetSiblingIndexes(node.parent, list); } } /*移除无效成员名字符*/ public static string RemoveMemberInvalidCharacters(string input) { // 使用正则表达式移除非字母、数字和下划线的字符 string pattern = "[^0-9a-zA-Z_]"; string cleanedInput = Regex.Replace(input, pattern, String.Empty); return cleanedInput; } /*ping*/ public static void PingObject(string path, bool forceProject = true) { var target = AssetDatabase.LoadAssetAtPath(path); PingObject(target, forceProject); } /*ping*/ public static void PingObject(Object obj, bool forceProject = true) { if (AssetDatabase.IsMainAsset(obj) && forceProject) { EditorApplication.ExecuteMenuItem("Window/General/Project"); } EditorGUIUtility.PingObject(obj); } /*标记改动并保存*/ public static void SetDirtyAndSave(Object target) { EditorUtility.SetDirty(target); AssetDatabase.SaveAssets(); } public static Transform RemapTo(GameObject root, HierarchyIndex hierarchyIndex) { var result = root.transform; foreach (var idx in hierarchyIndex) { result = result.transform.GetChild(idx); } return result.transform; } /// /// 是否有类型 /// public static bool HasType(string fullClass) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblies) { var type = assembly.GetType(fullClass); if (type != null) { return true; } } return false; } [MenuItem("GameObject/TyphoonUI/其它/创建UI画布-横屏")] public static void CreateUICanvasLandscape() { var canvas = InstantiateUICanvas(new Vector2(1920, 1080), 1); Selection.activeGameObject = canvas.gameObject; } [MenuItem("GameObject/TyphoonUI/其它/创建UI画布-竖屏")] public static void CreateUICanvasPortrait() { var canvas = InstantiateUICanvas(new Vector2(1080, 1920), 0); Selection.activeGameObject = canvas.gameObject; } public static UICanvas InstantiateUICanvas(Vector2 design, float matchWidthOrHeight) { var clone = new GameObject("UICanvas"); var canvas = clone.AddComponent(); var camera = Object.FindObjectOfType(); if (camera == null) { camera = InstantiateUICamera(); } var eventSystem = Object.FindObjectOfType(); if (eventSystem == null) { var go = new GameObject("EventSystem"); go.AddComponent(); go.AddComponent(); } canvas.Canvas.worldCamera = camera.Camera; canvas.Canvas.renderMode = RenderMode.ScreenSpaceCamera; canvas.CanvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; canvas.CanvasScaler.referenceResolution = design; canvas.CanvasScaler.matchWidthOrHeight = matchWidthOrHeight; return canvas; } [MenuItem("GameObject/TyphoonUI/其它/创建UI相机")] public static UICamera InstantiateUICamera() { var clone = new GameObject("UICamera"); var camera = clone.AddComponent(); camera.Camera.nearClipPlane = -100; camera.Camera.farClipPlane = 1000; Selection.activeGameObject = clone; return camera; } //生成UIBinding.cs public static void GenerateBindingCsFile() { var code = @"using UnityEngine.Scripting; namespace TyphoonUI { /// /// UI绑定 /// [Preserve] public class UIBinding : AbstractBinding { public override void Binding() { /*绑定面板*/ // [示例]: Bind(""Default"",""DemoPanel"", CanvasLayer.Bottom); /*绑定组件*/ // [示例]:Bind(""Default"",""DemoComponent""); } } }"; var path = Paths.UIBindingCsFile; CreateDirectoryIfNotExist(Path.GetDirectoryName(path)); if (!File.Exists(path)) { Debug.Log($"添加:{path}"); File.WriteAllText(path, code); AssetDatabase.Refresh(); PingObject(path); FormatCSharp.Format(path, () => { AssetDatabase.Refresh(); }); } } [MenuItem("TyphoonUI/编辑UIBinding.cs", priority = 5000)] public static void EditorBindingCs() { if (!File.Exists(Paths.UIBindingCsFile)) { GenerateBindingCsFile(); } var monoScript = AssetDatabase.LoadAssetAtPath(Paths.UIBindingCsFile); if (monoScript != null) { AssetDatabase.OpenAsset(monoScript); } } [MenuItem("TyphoonUI/更新所有 UIPackage 资源", priority = 3000)] public static void UpdatePackages() { var packages = GetAllPackages(); foreach (var package in packages) { Debug.Log($"更新UIPackage:{package}"); UIPackageEditor.UpdatePackage(package); } } /// /// 获取所有packages /// public static List GetAllPackages() { var result = new List(); var guids = AssetDatabase.FindAssets($"t:{typeof(UIPackage).FullName}", new[] { "Assets" }); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); var package = AssetDatabase.LoadAssetAtPath(path); result.Add(package); } return result; } /// /// 获取所有父节点组件 /// public static List GetComponentInParentIncludeInActive(GameObject target) where T : Component { var result = new List(); var node = target.transform; while (node != null) { var com = node.GetComponent(); if (com != null) { result.Add(com); } node = node.parent; } return result; } //显示消息 public static void ShowMessageBox(string msg, Action onClick = null) { var click = EditorUtility.DisplayDialog("提示", msg, "是"); if (click) { onClick?.Invoke(); } } //转换路径到unity路径 public static string PathToUnityPath(string path) { var full = Path.GetFullPath(path); var proj = Path.GetFullPath(Application.dataPath); full = full.Replace("\\", "/"); proj = proj.Replace("\\", "/"); return full.Replace(proj, "Assets"); } public static T LoadOrCreateIfNotExists(string path) where T : ScriptableObject { var result = AssetDatabase.LoadAssetAtPath(path); if (result == null) { CreateDirectoryIfNotExist(Path.GetDirectoryName(path)); result = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(result, PathToUnityPath(path)); AssetDatabase.Refresh(); } return result; } //创建预设-面板资源 [MenuItem("GameObject/UI/Panel (TyphoonUI)")] public static void CreatePreset_Panel() { var selectNode = Selection.activeGameObject; var canvasSource = $"{Paths.CORE_SOURCE_ROOT_PATH}/UICanvas_Bottom.prefab"; var cameraSource = $"{Paths.CORE_SOURCE_ROOT_PATH}/UICamera.prefab"; var eventSystemSource = $"{Paths.CORE_SOURCE_ROOT_PATH}/EventSystem.prefab"; var uiCamera = Object.FindObjectOfType(); var eventSystem = Object.FindObjectOfType(); var parentNode = selectNode; if (selectNode == null) { //创建内置画布 if (!File.Exists(canvasSource) || !File.Exists(cameraSource) || !File.Exists(eventSystemSource)) { ShowMessageBox("未创建核心资源,去安装?", Install); return; } //创建画布 parentNode = PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath(canvasSource)) as GameObject; parentNode.transform.SetAsLastSibling(); if (uiCamera == null) { var go = PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath(cameraSource)) as GameObject; go.transform.ResetTransformParams(); uiCamera = go.GetComponent(); go.transform.SetAsLastSibling(); } if (eventSystem == null) { var go = PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath(eventSystemSource)) as GameObject; go.transform.ResetTransformParams(); go.transform.SetAsLastSibling(); } try { parentNode.GetComponent().worldCamera = uiCamera.Camera; } catch (Exception _) { } } var clone = new GameObject("Panel"); clone.layer = LayerMask.NameToLayer("UI"); clone.transform.parent = parentNode.transform; var rectTransform = clone.AddComponentIfNotExists(); rectTransform.ResetTransformParams(); rectTransform.SetAnchor(AnchorPresets.StretchAll); rectTransform.sizeDelta = Vector2.zero; var image = clone.AddComponentIfNotExists(); image.color = new Color(1, 1, 1, 0.5f); clone.AddComponentIfNotExists(); clone.AddComponentIfNotExists(); clone.AddComponentIfNotExists(); clone.AddComponentIfNotExists(); Selection.activeGameObject = clone; } [MenuItem("GameObject/UI/UIScrollRect (横向)(TyphoonUI)")] public static void CreatePreset_UIScrollRect_Horizontal() { var selectNode = Selection.activeGameObject; if (selectNode == null) { ShowMessageBox("需要一个有效节点"); return; } var presetPath = $"{Paths.RootPath}/Res/Preset/UIScrollRect_Horizontal.prefab"; var source = AssetDatabase.LoadAssetAtPath(presetPath); var clone = GameObject.Instantiate(source, selectNode.transform); clone.name = "UIScrollRect_Horizontal"; clone.transform.ResetTransformParams(); var nodes = clone.GetComponentsInChildrenIncludeInactive(); foreach (var transform in nodes) { transform.gameObject.layer = selectNode.gameObject.layer; } Selection.activeObject = clone; } [MenuItem("GameObject/UI/UIScrollRect (垂直)(TyphoonUI)")] public static void CreatePreset_UIScrollRect_Vertical() { var selectNode = Selection.activeGameObject; if (selectNode == null) { ShowMessageBox("需要一个有效节点"); return; } var presetPath = $"{Paths.RootPath}/Res/Preset/UIScrollRect_Vertical.prefab"; var source = AssetDatabase.LoadAssetAtPath(presetPath); var clone = GameObject.Instantiate(source, selectNode.transform); clone.name = "UIScrollRect_Vertical"; clone.transform.ResetTransformParams(); var nodes = clone.GetComponentsInChildrenIncludeInactive(); foreach (var transform in nodes) { transform.gameObject.layer = selectNode.gameObject.layer; } Selection.activeObject = clone; } [MenuItem("GameObject/UI/UIScrollRectLoop (横向)(TyphoonUI)")] public static void CreatePreset_UIScrollRectLoop_Horizontal() { var selectNode = Selection.activeGameObject; if (selectNode == null) { ShowMessageBox("需要一个有效节点"); return; } var presetPath = $"{Paths.RootPath}/Res/Preset/UIScrollRectLoop_Horizontal.prefab"; var source = AssetDatabase.LoadAssetAtPath(presetPath); var clone = GameObject.Instantiate(source, selectNode.transform); clone.name = "UIScrollRectLoop_Horizontal"; clone.transform.ResetTransformParams(); var nodes = clone.GetComponentsInChildrenIncludeInactive(); foreach (var transform in nodes) { transform.gameObject.layer = selectNode.gameObject.layer; } Selection.activeObject = clone; } [MenuItem("GameObject/UI/UIScrollRectLoop (垂直)(TyphoonUI)")] public static void CreatePreset_UIScrollRectLoop_Vertical() { var selectNode = Selection.activeGameObject; if (selectNode == null) { ShowMessageBox("需要一个有效节点"); return; } var presetPath = $"{Paths.RootPath}/Res/Preset/UIScrollRectLoop_Vertical.prefab"; var source = AssetDatabase.LoadAssetAtPath(presetPath); var clone = GameObject.Instantiate(source, selectNode.transform); clone.name = "UIScrollRectLoop_Vertical"; clone.transform.ResetTransformParams(); var nodes = clone.GetComponentsInChildrenIncludeInactive(); foreach (var transform in nodes) { transform.gameObject.layer = selectNode.gameObject.layer; } Selection.activeObject = clone; } [MenuItem("GameObject/UI/UIGridView (横向)(TyphoonUI)")] public static void CreatePreset_UIGridView_Horizontal() { var selectNode = Selection.activeGameObject; if (selectNode == null) { ShowMessageBox("需要一个有效节点"); return; } var presetPath = $"{Paths.RootPath}/Res/Preset/UIGridView_Horizontal.prefab"; var source = AssetDatabase.LoadAssetAtPath(presetPath); var clone = GameObject.Instantiate(source, selectNode.transform); clone.name = "UIGridView_Horizontal"; clone.transform.ResetTransformParams(); var nodes = clone.GetComponentsInChildrenIncludeInactive(); foreach (var transform in nodes) { transform.gameObject.layer = selectNode.gameObject.layer; } Selection.activeObject = clone; } [MenuItem("GameObject/UI/UIGridView (垂直)(TyphoonUI)")] public static void CreatePreset_UIGridView_Vertical() { var selectNode = Selection.activeGameObject; if (selectNode == null) { ShowMessageBox("需要一个有效节点"); return; } var presetPath = $"{Paths.RootPath}/Res/Preset/UIGridView_Vertical.prefab"; var source = AssetDatabase.LoadAssetAtPath(presetPath); var clone = GameObject.Instantiate(source, selectNode.transform); clone.name = "UIGridView_Vertical"; clone.transform.ResetTransformParams(); var nodes = clone.GetComponentsInChildrenIncludeInactive(); foreach (var transform in nodes) { transform.gameObject.layer = selectNode.gameObject.layer; } Selection.activeObject = clone; } [MenuItem("GameObject/UI/UIGridViewLoop (横向)(TyphoonUI)")] public static void CreatePreset_UIGridViewLoop_Horizontal() { var selectNode = Selection.activeGameObject; if (selectNode == null) { ShowMessageBox("需要一个有效节点"); return; } var presetPath = $"{Paths.RootPath}/Res/Preset/UIGridViewLoop_Horizontal.prefab"; var source = AssetDatabase.LoadAssetAtPath(presetPath); var clone = GameObject.Instantiate(source, selectNode.transform); clone.name = "UIGridViewLoop_Horizontal"; clone.transform.ResetTransformParams(); var nodes = clone.GetComponentsInChildrenIncludeInactive(); foreach (var transform in nodes) { transform.gameObject.layer = selectNode.gameObject.layer; } Selection.activeObject = clone; } [MenuItem("GameObject/UI/UIGridViewLoop (垂直)(TyphoonUI)")] public static void CreatePreset_UIGridViewLoop_Vertical() { var selectNode = Selection.activeGameObject; if (selectNode == null) { ShowMessageBox("需要一个有效节点"); return; } var presetPath = $"{Paths.RootPath}/Res/Preset/UIGridViewLoop_Vertical.prefab"; var source = AssetDatabase.LoadAssetAtPath(presetPath); var clone = GameObject.Instantiate(source, selectNode.transform); clone.name = "UIGridViewLoop_Vertical"; clone.transform.ResetTransformParams(); var nodes = clone.GetComponentsInChildrenIncludeInactive(); foreach (var transform in nodes) { transform.gameObject.layer = selectNode.gameObject.layer; } Selection.activeObject = clone; } public static Type GetType(string fullName) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblies) { var type = assembly.GetType(fullName); if (type != null) { return type; } } return null; } } }