diff --git a/Editor/Postprocessor/Atlas/AtlasConfiguration.cs b/Editor/Postprocessor/Atlas/AtlasConfiguration.cs index c009fba..d68e383 100644 --- a/Editor/Postprocessor/Atlas/AtlasConfiguration.cs +++ b/Editor/Postprocessor/Atlas/AtlasConfiguration.cs @@ -1,38 +1,31 @@ #if UNITY_EDITOR using UnityEngine; using UnityEditor; +using UnityEngine.Serialization; [AlicizaX.Editor.Setting.FilePath("ProjectSettings/AtlasConfiguration.asset")] public class AtlasConfiguration : AlicizaX.Editor.Setting.ScriptableSingleton { - [Header("目录设置")] - [Tooltip("生成的图集输出目录")] - public string outputAtlasDir = "Assets/Art/Atlas"; + [Tooltip("生成的图集输出目录")] public string outputAtlasDir = "Assets/Art/Atlas"; - [Tooltip("需要生成图集的UI根目录")] - public string sourceAtlasRoot = "Assets/Bundles/UIRaw/Atlas"; + [Tooltip("需要生成图集的UI根目录")] public string sourceAtlasRoot = "Assets/Bundles/UIRaw/Atlas"; - [Tooltip("不需要生成图集的UI目录")] - public string excludeFolder = "Assets/Bundles/UIRaw/Raw"; - [Header("平台格式设置")] - public TextureImporterFormat androidFormat = TextureImporterFormat.ASTC_6x6; + [Tooltip("平台格式设置")] public TextureImporterFormat androidFormat = TextureImporterFormat.ASTC_6x6; public TextureImporterFormat iosFormat = TextureImporterFormat.ASTC_5x5; public TextureImporterFormat webglFormat = TextureImporterFormat.ASTC_6x6; - [Header("PackingSetting")] - public int padding = 2; + [Tooltip("PackingSetting")] public int padding = 2; public bool enableRotation = true; public int blockOffset = 1; public bool tightPacking = true; - [Header("其他设置")] - [Range(0, 100)] public int compressionQuality = 50; + [Tooltip("其他设置")] public int compressionQuality = 50; public bool autoGenerate = true; public bool enableLogging = true; public bool enableV2 = true; - [Header("排除关键词")] - public string[] excludeKeywords = { "_Delete", "_Temp" }; + [Tooltip("排除关键词")] public string[] excludeKeywords = { "_Delete", "_Temp" }; + [Tooltip("不需要生成图集的UI目录")] public string[] excludeFolders = { "Assets/Bundles/UIRaw/Raw" }; } #endif diff --git a/Editor/Postprocessor/Atlas/AtlasEditorWindow.cs b/Editor/Postprocessor/Atlas/AtlasEditorWindow.cs index 0faad6c..9cdd672 100644 --- a/Editor/Postprocessor/Atlas/AtlasEditorWindow.cs +++ b/Editor/Postprocessor/Atlas/AtlasEditorWindow.cs @@ -1,52 +1,177 @@ #if UNITY_EDITOR -using System; using UnityEditor; +using UnityEditorInternal; using UnityEngine; public class AtlasConfigWindow : EditorWindow { - [MenuItem("Tools/图集工具/配置面板")] + [MenuItem("Tools/图集管理工具/Configuration Panel")] public static void ShowWindow() { - GetWindow("Atlas Config").minSize = new Vector2(450, 400); + GetWindow("Atlas Config").minSize = new Vector2(500, 600); } - private Vector2 _scrollPosition; + private Vector2 scrollPosition; + private ReorderableList keywordsList; + private ReorderableList foldersList; + private bool showExclusionSettings = true; - private int[] _paddingEnum = new int[] { 2, 4, 8 }; - private bool _showExcludeKeywords = false; // 新增折叠状态变量 + private void OnEnable() + { + var config = AtlasConfiguration.Instance; + + // 初始化关键词列表 + keywordsList = CreateKeywordsList(config); + + // 初始化目录列表 + foldersList = CreateFoldersList(config); + } private void OnGUI() { var config = AtlasConfiguration.Instance; - using (var scrollScope = new EditorGUILayout.ScrollViewScope(_scrollPosition)) + using (var scroll = new EditorGUILayout.ScrollViewScope(scrollPosition)) { - _scrollPosition = scrollScope.scrollPosition; + scrollPosition = scroll.scrollPosition; EditorGUI.BeginChangeCheck(); - DrawFolderSettings(config); + DrawDirectorySettings(config); DrawPlatformSettings(config); DrawPackingSettings(config); - DrawAdvancedSettings(config); + DrawCompressionSettings(config); + DrawExclusionSettings(config); + DrawFeatureToggles(config); if (EditorGUI.EndChangeCheck()) { AtlasConfiguration.Save(true); AssetDatabase.Refresh(); } - - DrawActionButtons(); } + + DrawActionButtons(); } - private void DrawFolderSettings(AtlasConfiguration config) + #region List Creation + private ReorderableList CreateKeywordsList(AtlasConfiguration config) { - GUILayout.Label("目录设置", EditorStyles.boldLabel); - config.outputAtlasDir = DrawFolderField("输出目录", config.outputAtlasDir); - config.sourceAtlasRoot = DrawFolderField("收集目录", config.sourceAtlasRoot); - config.excludeFolder = DrawFolderField("排除目录", config.excludeFolder); + var list = new ReorderableList(config.excludeKeywords, typeof(string), true, true, true, true) + { + drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Excluded Keywords"), + elementHeight = EditorGUIUtility.singleLineHeight + 4, + drawElementCallback = (rect, index, active, focused) => + { + rect.y += 2; + config.excludeKeywords[index] = EditorGUI.TextField( + new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), + config.excludeKeywords[index] + ); + }, + onAddCallback = _ => + { + ArrayUtility.Add(ref config.excludeKeywords, ""); + EditorGUI.FocusTextInControl(null); + }, + onRemoveCallback = _ => ArrayUtility.RemoveAt(ref config.excludeKeywords, keywordsList.index) + }; + + return list; + } + + private ReorderableList CreateFoldersList(AtlasConfiguration config) + { + var list = new ReorderableList(config.excludeFolders, typeof(string), true, true, true, true) + { + drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Excluded Folders"), + elementHeight = EditorGUIUtility.singleLineHeight + 4, + drawElementCallback = (rect, index, active, focused) => + { + rect.y += 2; + var textRect = new Rect(rect.x, rect.y, rect.width - 60, EditorGUIUtility.singleLineHeight); + var buttonRect = new Rect(rect.x + rect.width - 55, rect.y, 50, EditorGUIUtility.singleLineHeight); + + config.excludeFolders[index] = EditorGUI.TextField(textRect, config.excludeFolders[index]); + + if (GUI.Button(buttonRect, "Browse")) + { + var path = EditorUtility.OpenFolderPanel("Select Folder", Application.dataPath, ""); + if (!string.IsNullOrEmpty(path)) + { + config.excludeFolders[index] = "Assets" + path.Substring(Application.dataPath.Length); + } + } + }, + onAddCallback = _ => + { + ArrayUtility.Add(ref config.excludeFolders, ""); + EditorGUI.FocusTextInControl(null); + }, + onRemoveCallback = _ => ArrayUtility.RemoveAt(ref config.excludeFolders, foldersList.index) + }; + + return list; + } + #endregion + + #region Drawing Methods + private void DrawDirectorySettings(AtlasConfiguration config) + { + GUILayout.Label("Directory Settings", EditorStyles.boldLabel); + config.outputAtlasDir = DrawFolderField("Output Directory", config.outputAtlasDir); + config.sourceAtlasRoot = DrawFolderField("Source Root", config.sourceAtlasRoot); + EditorGUILayout.Space(); + } + + private void DrawPlatformSettings(AtlasConfiguration config) + { + GUILayout.Label("Platform Settings", EditorStyles.boldLabel); + config.androidFormat = (TextureImporterFormat)EditorGUILayout.EnumPopup("Android Format", config.androidFormat); + config.iosFormat = (TextureImporterFormat)EditorGUILayout.EnumPopup("iOS Format", config.iosFormat); + config.webglFormat = (TextureImporterFormat)EditorGUILayout.EnumPopup("WebGL Format", config.webglFormat); + EditorGUILayout.Space(); + } + + private void DrawPackingSettings(AtlasConfiguration config) + { + GUILayout.Label("Packing Settings", EditorStyles.boldLabel); + config.padding = EditorGUILayout.IntPopup("Padding", config.padding, + new[] { "2", "4", "8" }, new[] { 2, 4, 8 }); + config.blockOffset = EditorGUILayout.IntField("Block Offset", config.blockOffset); + config.enableRotation = EditorGUILayout.Toggle("Enable Rotation", config.enableRotation); + config.tightPacking = EditorGUILayout.Toggle("Trim Transparency", config.tightPacking); + EditorGUILayout.Space(); + } + + private void DrawCompressionSettings(AtlasConfiguration config) + { + GUILayout.Label("Compression", EditorStyles.boldLabel); + config.compressionQuality = EditorGUILayout.IntSlider("Quality", config.compressionQuality, 0, 100); + EditorGUILayout.Space(); + } + + private void DrawExclusionSettings(AtlasConfiguration config) + { + showExclusionSettings = EditorGUILayout.BeginFoldoutHeaderGroup(showExclusionSettings, "Exclusion Settings"); + if (showExclusionSettings) + { + EditorGUILayout.HelpBox("Items matching these criteria will be excluded from atlas generation", + MessageType.Info); + + keywordsList.DoLayoutList(); + EditorGUILayout.Space(10); + foldersList.DoLayoutList(); + } + EditorGUILayout.EndFoldoutHeaderGroup(); + } + + private void DrawFeatureToggles(AtlasConfiguration config) + { + GUILayout.Label("Features", EditorStyles.boldLabel); + config.autoGenerate = EditorGUILayout.Toggle("Auto Generate", config.autoGenerate); + config.enableLogging = EditorGUILayout.Toggle("Enable Logging", config.enableLogging); + config.enableV2 = EditorGUILayout.Toggle("Use V2 Packing", config.enableV2); EditorGUILayout.Space(); } @@ -55,7 +180,7 @@ public class AtlasConfigWindow : EditorWindow using (new EditorGUILayout.HorizontalScope()) { path = EditorGUILayout.TextField(label, path); - if (GUILayout.Button("选择", GUILayout.Width(60))) + if (GUILayout.Button("Select", GUILayout.Width(60))) { var newPath = EditorUtility.OpenFolderPanel(label, Application.dataPath, ""); if (!string.IsNullOrEmpty(newPath)) @@ -64,95 +189,25 @@ public class AtlasConfigWindow : EditorWindow } } } - return path; } - private void DrawPlatformSettings(AtlasConfiguration config) - { - GUILayout.Label("平台设置", EditorStyles.boldLabel); - config.androidFormat = (TextureImporterFormat)EditorGUILayout.EnumPopup("Android 格式", config.androidFormat); - config.iosFormat = (TextureImporterFormat)EditorGUILayout.EnumPopup("iOS 格式", config.iosFormat); - config.webglFormat = (TextureImporterFormat)EditorGUILayout.EnumPopup("WebGL 格式", config.webglFormat); - config.compressionQuality = EditorGUILayout.IntSlider("压缩质量", config.compressionQuality, 0, 100); - EditorGUILayout.Space(); - } - - private void DrawPackingSettings(AtlasConfiguration config) - { - GUILayout.Label("PackingSetting", EditorStyles.boldLabel); - config.padding = EditorGUILayout.IntPopup("Padding", - config.padding, - Array.ConvertAll(_paddingEnum, x => x.ToString()), - _paddingEnum); - config.blockOffset = EditorGUILayout.IntField("BlockOffset", config.blockOffset); - config.enableRotation = EditorGUILayout.Toggle("Enable Rotation", config.enableRotation); - config.tightPacking = EditorGUILayout.Toggle("剔除透明区域", config.tightPacking); - EditorGUILayout.Space(); - } - - - private void DrawAdvancedSettings(AtlasConfiguration config) - { - GUILayout.Label("高级设置", EditorStyles.boldLabel); - config.autoGenerate = EditorGUILayout.Toggle("自动生成", config.autoGenerate); - config.enableLogging = EditorGUILayout.Toggle("启用日志", config.enableLogging); - config.enableV2 = EditorGUILayout.Toggle("启用V2打包", config.enableV2); - // 优化后的排除关键词显示 - _showExcludeKeywords = EditorGUILayout.BeginFoldoutHeaderGroup(_showExcludeKeywords, "排除关键词"); - if (_showExcludeKeywords) - { - int keywordCount = config.excludeKeywords?.Length ?? 0; - int newCount = EditorGUILayout.IntField("数量", keywordCount); - - // 调整数组大小 - if (newCount != keywordCount) - { - Array.Resize(ref config.excludeKeywords, newCount); - } - - // 绘制每个元素 - for (int i = 0; i < newCount; i++) - { - config.excludeKeywords[i] = EditorGUILayout.TextField($"关键词 {i + 1}", - config.excludeKeywords[i] ?? ""); - } - - // 添加快捷按钮 - EditorGUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (GUILayout.Button("添加", GUILayout.Width(60))) - { - Array.Resize(ref config.excludeKeywords, newCount + 1); - } - - if (GUILayout.Button("清空", GUILayout.Width(60))) - { - config.excludeKeywords = Array.Empty(); - } - - EditorGUILayout.EndHorizontal(); - } - - EditorGUILayout.EndFoldoutHeaderGroup(); - - EditorGUILayout.Space(); - } - private void DrawActionButtons() { + EditorGUILayout.Space(20); using (new EditorGUILayout.HorizontalScope()) { - if (GUILayout.Button("立即生成所有图集", GUILayout.Height(30))) + if (GUILayout.Button("Generate All Atlases", GUILayout.Height(30))) { EditorSpriteSaveInfo.ForceGenerateAll(); } - if (GUILayout.Button("清空缓存", GUILayout.Height(30))) + if (GUILayout.Button("Clear Cache", GUILayout.Height(30))) { EditorSpriteSaveInfo.ClearCache(); } } } + #endregion } #endif diff --git a/Editor/Postprocessor/Atlas/EditorSpriteSaveInfo.cs b/Editor/Postprocessor/Atlas/EditorSpriteSaveInfo.cs index 43ecc50..f4ec75d 100644 --- a/Editor/Postprocessor/Atlas/EditorSpriteSaveInfo.cs +++ b/Editor/Postprocessor/Atlas/EditorSpriteSaveInfo.cs @@ -30,10 +30,51 @@ public static class EditorSpriteSaveInfo _initialized = true; } + + public static void ConvertToSprite(string assetPath) + { + if (!ShouldProcess(assetPath)) return; + + #region Convert Sprite + + TextureImporter ti = AssetImporter.GetAtPath(assetPath) as TextureImporter; + if (ti == null) return; + bool modify = false; + if (ti.textureType != TextureImporterType.Sprite) + { + ti.textureType = TextureImporterType.Sprite; + ti.spriteImportMode = SpriteImportMode.Single; + Debug.Log("设置Sprite"); + modify = true; + } + + if (!string.IsNullOrEmpty(ti.spritePackingTag)) + { + ti.spritePackingTag = string.Empty; + modify = true; + } + + var setting = new TextureImporterSettings(); + ti.ReadTextureSettings(setting); + if (setting.spriteGenerateFallbackPhysicsShape) + { + setting.spriteGenerateFallbackPhysicsShape = false; + ti.SetTextureSettings(setting); + modify = true; + } + + if (modify) + { + ti.SaveAndReimport(); + } + #endregion + } + public static void OnImportSprite(string assetPath) { if (!ShouldProcess(assetPath)) return; + var atlasName = GetAtlasName(assetPath); if (string.IsNullOrEmpty(atlasName)) return; @@ -51,6 +92,7 @@ public static class EditorSpriteSaveInfo } } + public static void OnDeleteSprite(string assetPath) { if (!ShouldProcess(assetPath)) return; @@ -207,33 +249,33 @@ public static class EditorSpriteSaveInfo } #if UNITY_2022_1_OR_NEWER - private static void ConfigureAtlasV2Settings(SpriteAtlasImporter atlasImporter) + private static void ConfigureAtlasV2Settings(SpriteAtlasImporter atlasImporter) + { + void SetPlatform(string platform, TextureImporterFormat format) { - void SetPlatform(string platform, TextureImporterFormat format) - { - var settings = atlasImporter.GetPlatformSettings(platform); - if (settings == null) return; - ; - settings.overridden = true; - settings.format = format; - settings.compressionQuality = Config.compressionQuality; - atlasImporter.SetPlatformSettings(settings); - } - - SetPlatform("Android", Config.androidFormat); - SetPlatform("iPhone", Config.iosFormat); - SetPlatform("WebGL", Config.webglFormat); - - var packingSettings = new SpriteAtlasPackingSettings - { - padding = Config.padding, - enableRotation = Config.enableRotation, - blockOffset = Config.blockOffset, - enableTightPacking = Config.tightPacking, - enableAlphaDilation = true - }; - atlasImporter.packingSettings = packingSettings; + var settings = atlasImporter.GetPlatformSettings(platform); + if (settings == null) return; + ; + settings.overridden = true; + settings.format = format; + settings.compressionQuality = Config.compressionQuality; + atlasImporter.SetPlatformSettings(settings); } + + SetPlatform("Android", Config.androidFormat); + SetPlatform("iPhone", Config.iosFormat); + SetPlatform("WebGL", Config.webglFormat); + + var packingSettings = new SpriteAtlasPackingSettings + { + padding = Config.padding, + enableRotation = Config.enableRotation, + blockOffset = Config.blockOffset, + enableTightPacking = Config.tightPacking, + enableAlphaDilation = true + }; + atlasImporter.packingSettings = packingSettings; + } #else private static void ConfigureAtlasV2Settings(SpriteAtlasAsset spriteAtlasAsset) { @@ -316,7 +358,7 @@ public static class EditorSpriteSaveInfo private static bool IsExcluded(string path) { - return path.StartsWith(Config.excludeFolder) || + return Config.excludeFolders.Any(path.StartsWith) || Config.excludeKeywords.Any(k => path.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0); } diff --git a/Editor/Postprocessor/Atlas/SpritePostprocessor.cs b/Editor/Postprocessor/Atlas/SpritePostprocessor.cs index f0321ca..06e6ab0 100644 --- a/Editor/Postprocessor/Atlas/SpritePostprocessor.cs +++ b/Editor/Postprocessor/Atlas/SpritePostprocessor.cs @@ -1,4 +1,5 @@ -using UnityEditor; +using System.Linq; +using UnityEditor; using UnityEngine; public class SpritePostprocessor : AssetPostprocessor @@ -40,6 +41,7 @@ public class SpritePostprocessor : AssetPostprocessor { ProcessAssets(importedAssets, (path) => { + EditorSpriteSaveInfo.ConvertToSprite(path); EditorSpriteSaveInfo.OnImportSprite(path); LogProcessed("[Added]", path); }); @@ -96,7 +98,7 @@ public class SpritePostprocessor : AssetPostprocessor if (assetPath.StartsWith("Packages/")) return false; if (!assetPath.StartsWith(config.sourceAtlasRoot)) return false; - if (assetPath.StartsWith(config.excludeFolder)) return false; + if (config.excludeFolders.Any(assetPath.StartsWith)) return false; if (!IsValidImageFile(assetPath)) return false;