using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.U2D;
using UnityEngine;
using UnityEngine.U2D;
///
/// 预制体图集打包工具。
///
/// 【用途】
/// 选中若干 UI 预制体(如大厅散包的各子面板)→ 扫描它们引用的全部 Sprite →
/// 一键创建 SpriteAtlas 并把这些图合进去(自动按总像素估算合适的图集尺寸),
/// 避免散包后每张小图单独打包/加载,也避免多个包重复携带同一批图。
///
/// 【使用】
/// 1. Project 里选中一个或多个预制体(子面板 prefab)
/// 2. 菜单 Tools → 图集工具 → [扫描选中预制体]
/// 3. 检查图片列表(会自动排除 Unity 内置资源;可勾选排除大背景图)
/// 4. 设置输出目录/图集名/尺寸(默认自动计算)/ Padding
/// 5. 选择要加入的 Addressables 组(强烈建议:与使用这些图的 prefab 同组,
/// 否则图集不进组会被引用它的每个 bundle 各复制一份!)
/// 6. [创建图集] → 重建 Addressables 内容后生效
///
/// 【注意】
/// - 打进图集的图片无需改 TextureImporter(图集优先于单图导入设置)
/// - 单图超过图集最大尺寸时 SpriteAtlas 会自动分页(multi-atlas)
/// - 创建后记得 Build → New Build 重建内容
///
public class SpriteAtlasBuilderWindow : EditorWindow
{
[MenuItem("Tools/图集工具")]
public static void Open()
{
var w = GetWindow("图集工具");
w.minSize = new Vector2(520f, 560f);
}
private class SpriteInfo
{
public Sprite sprite;
public string path;
public int w, h;
public bool big; // 边长 ≥ 512 的大图(默认排除)
}
private readonly List _sprites = new List();
private Vector2 _scroll;
private string _outputDir = "Assets/MyGame/aa/UI/Atlases";
private string _atlasName = "UIAtlas";
private int _maxSizeIndex = 0;
private readonly string[] _maxSizeOptions = { "自动(按总像素)", "1024", "2048", "4096" };
private int _padding = 4;
private bool _excludeBig = true;
private string[] _groupNames = { "不加入(稍后手动)" };
private int _groupIndex = 0;
private void OnEnable() => RefreshGroups();
private void RefreshGroups()
{
var list = new List { "不加入(稍后手动)" };
try
{
var s = AddressableAssetSettingsDefaultObject.Settings;
if (s != null)
list.AddRange(s.groups.Where(g => !g.ReadOnly).Select(g => g.Name));
}
catch { }
_groupNames = list.ToArray();
}
private void OnGUI()
{
EditorGUILayout.HelpBox(
"1. Project 里选中一个或多个 UI 预制体\n" +
"2. 点 [扫描选中预制体] 收集引用的 Sprite\n" +
"3. 创建图集时选择与这些预制体相同的 Addressables 组\n" +
"(图集不进组会被引用它的每个 bundle 复制一份)", MessageType.Info);
if (GUILayout.Button("扫描选中预制体", GUILayout.Height(32)))
Scan();
EditorGUILayout.Space(4);
// ---- 扫描结果 ----
if (_sprites.Count > 0)
{
var included = _sprites.Where(s => !(_excludeBig && s.big)).ToList();
long totalPixels = included.Sum(s => (long)s.w * s.h);
long bigCount = _sprites.Count(s => s.big);
int autoSize = CalcAutoSize(included, _padding);
EditorGUILayout.LabelField($"共 {_sprites.Count} 张图" +
(bigCount > 0 ? $"(其中 {bigCount} 张大图,见下方排除开关)" : ""), EditorStyles.boldLabel);
EditorGUILayout.LabelField($"合计像素: {totalPixels / 10000f:F1} 万 | 自动图集尺寸: {autoSize}");
_excludeBig = EditorGUILayout.ToggleLeft("排除大图(边长≥512,背景图等通常不进图集)", _excludeBig);
_scroll = EditorGUILayout.BeginScrollView(_scroll, GUILayout.MinHeight(180));
foreach (var s in _sprites)
{
var style = s.big ? new GUIStyle(EditorStyles.label) { normal = { textColor = Color.yellow } } : EditorStyles.label;
GUILayout.Label($"{s.w}×{s.h}{(s.big ? " [大图]" : "")} {s.path}", style);
}
EditorGUILayout.EndScrollView();
}
else
{
EditorGUILayout.HelpBox("尚未扫描(或选中对象里没有预制体)", MessageType.None);
}
EditorGUILayout.Space(8);
// ---- 设置 ----
EditorGUILayout.LabelField("图集设置", EditorStyles.boldLabel);
_outputDir = EditorGUILayout.TextField("输出目录", _outputDir);
_atlasName = EditorGUILayout.TextField("图集名称", _atlasName);
_maxSizeIndex = EditorGUILayout.Popup("最大尺寸", _maxSizeIndex, _maxSizeOptions);
_padding = EditorGUILayout.IntSlider("图集内边距(Padding)", _padding, 1, 16);
EditorGUILayout.Space(4);
EditorGUILayout.LabelField("加入 Addressables 组(建议与预制体同组)", EditorStyles.boldLabel);
_groupIndex = EditorGUILayout.Popup("目标组", _groupIndex, _groupNames);
EditorGUILayout.Space(8);
using (new EditorGUI.DisabledScope(_sprites.Count == 0))
{
if (GUILayout.Button("创建图集", GUILayout.Height(36)))
Create();
}
}
/// 扫描选中的预制体,收集全部 Sprite 引用(去重、排除引擎内置资源)
private void Scan()
{
_sprites.Clear();
var seen = new HashSet