#if UNITY_EDITOR using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace AIGame.EditorTools { /// Common 标准控件的九宫格导入规则,基于 Doc/UI_Normal_Set.md。 public static class UiSpriteNineSliceRules { private const string CommonRoot = "Assets/Bundles/UI/Texture/Common/"; // W,H,L,R,T,B:设计尺寸与边界;实际切图按尺寸换算,并包含 2px 透明留边。 private static readonly Dictionary Rules = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "ui_panel_main", new[] { 480, 256, 40, 40, 40, 40 } }, { "ui_panel_popup", new[] { 360, 240, 36, 36, 36, 36 } }, { "ui_panel_content", new[] { 320, 200, 28, 28, 28, 28 } }, { "ui_panel_header", new[] { 400, 80, 32, 32, 24, 24 } }, { "btn_primary_normal", new[] { 224, 88, 32, 32, 24, 24 } }, { "btn_primary_pressed", new[] { 224, 88, 32, 32, 24, 24 } }, { "btn_primary_disabled", new[] { 224, 88, 32, 32, 24, 24 } }, { "btn_secondary_normal", new[] { 224, 88, 32, 32, 24, 24 } }, { "btn_secondary_pressed", new[] { 224, 88, 32, 32, 24, 24 } }, { "btn_small_normal", new[] { 160, 64, 24, 24, 18, 18 } }, { "btn_small_pressed", new[] { 160, 64, 24, 24, 18, 18 } }, { "scrollbar_track_v", new[] { 28, 160, 8, 8, 16, 16 } }, { "scrollbar_thumb_v", new[] { 28, 80, 8, 8, 16, 16 } }, { "input_bg_normal", new[] { 360, 72, 28, 28, 20, 20 } }, { "input_bg_focus", new[] { 360, 72, 28, 28, 20, 20 } }, { "ui_list_item_normal", new[] { 320, 72, 20, 20, 16, 16 } }, { "ui_list_item_selected", new[] { 320, 72, 20, 20, 16, 16 } }, { "tab_active", new[] { 200, 72, 28, 28, 20, 20 } }, { "tab_inactive", new[] { 200, 72, 28, 28, 20, 20 } }, { "progress_bg", new[] { 400, 32, 16, 16, 10, 10 } }, { "progress_fill", new[] { 400, 32, 16, 16, 10, 10 } }, { "slider_track", new[] { 320, 28, 14, 14, 8, 8 } }, { "slider_fill", new[] { 320, 28, 14, 14, 8, 8 } }, { "toast_bg", new[] { 280, 80, 28, 28, 20, 20 } }, { "toggle_bg_off", new[] { 112, 56, 28, 28, 18, 18 } }, { "toggle_bg_on", new[] { 112, 56, 28, 28, 18, 18 } } }; public static bool IsStandardControl(string path) { if (string.IsNullOrEmpty(path)) return false; path = path.Replace('\\', '/'); return path.StartsWith(CommonRoot, StringComparison.OrdinalIgnoreCase) && string.Equals(Path.GetDirectoryName(path)?.Replace('\\', '/'), CommonRoot.TrimEnd('/'), StringComparison.OrdinalIgnoreCase) && Rules.ContainsKey(Path.GetFileNameWithoutExtension(path)); } public static Vector4 GetBorder(string path, int width, int height) { int[] rule = Rules[Path.GetFileNameWithoutExtension(path)]; float sx = Mathf.Max(1, width - 4) / (float)rule[0]; float sy = Mathf.Max(1, height - 4) / (float)rule[1]; // Unity Vector4 顺序为 L,B,R,T,与文档 L,R,T,B 不同。 float maxX = Mathf.Max(1, (width - 1) / 2); float maxY = Mathf.Max(1, (height - 1) / 2); return new Vector4( Mathf.Clamp(Mathf.Round(rule[2] * sx) + 2, 1, maxX), Mathf.Clamp(Mathf.Round(rule[5] * sy) + 2, 1, maxY), Mathf.Clamp(Mathf.Round(rule[3] * sx) + 2, 1, maxX), Mathf.Clamp(Mathf.Round(rule[4] * sy) + 2, 1, maxY)); } public static bool Configure(TextureImporter importer) { if (!IsStandardControl(importer.assetPath)) return false; importer.GetSourceTextureWidthAndHeight(out int width, out int height); Vector4 border = GetBorder(importer.assetPath, width, height); var settings = new TextureImporterSettings(); importer.ReadTextureSettings(settings); bool changed = importer.textureType != TextureImporterType.Sprite || importer.spriteImportMode != SpriteImportMode.Single || settings.spriteMeshType != SpriteMeshType.FullRect || importer.spriteBorder != border; if (!changed) return false; importer.textureType = TextureImporterType.Sprite; importer.spriteImportMode = SpriteImportMode.Single; importer.ReadTextureSettings(settings); settings.spriteMeshType = SpriteMeshType.FullRect; importer.SetTextureSettings(settings); importer.spriteBorder = border; return true; } [MenuItem("Tools/UI/Apply Standard Control Nine-Slice")] public static void ApplyFromMenu() { Debug.Log("[UIGen] 九宫格更新: " + ApplyExisting() + " 张标准控件图。"); } public static int ApplyExisting() { if (EditorApplication.isPlayingOrWillChangePlaymode) throw new InvalidOperationException("Apply UI sprite borders in Edit Mode."); int changed = 0; foreach (string name in Rules.Keys) { string path = CommonRoot + name + ".png"; var importer = AssetImporter.GetAtPath(path) as TextureImporter; if (importer == null || !Configure(importer)) continue; importer.SaveAndReimport(); changed++; } return changed; } } public sealed class UiSpriteNineSlicePostprocessor : AssetPostprocessor { private void OnPreprocessTexture() { UiSpriteNineSliceRules.Configure((TextureImporter)assetImporter); } } } #endif