/*=================================================
*FileName: PrefabAtlasSplitter.cs
*Author: yrc & qww
*UnityVersion: 2021.2.18f1
*Date: 2025-10-30 17:07
*Description: 1.选中哪个图集拆哪个,可以多拆,输出到指定目录, 2.一键拆解
*History:
=================================================*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace AtlasTool
{
public class PrefabAtlasSplitter : EditorWindow
{
///Assets路径
public static string _AssetsPath = string.Empty;
// 默认路径配置
private const string DEFAULT_SOURCE_PATH = @”D:ProjectUIatlas_source”;
private const string DEFAULT_EXPORT_PATH = @”D:ProjectUIatlas_export”;
// 路径配置
private static string _sourcePath = “”;
private static string _exportPath = “”;
private const string SOURCE_PATH_KEY = “PrefabAtlasSplitter_SourcePath”;
private const string EXPORT_PATH_KEY = “PrefabAtlasSplitter_ExportPath”;
// UI变量
private Vector2 _scrollPosition;
private Vector2 _prefabListScrollPosition;
private string _filterText = “”;
// Prefab列表相关
private List<string> _prefabPaths = new List<string>();
private List<string> _filteredPrefabPaths = new List<string>();
private bool[] _selectedPrefabs;
// 处理状态
private bool _isProcessing = false;
private int _currentProcessIndex = 0;
private List<string> _processingQueue = new List<string>();
private string _currentProcessingPrefab = “”;
// 搜索模式开关
private bool _useAtlasFilter = true;
private const string USE_ATLAS_FILTER_KEY = “PrefabAtlasSplitter_UseAtlasFilter”;
// 新增:状态枚举,参考第一个工具
private enum ProcessState
{
Idle,
Processing,
Complete
}
private ProcessState _currentState = ProcessState.Idle;
private static void InitPath()
{
string dataPath = Application.dataPath;
// 匹配根目录后的第一个文件夹名
Match match = Regex.Match(dataPath, @”(?<=^[A-Z]:[\/])[^\/]+”);
if (match.Success)
{
string directory = Path.GetPathRoot(dataPath);
_AssetsPath = FormatPath(Path.GetDirectoryName(dataPath) + “/”, 1);
}
else
{
Console.WriteLine(“未找到目标文件夹名”);
}
}
[MenuItem(“Assets/图集工具 一键拆图集”, false, 2000)]
static void ExportHierarchyDown()
{
ExportHierarchy();
}
[MenuItem(“GameObject/图集工具 一键拆图集”, true, -1)]
static bool ValidateExportHierarchyLeft()
{
return Selection.activeGameObject != null && Selection.activeGameObject.GetComponent<UIAtlas>() != null;
}
[MenuItem(“GameObject/图集工具 一键拆图集”, false, -1)]
static void ExportHierarchyLeft()
{
ExportHierarchy();
}
[MenuItem(“Tools/图集工具/一键拆图集”)]
static void ShowWindow()
{
PrefabAtlasSplitter window = GetWindow<PrefabAtlasSplitter>(“Yrc~自动拆解图集工具”);
window.minSize = new Vector2(600, 700);
window.Show();
// 加载保存的路径设置
_sourcePath = EditorPrefs.GetString(SOURCE_PATH_KEY, DEFAULT_SOURCE_PATH);
_exportPath = EditorPrefs.GetString(EXPORT_PATH_KEY, DEFAULT_EXPORT_PATH);
// 加载开关状态
window._useAtlasFilter = EditorPrefs.GetBool(USE_ATLAS_FILTER_KEY, true);
// 自动刷新Prefab列表
window.RefreshPrefabList();
}
/// <summary>
/// 绘制窗口UI
/// </summary>
private void OnGUI()
{
// 标题
DrawHeader();
// 路径设置
DrawPathSettings();
EditorGUILayout.Space();
// Prefab列表
DrawPrefabList();
EditorGUILayout.Space();
// 操作按钮
DrawActionButtons();
EditorGUILayout.Space();
// 处理状态
DrawStatus();
}
private void DrawHeader()
{
EditorGUILayout.Space();
// 创建居中样式
GUIStyle centeredLabelStyle = new GUIStyle(EditorStyles.boldLabel);
centeredLabelStyle.alignment = TextAnchor.MiddleCenter;
// 设置颜色
centeredLabelStyle.normal.textColor = Color.white;
// 使用居中样式绘制标签
GUILayout.Label(“选择文件夹列表,自动拆分图集”, centeredLabelStyle);
EditorGUILayout.Space();
}
/// <summary>
/// 绘制路径设置
/// </summary>
private void DrawPathSettings()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
{
EditorGUILayout.LabelField(“路径设置”, EditorStyles.boldLabel);
// 源目录
DrawPathField(“源目录 (包含Prefab):”, ref _sourcePath, “选择源目录”,
“选择包含图集Prefab的目录”, SOURCE_PATH_KEY, DEFAULT_SOURCE_PATH);
EditorGUILayout.Space();
// 导出目录
DrawPathField(“导出目录:”, ref _exportPath, “选择导出目录”,
“选择图片导出目录”, EXPORT_PATH_KEY, DEFAULT_EXPORT_PATH);
// 添加提示
EditorGUILayout.HelpBox(“注意:源目录必须在Assets目录下,否则无法加载Prefab。”, MessageType.Info);
}
EditorGUILayout.EndVertical();
}
/// <summary>
/// 绘制路径字段
/// </summary>
private void DrawPathField(string label, ref string path, string buttonText, string tooltip, string editorPrefsKey, string defaultPath)
{
EditorGUILayout.BeginVertical();
{
EditorGUILayout.LabelField(new GUIContent(label, tooltip), EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
{
// 路径显示和编辑
string newPath = EditorGUILayout.TextField(path, GUILayout.Height(20));
if (newPath != path)
{
path = newPath;
EditorPrefs.SetString(editorPrefsKey, path);
}
// 选择文件夹按钮
if (GUILayout.Button(buttonText, GUILayout.Width(100), GUILayout.Height(20)))
{
string selectedPath = EditorUtility.OpenFolderPanel(buttonText,
Directory.Exists(path) ? path : Application.dataPath, “”);
if (!string.IsNullOrEmpty(selectedPath))
{
path = selectedPath;
EditorPrefs.SetString(editorPrefsKey, path);
// 如果是源目录改变,刷新Prefab列表
if (editorPrefsKey == SOURCE_PATH_KEY)
{
RefreshPrefabList();
}
Repaint();
}
}
// 打开文件夹按钮
if (GUILayout.Button(“打开”, GUILayout.Width(50), GUILayout.Height(20)))
{
if (Directory.Exists(path))
{
EditorUtility.RevealInFinder(path);
}
else
{
EditorUtility.DisplayDialog(“提示”, “目录不存在,无法打开”, “确定”);
}
}
}
EditorGUILayout.EndHorizontal();
// 路径验证和信息
if (!Directory.Exists(path))
{
EditorGUILayout.HelpBox(“目录不存在”, MessageType.Warning);
}
else
{
EditorGUILayout.LabelField($”有效目录: {path}”, EditorStyles.miniLabel);
}
}
EditorGUILayout.EndVertical();
}
/// <summary>
/// 绘制Prefab列表
/// </summary>
private void DrawPrefabList()
{
EditorGUILayout.LabelField(“Prefab 列表”, EditorStyles.boldLabel);
EditorGUILayout.BeginVertical(“box”);
// 工具栏
DrawPrefabListToolbar();
EditorGUILayout.Space();
// Prefab列表
DrawPrefabListContent();
EditorGUILayout.EndVertical();
}
/// <summary>
/// 绘制Prefab列表工具栏
/// </summary>
private void DrawPrefabListToolbar()
{
EditorGUILayout.BeginHorizontal();
{
// 刷新按钮
if (GUILayout.Button(“刷新 Prefab 列表”, GUILayout.Width(120)))
{
RefreshPrefabList();
}
// 筛选
EditorGUILayout.LabelField(“筛选:”, GUILayout.Width(30));
string newFilterText = EditorGUILayout.TextField(_filterText, GUILayout.Width(150));
if (newFilterText != _filterText)
{
_filterText = newFilterText;
ApplyFilter();
}
// 搜索模式开关
bool newFilterValue = EditorGUILayout.ToggleLeft(“仅搜索 atlas_*.prefab 文件”, _useAtlasFilter, GUILayout.Width(180));
if (newFilterValue != _useAtlasFilter)
{
_useAtlasFilter = newFilterValue;
EditorPrefs.SetBool(USE_ATLAS_FILTER_KEY, _useAtlasFilter);
RefreshPrefabList(); // 开关改变时自动刷新列表
}
// 显示当前搜索模式状态
string searchMode = _useAtlasFilter ? “当前: atlas_*.prefab” : “当前: 所有 *.prefab”;
EditorGUILayout.LabelField(searchMode, EditorStyles.miniLabel);
EditorGUILayout.Space();
GUILayout.FlexibleSpace();
// 全选/全不选
if (GetDisplayedPrefabs().Count > 0)
{
if (GUILayout.Button(“全选”, GUILayout.Width(60)))
SelectAllPrefabs(true);
if (GUILayout.Button(“全不选”, GUILayout.Width(60)))
SelectAllPrefabs(false);
}
}
EditorGUILayout.EndHorizontal();
// 显示筛选状态
if (!string.IsNullOrEmpty(_filterText))
{
EditorGUILayout.HelpBox($”筛选: “{_filterText}” – 显示 {GetDisplayedPrefabs().Count}/{_prefabPaths.Count} 个Prefab”,
MessageType.Info);
}
}
/// <summary>
/// 绘制Prefab列表内容
/// </summary>
private void DrawPrefabListContent()
{
var displayedPrefabs = GetDisplayedPrefabs();
if (displayedPrefabs.Count > 0)
{
EditorGUILayout.LabelField($”找到 {displayedPrefabs.Count} 个Prefab:”, EditorStyles.boldLabel);
_prefabListScrollPosition = EditorGUILayout.BeginScrollView(_prefabListScrollPosition, GUILayout.Height(300));
for (int i = 0; i < displayedPrefabs.Count; i++)
{
EditorGUILayout.BeginHorizontal();
string prefabPath = displayedPrefabs[i];
int originalIndex = _prefabPaths.IndexOf(prefabPath);
if (originalIndex >= 0 && originalIndex < _selectedPrefabs.Length)
{
bool newValue = EditorGUILayout.Toggle(_selectedPrefabs[originalIndex], GUILayout.Width(20));
if (newValue != _selectedPrefabs[originalIndex])
{
_selectedPrefabs[originalIndex] = newValue;
}
}
// 显示Prefab名称和路径
string prefabName = Path.GetFileNameWithoutExtension(prefabPath);
EditorGUILayout.LabelField(prefabName, GUILayout.Width(150));
// 显示相对路径
EditorGUILayout.LabelField(prefabPath, EditorStyles.miniLabel);
// 打开文件夹按钮
if (GUILayout.Button(“打开文件夹”, GUILayout.Width(80)))
{
string folderPath = Path.GetDirectoryName(prefabPath);
if (!string.IsNullOrEmpty(folderPath))
{
string fullPath = Path.GetFullPath(Path.Combine(Application.dataPath, “..”, folderPath));
if (Directory.Exists(fullPath))
{
EditorUtility.RevealInFinder(fullPath);
}
else
{
EditorUtility.DisplayDialog(“提示”, “文件夹不存在”, “确定”);
}
}
}
EditorGUILayout.EndHorizontal();
// 分隔线
if (i < displayedPrefabs.Count – 1)
{
GUILayout.Box(“”, GUILayout.ExpandWidth(true), GUILayout.Height(1));
}
}
EditorGUILayout.EndScrollView();
// 选中统计
int selectedCount = GetSelectedPrefabCount();
EditorGUILayout.LabelField($”已选中: {selectedCount} 个Prefab”, EditorStyles.boldLabel);
}
else
{
if (_prefabPaths.Count > 0)
{
EditorGUILayout.HelpBox($”筛选 “{_filterText}” 没有匹配的Prefab”, MessageType.Warning);
}
else
{
EditorGUILayout.HelpBox(“未找到Prefab文件,请检查源目录路径”, MessageType.Info);
}
}
}
/// <summary>
/// 绘制操作按钮
/// </summary>
private void DrawActionButtons()
{
EditorGUILayout.BeginVertical(“box”);
{
// 批量处理按钮
EditorGUILayout.LabelField(“批量处理”, EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
{
bool canProcess = !_isProcessing && GetSelectedPrefabCount() > 0;
GUI.enabled = canProcess;
if (GUILayout.Button(“开始拆图(批量处理)”, GUILayout.Height(40)))
{
StartBatchExport();
}
GUI.enabled = true;
if (_isProcessing)
{
if (GUILayout.Button(“停止处理”, GUILayout.Width(80), GUILayout.Height(40)))
{
StopProcessing();
}
}
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
/// <summary>
/// 绘制状态信息
/// </summary>
private void DrawStatus()
{
EditorGUILayout.BeginVertical(“box”);
{
EditorGUILayout.LabelField(“当前状态:”, GetStateDisplayText(), EditorStyles.boldLabel);
if (_currentState != ProcessState.Idle)
{
EditorGUILayout.Space();
Rect progressRect = EditorGUILayout.GetControlRect();
EditorGUI.ProgressBar(progressRect, GetProgress(), GetProgressText());
}
// 显示选中图集信息
if (Selection.activeGameObject != null)
{
UIAtlas atlas = Selection.activeGameObject.GetComponent<UIAtlas>();
if (atlas != null)
{
EditorGUILayout.HelpBox($”已选中图集: {Selection.activeGameObject.name}”, MessageType.Info);
}
}
// 显示处理队列信息
if (_processingQueue.Count > 0)
{
EditorGUILayout.HelpBox($”待处理队列: {_processingQueue.Count} 个Prefab”, MessageType.None);
}
}
EditorGUILayout.EndVertical();
}
/// <summary>
/// 获取状态显示文本
/// </summary>
private string GetStateDisplayText()
{
return _currentState switch
{
ProcessState.Idle => “就绪 – 请选择要拆分的图集Prefab”,
ProcessState.Processing => $”处理中 – {Path.GetFileNameWithoutExtension(_currentProcessingPrefab)} ({_currentProcessIndex + 1}/{_processingQueue.Count})”,
ProcessState.Complete => “处理完成”,
_ => “未知状态”
};
}
/// <summary>
/// 获取进度文本 – 新增
/// </summary>
private string GetProgressText()
{
if (_processingQueue.Count == 0) return “就绪”;
return $”处理进度: {_currentProcessIndex + 1}/{_processingQueue.Count}”;
}
/// <summary>
/// 获取进度值 – 新增
/// </summary>
private float GetProgress()
{
if (_processingQueue.Count == 0) return 0f;
return (_currentProcessIndex + 1) * 1.0f / _processingQueue.Count;
}
/// <summary>
/// 刷新Prefab列表
/// </summary>
private void RefreshPrefabList()
{
_prefabPaths.Clear();
if (!Directory.Exists(_sourcePath))
{
Debug.LogWarning($”源目录不存在: {_sourcePath}”);
return;
}
try
{
// 根据开关状态选择搜索模式
string searchPattern = _useAtlasFilter ? “atlas_*.prefab” : “*.prefab”;
string searchMode = _useAtlasFilter ? “atlas_*.prefab” : “所有 *.prefab”;
// 搜索所有Prefab文件
var prefabFiles = Directory.GetFiles(_sourcePath, searchPattern, SearchOption.AllDirectories)
.Where(path => !path.Contains(“/Editor/”) && !path.Contains(“\Editor\”))
.OrderBy(path => Path.GetFileName(path))
.ToArray();
_prefabPaths.AddRange(prefabFiles);
_selectedPrefabs = new bool[_prefabPaths.Count];
// 应用筛选
ApplyFilter();
Debug.Log($”使用搜索模式: {searchMode},找到 {_prefabPaths.Count} 个Prefab文件”);
}
catch (Exception e)
{
Debug.LogError($”刷新Prefab列表失败: {e.Message}”);
}
}
/// <summary>
/// 应用筛选条件
/// </summary>
private void ApplyFilter()
{
if (string.IsNullOrEmpty(_filterText))
{
_filteredPrefabPaths = new List<string>(_prefabPaths);
}
else
{
_filteredPrefabPaths = _prefabPaths
.Where(path => Path.GetFileName(path).ToLower().Contains(_filterText.ToLower()))
.ToList();
}
}
/// <summary>
/// 获取当前显示的Prefab列表
/// </summary>
private List<string> GetDisplayedPrefabs()
{
return string.IsNullOrEmpty(_filterText) ? _prefabPaths : _filteredPrefabPaths;
}
/// <summary>
/// 全选/全不选Prefab
/// </summary>
private void SelectAllPrefabs(bool selected)
{
var displayedPrefabs = GetDisplayedPrefabs();
for (int i = 0; i < _prefabPaths.Count; i++)
{
// 只选中当前显示的Prefab
if (displayedPrefabs.Contains(_prefabPaths[i]))
{
_selectedPrefabs[i] = selected;
}
}
}
/// <summary>
/// 获取选中的Prefab数量
/// </summary>
private int GetSelectedPrefabCount()
{
return _selectedPrefabs?.Count(x => x) ?? 0;
}
/// <summary>
/// 开始批量导出
/// </summary>
private void StartBatchExport()
{
if (!ValidatePaths())
{
EditorUtility.DisplayDialog(“错误”, “路径验证失败,请检查路径设置”, “确定”);
return;
}
_processingQueue.Clear();
// 收集选中的Prefab
for (int i = 0; i < _prefabPaths.Count; i++)
{
if (_selectedPrefabs[i])
_processingQueue.Add(_prefabPaths[i]);
}
if (_processingQueue.Count == 0)
{
EditorUtility.DisplayDialog(“提示”, “请至少选择一个Prefab”, “确定”);
return;
}
_isProcessing = true;
_currentState = ProcessState.Processing; // 新增:更新状态
_currentProcessIndex = 0;
Debug.Log($”开始处理 {_processingQueue.Count} 个Prefab…”);
Debug.Log($”导出目录: {_exportPath}”);
// 开始处理第一个Prefab
ProcessNextPrefab();
}
/// <summary>
/// 验证路径
/// </summary>
private bool ValidatePaths()
{
if (!Directory.Exists(_sourcePath))
{
Debug.LogError($”源目录不存在: {_sourcePath}”);
return false;
}
try
{
if (!Directory.Exists(_exportPath))
Directory.CreateDirectory(_exportPath);
}
catch (Exception e)
{
Debug.LogError($”创建导出目录失败: {e.Message}”);
return false;
}
return true;
}
/// <summary>
/// 停止处理
/// </summary>
private void StopProcessing()
{
_isProcessing = false;
_currentState = ProcessState.Idle; // 新增:更新状态
_processingQueue.Clear();
_currentProcessingPrefab = “”;
EditorUtility.ClearProgressBar();
Debug.Log(“拆图处理已停止”);
}
/// <summary>
/// 处理下一个Prefab
/// </summary>
private void ProcessNextPrefab()
{
if (_currentProcessIndex >= _processingQueue.Count)
{
FinalizeProcessing();
return;
}
_currentProcessingPrefab = _processingQueue[_currentProcessIndex];
string prefabName = Path.GetFileNameWithoutExtension(_currentProcessingPrefab);
Debug.Log($”=== 开始处理Prefab: {prefabName} ===”);
// 使用协程方式处理
EditorApplication.update += ProcessPrefabCoroutine;
}
/// <summary>
/// 协程方式处理Prefab
/// </summary>
private void ProcessPrefabCoroutine()
{
try
{
EditorApplication.update -= ProcessPrefabCoroutine;
if (!_isProcessing) return;
// 执行拆图 – 使用新的导出逻辑
bool success = ExportSingleAtlasNew(_currentProcessingPrefab);
if (success)
{
Debug.Log($”✅ Prefab处理成功: {_currentProcessingPrefab}”);
}
else
{
Debug.LogError($”❌ Prefab处理失败: {_currentProcessingPrefab}”);
}
_currentProcessIndex++;
ProcessNextPrefab();
}
catch (Exception e)
{
EditorApplication.update -= ProcessPrefabCoroutine;
Debug.LogError($”处理Prefab时发生错误: {e.Message}
{e.StackTrace}”);
_currentProcessIndex++;
ProcessNextPrefab();
}
}
/// <summary>
/// 完成处理
/// </summary>
private void FinalizeProcessing()
{
_isProcessing = false;
_currentState = ProcessState.Complete; // 新增:更新状态
EditorUtility.ClearProgressBar();
AssetDatabase.Refresh();
string message = $”拆图完成!
处理了 {_currentProcessIndex} 个Prefab
导出目录: {_exportPath}”;
EditorUtility.DisplayDialog(“拆图完成”, message, “确定”);
Debug.Log($”🎉 拆图完成!处理了 {_currentProcessIndex} 个Prefab,导出目录: {_exportPath}”);
// 3秒后重置状态为Idle
EditorApplication.delayCall += () =>
{
_currentState = ProcessState.Idle;
Repaint();
};
}
// ========== 原有的直接拆图逻辑 ==========
static void ExportHierarchy()
{
InitPath();
InitAtlas();
UpdateAtlas();
}
/// UI路径
public static string Prefab_PATH = string.Empty;
public static List<string> proPrefabPathArr = new List<string>();
//———————————————- 拆分图集 ,生成 PSD 读取用 Start ———————–
#region 拆分图集 ,生成 PSD 读取用 Start
public static string texPath = “”;
public static List<string> ObjAssetsPathList = new List<string>();
public static int index = 0;
private static string pngNameStr = “”;
public static void InitAtlas()
{
index = 0;
ObjAssetsPathList.Clear();
proPrefabPathArr = new List<string>();
// 使用配置的导出路径
texPath = _exportPath;
// 确保目录存在
if (!Directory.Exists(texPath))
{
Directory.CreateDirectory(texPath);
Debug.Log($”创建输出目录: {texPath}”);
}
pngNameStr = “”;
Debug.Log($”拆图输出目录: {texPath}”);
}
public static void UpdateAtlas()
{
// 获取选中的图集Prefab
var selectedObjects = Selection.gameObjects;
foreach (var selectedObj in selectedObjects)
{
UIAtlas atlas = selectedObj.GetComponent<UIAtlas>();
if (atlas != null)
{
string prefabPath = GetPrefabPath(selectedObj);
if (!string.IsNullOrEmpty(prefabPath))
{
ObjAssetsPathList.Add(prefabPath);
Debug.Log($”添加图集Prefab: {prefabPath}”);
}
}
}
if (ObjAssetsPathList.Count == 0)
{
EditorUtility.DisplayDialog(“提示”, “请先选中一个或多个包含UIAtlas组件的Prefab!”, “确定”);
return;
}
// 使用协程方式处理,避免阻塞主线程
EditorApplication.update += ProcessAtlasCoroutine;
}
/// <summary>
/// 协程方式处理图集,避免递归导致的栈溢出
/// </summary>
private static void ProcessAtlasCoroutine()
{
try
{
if (index >= ObjAssetsPathList.Count)
{
// 处理完成
EditorApplication.update -= ProcessAtlasCoroutine;
EditorUtility.ClearProgressBar();
AssetDatabase.Refresh();
EditorUtility.DisplayDialog(“提示”, pngNameStr + “分割完成!!!”, “确定”);
return;
}
float progress = index * 1.0f / ObjAssetsPathList.Count;
EditorUtility.DisplayProgressBar(“分解图集”, $”分解图集中 ({index + 1}/{ObjAssetsPathList.Count})…”, progress);
string currentAtlasPath = ObjAssetsPathList[index];
Debug.Log($”开始处理图集: {currentAtlasPath}”);
if (!ExportSingleAtlasNew(currentAtlasPath))
{
Debug.LogWarning($”图集处理失败: {currentAtlasPath}”);
}
index++;
}
catch (Exception e)
{
EditorApplication.update -= ProcessAtlasCoroutine;
EditorUtility.ClearProgressBar();
Debug.LogError($”处理图集时发生错误: {e.Message}
{e.StackTrace}”);
EditorUtility.DisplayDialog(“错误”, $”处理图集时发生错误: {e.Message}”, “确定”);
}
}
/// <summary>
/// 新的拆图方法 – 修复版本
/// </summary>
public static bool ExportSingleAtlasNew(string atlasPath)
{
try
{
atlasPath = ConvertToAssetPath(atlasPath);
SetNGUIAtlas(atlasPath);
string pngName = Path.GetFileNameWithoutExtension(atlasPath);
// 使用配置的导出路径
string folderPath = Path.Combine(_exportPath, pngName);
if (NGUISettings.atlas == null)
{
Debug.LogWarning($”跳过图集 {pngName}:未找到有效的UIAtlas组件”);
return false;
}
UIAtlas atlas = NGUISettings.atlas;
List<UISpriteData> exitSpritesList = atlas.spriteList;
// 确保图集纹理已加载
if (atlas.texture == null)
{
Debug.LogError($”图集纹理为空: {pngName}”);
return false;
}
// 重新导入纹理确保可读
string texturePath = AssetDatabase.GetAssetPath(atlas.texture);
if (!string.IsNullOrEmpty(texturePath))
{
TextureImporter importer = AssetImporter.GetAtPath(texturePath) as TextureImporter;
if (importer != null && !importer.isReadable)
{
importer.isReadable = true;
importer.SaveAndReimport();
Debug.Log($”重新导入纹理: {texturePath}”);
}
}
Texture2D atlasTexture = DecompressTexture(atlas.texture as Texture2D);
if (atlasTexture == null)
{
Debug.LogError($”无法加载图集纹理: {pngName}”);
return false;
}
int oldWidth = atlasTexture.width;
int oldHeight = atlasTexture.height;
Color32[] oldPixels = atlasTexture.GetPixels32();
Debug.Log($”处理图集 {pngName}, 尺寸: {oldWidth}x{oldHeight}, 精灵数量: {exitSpritesList.Count}”);
// 确保输出目录存在
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
Debug.Log($”创建输出目录: {folderPath}”);
}
int successCount = 0;
foreach (var es in exitSpritesList)
{
try
{
int xmin = Mathf.Clamp(es.x, 0, oldWidth);
int ymin = Mathf.Clamp(es.y, 0, oldHeight);
int newWidth = Mathf.Clamp(es.width, 0, oldWidth – xmin);
int newHeight = Mathf.Clamp(es.height, 0, oldHeight – ymin);
if (newWidth == 0 || newHeight == 0)
{
Debug.LogWarning($”跳过无效尺寸的精灵: {es.name}, 尺寸: {newWidth}x{newHeight}”);
continue;
}
Color32[] newPixels = new Color32[newWidth * newHeight];
for (int y = 0; y < newHeight; ++y)
{
for (int x = 0; x < newWidth; ++x)
{
int newIndex = (newHeight – 1 – y) * newWidth + x;
int oldIndex = (oldHeight – 1 – (ymin + y)) * oldWidth + (xmin + x);
if (oldIndex >= 0 && oldIndex < oldPixels.Length)
{
newPixels[newIndex] = oldPixels[oldIndex];
}
else
{
newPixels[newIndex] = new Color32(0, 0, 0, 0);
}
}
}
// 处理padding
int xpad = es.hasPadding ? (es.paddingLeft + es.paddingRight) : 0;
int ypad = es.hasPadding ? (es.paddingTop + es.paddingBottom) : 0;
int bigWidth = newWidth + xpad;
int bigHeight = newHeight + ypad;
Texture2D outputTexture = new Texture2D(bigWidth, bigHeight, TextureFormat.ARGB32, false);
outputTexture.alphaIsTransparency = true;
// 初始化透明背景
Color32[] transparentPixels = new Color32[bigWidth * bigHeight];
for (int i = 0; i < transparentPixels.Length; i++)
{
transparentPixels[i] = new Color32(0, 0, 0, 0);
}
outputTexture.SetPixels32(transparentPixels);
// 将原始图像数据放置到正确位置(考虑padding)
int xOffset = xpad / 2;
int yOffset = ypad / 2;
outputTexture.SetPixels32(xOffset, yOffset, newWidth, newHeight, newPixels);
outputTexture.Apply();
byte[] bytes = outputTexture.EncodeToPNG();
string savePngPath = Path.Combine(folderPath, es.name + “.png”);
File.WriteAllBytes(savePngPath, bytes);
UnityEngine.Object.DestroyImmediate(outputTexture);
successCount++;
Debug.Log($”成功导出精灵: {es.name} -> {savePngPath}”);
}
catch (Exception e)
{
Debug.LogError($”导出精灵 {es.name} 时出错: {e.Message}”);
}
}
UnityEngine.Object.DestroyImmediate(atlasTexture);
pngNameStr += $”{pngName} (成功: {successCount}/{exitSpritesList.Count})
“;
Debug.Log($”图集 {pngName} 处理完成: 成功导出 {successCount} 个精灵”);
// 刷新文件系统
AssetDatabase.Refresh();
return successCount > 0;
}
catch (Exception e)
{
Debug.LogError($”拆图过程发生错误: {e.Message}
{e.StackTrace}”);
return false;
}
}
// 保留原有方法用于兼容
public static bool ExportSingleAtlas(string atlasPath)
{
return ExportSingleAtlasNew(atlasPath);
}
public static Texture2D DecompressTexture(Texture2D source)
{
if (source == null) return null;
RenderTexture rt = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.ARGB32);
Graphics.Blit(source, rt);
RenderTexture previous = RenderTexture.active;
RenderTexture.active = rt;
Texture2D readableTex = new Texture2D(source.width, source.height, TextureFormat.ARGB32, false);
readableTex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
readableTex.Apply();
RenderTexture.active = previous;
RenderTexture.ReleaseTemporary(rt);
return readableTex;
}
/// <summary>
/// 获取Prefab在项目中的路径
/// </summary>
private static string GetPrefabPath(GameObject gameObject)
{
// 如果是Prefab实例,获取其Prefab资源路径
if (PrefabUtility.IsPartOfAnyPrefab(gameObject))
{
var prefabAsset = PrefabUtility.GetCorrespondingObjectFromOriginalSource(gameObject);
if (prefabAsset != null)
{
return AssetDatabase.GetAssetPath(prefabAsset);
}
}
// 如果是直接选中的Prefab资源
return AssetDatabase.GetAssetPath(gameObject);
}
public static void SetNGUIAtlas(string path)
{
GameObject go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject;
if (go == null)
{
Debug.LogError($”无法加载Prefab: {path}”);
return;
}
UIAtlas obj = go.GetComponent<UIAtlas>();
if (obj == null)
{
Debug.LogError($”Prefab中未找到UIAtlas组件: {path}”);
return;
}
UIAtlasMaker.OnSelectAtlasStatic(obj);
Debug.Log($”已设置当前图集: {path}”);
}
private static string ConvertToAssetPath(string fullPath)
{
fullPath = fullPath.Replace('\', '/');
string assetsPath = Application.dataPath.Replace('\', '/');
if (fullPath.StartsWith(assetsPath))
{
return “Assets” + fullPath.Substring(assetsPath.Length);
}
return fullPath;
}
public static string FormatPath(string path, int sign = 0)
{
path = path.Replace(“/”, “\”);
if (Application.platform == RuntimePlatform.OSXEditor || sign == 1)
path = path.Replace(“\”, “/”);
return path;
}
#endregion 拆分图集 生成图片 END
}
}
