Unity 一键拆解图集 (1.直接Prefab~拆图 2.弹窗一键拆图)

内容分享2小时前发布
7 0 0

/*=================================================
*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 
    }
}

© 版权声明

相关文章

暂无评论

none
暂无评论...