using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Text.RegularExpressions; /// /// 大厅图集切片烘焙工具(绕开 TextureImporter 管线,纯 Unity API): /// 1. 读取 NGUI 图集 prefab 的 mSprites 坐标 /// 2. Sprite.Create 从大图切出 56 个 sprite,AssetDatabase.CreateAsset 落盘 /// 3. 大厅 prefab 里 108 个 Image 按节点语义规则重绑切片 /// 菜单:Tools / 烘焙大厅图集切片 /// public static class BakeLobbySlicesTool { const string AtlasPrefabPath = "Assets/dataresources/atlas/a大厅/atlas_bylobbyclassic/Atlas_ByLobbyClassic.prefab"; const string AtlasTexPath = "Assets/dataresources/atlas/a大厅/atlas_bylobbyclassic/Atlas_ByLobbyClassic.png"; const string OutDir = "Assets/BakeLobbySlicesAssets"; const string LobbyPrefabPath = "Assets/bundles/by_lobby/bundles/panel/BY_LbPlayMainUGUIPanel.prefab"; [MenuItem("Tools/烘焙大厅图集切片")] public static void Bake() { // ---------- 1. 读图集坐标 ---------- var atlasObj = AssetDatabase.LoadAssetAtPath(AtlasPrefabPath); if (atlasObj == null) { Debug.LogError("[烘焙] 图集 prefab 加载失败: " + AtlasPrefabPath); return; } var so = new SerializedObject(atlasObj); var mSprites = so.FindProperty("mSprites"); if (mSprites == null || !mSprites.isArray || mSprites.arraySize == 0) { Debug.LogError("[烘焙] mSprites 读取失败 (arraySize=" + (mSprites == null ? -1 : mSprites.arraySize) + ")"); return; } var tex = AssetDatabase.LoadAssetAtPath(AtlasTexPath); if (tex == null) { Debug.LogError("[烘焙] 大图加载失败,请先等 Unity 刷新完成: " + AtlasTexPath); return; } Debug.Log($"[烘焙] 图集 {mSprites.arraySize} 切片, 大图 {tex.width}x{tex.height}"); // ---------- 2. 切片落盘 ---------- if (!AssetDatabase.IsValidFolder(OutDir)) AssetDatabase.CreateFolder("Assets", "BakeLobbySlicesAssets"); var made = new Dictionary(); for (int i = 0; i < mSprites.arraySize; i++) { var el = mSprites.GetArrayElementAtIndex(i); string name = el.FindPropertyRelative("name").stringValue; var rect = new Rect(el.FindPropertyRelative("x").floatValue, el.FindPropertyRelative("y").floatValue, el.FindPropertyRelative("width").floatValue, el.FindPropertyRelative("height").floatValue); var border = new Vector4(el.FindPropertyRelative("borderLeft").floatValue, el.FindPropertyRelative("borderBottom").floatValue, el.FindPropertyRelative("borderRight").floatValue, el.FindPropertyRelative("borderTop").floatValue); var sp = Sprite.Create(tex, rect, new Vector2(0.5f, 0.5f), 100f, 0, SpriteMeshType.FullRect, border); sp.name = name; var path = $"{OutDir}/{name}.asset"; AssetDatabase.DeleteAsset(path); AssetDatabase.CreateAsset(sp, path); made[name] = sp; } Debug.Log($"[烘焙] 生成 {made.Count} 个 sprite asset → {OutDir}"); // ---------- 3. 重绑大厅 prefab ---------- var root = PrefabUtility.LoadPrefabContents(LobbyPrefabPath); int bound = 0, missing = 0; try { var images = root.GetComponentsInChildren(true); foreach (var img in images) { var sp = PickSlice(img.gameObject.name, made); if (sp != null) { img.sprite = sp; bound++; } else missing++; } PrefabUtility.SaveAsPrefabAsset(root, LobbyPrefabPath); } finally { PrefabUtility.UnloadPrefabContents(root); } Debug.Log($"[烘焙] prefab 重绑 {bound} 个 Image, 未匹配 {missing} 个 —— 完成!"); } // 节点名 → 切片(与此前 Python 修复用的语义规则一致,最后有兜底) static Sprite PickSlice(string n, Dictionary m) { string s = null; if (Regex.IsMatch(n, @"Frame_bg$")) s = "topbar_frame"; else if (n.Contains("Frame_lobby")) s = "topbar_def2"; else if (n.Contains("topbar_btn")) s = "topbar_btn"; else if (n.Contains("BtnClassic") || n == "Classic") s = "ic_tab_n1"; else if (n.Contains("BtnSuper") || n == "Super") s = "ic_tab_n2"; else if (n.Contains("BtnSiuxian") || n == "Siuxian") s = "ic_tab_n3"; else if (n.Contains("BtnFold")) s = "ic_tab_n4"; else if (n.StartsWith("tab_sel")) s = "tab_sel1"; else if (n.Contains("tab_bg")) s = "tab_bg"; else if (n.Contains("share_btn_bg")) s = "nor_frame_b"; else if (n.Contains("icon_more")) s = "icon_more"; else if (n.Contains("redDot")) s = "com_redDot"; else if (n.Contains("arrow") || n.Contains("jiantou")) s = "arrow"; else if (n == "bg_arrow") s = "bg_arrow"; else if (Regex.IsMatch(n, @"^(gem|gold|money|energy|bp)_bg")) s = "icon_bg"; else if (n.EndsWith("_spr")) s = "icon_bg2"; else if (n.Contains("headIcon")) s = "s_frame_p"; else if (n.Contains("player_level") || n.Contains("vip_spr")) s = "s_frame_r"; else if (n.EndsWith("_btn")) s = "btn"; else if (n.Contains("Frame") || n.Contains("frame")) s = "nor_frame_b2"; else if (n.StartsWith("eff_") || n.Contains("_eff")) s = "effect_1"; else if (n.Contains("lizi") || n.Contains("glow") || n.Contains("star") || n.Contains("sanguang")) s = "tx_mask"; else if (Regex.IsMatch(n, @"^bg( \(\d+\))?$")) s = "nor_frame_b"; else if (Regex.IsMatch(n, @"^Background( \(\d+\))?$")) s = "nor_frame_b2"; else if (n.Contains("ContentRoot")) s = "icon_bg"; else if (n.Contains("GodGuardTab") || n.Contains("ActivityCenter")) s = "tab_bg02"; else if (n.Contains("propIcon")) s = "icon_bg3"; else if (n.Contains("propTip")) s = "ls_txk_1"; else if (n.Contains("setup_bg")) s = "icon_bg3"; else if (n.Contains("musicOpen")) s = "icon_cardSystem_Bubble01"; else if (n.Contains("hlby_") || n.Contains("hong_spr")) s = "icon_cardSystem_Bubble02"; else if (n == "zi" || n.Contains("Label") || n.EndsWith("lab")) s = "txt_y_1"; else if (Regex.IsMatch(n, @"^Sprite( \(\d+\))?$")) s = "icon_bg2"; if (s != null && m.ContainsKey(s)) return m[s]; return m.ContainsKey("icon_bg2") ? m["icon_bg2"] : null; // 兜底 } }