using System.IO; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; using Newtonsoft.Json.Linq; /// /// UI贴图导出工具 - 将Art目录的UI贴图复制到Art_Out目录 /// public class UITextureExporter : EditorWindow { [MenuItem("Tools/*美术资源工具/UI贴图导出(Art -> Art_Out)")] public static void ShowWindow() { GetWindow("UI贴图导出工具"); } private string sourceFolder = "Assets/Art/UI/Texture/Activity/Activity_CheckIn"; private string targetFolder = "Assets/Art_Out/UI/Texture/Activity/Activity_CheckIn"; private string pictorialDataFolder = "Assets/Config/Data/ActivityPicturialData"; private Vector2 scrollPosition; private int currentTab = 0; private string[] tabNames = { "目录导出", "画报图片导出" }; private void OnGUI() { GUILayout.Label("UI贴图导出工具", EditorStyles.boldLabel); GUILayout.Space(10); // 标签页切换 currentTab = GUILayout.Toolbar(currentTab, tabNames); GUILayout.Space(10); if (currentTab == 0) { DrawDirectoryExportTab(); } else if (currentTab == 1) { DrawPictorialExportTab(); } } /// /// 目录导出标签页 /// private void DrawDirectoryExportTab() { EditorGUILayout.HelpBox( "此工具用于将 Assets/Art/UI/Texture 目录下的贴图复制到 Assets/Art_Out/UI/Texture 目录\n" + "确保运行时 YooAsset 可以加载这些资源。", MessageType.Info ); GUILayout.Space(10); // 输入区域 GUILayout.Label("源目录(Art):", EditorStyles.boldLabel); sourceFolder = EditorGUILayout.TextField(sourceFolder); GUILayout.Space(5); GUILayout.Label("目标目录(Art_Out):", EditorStyles.boldLabel); targetFolder = EditorGUILayout.TextField(targetFolder); GUILayout.Space(10); // 快速选项 GUILayout.Label("快速选项:", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Activity_CheckIn")) { sourceFolder = "Assets/Art/UI/Texture/Activity/Activity_CheckIn"; targetFolder = "Assets/Art_Out/UI/Texture/Activity/Activity_CheckIn"; } if (GUILayout.Button("全部Activity")) { sourceFolder = "Assets/Art/UI/Texture/Activity"; targetFolder = "Assets/Art_Out/UI/Texture/Activity"; } if (GUILayout.Button("全部UI/Texture")) { sourceFolder = "Assets/Art/UI/Texture"; targetFolder = "Assets/Art_Out/UI/Texture"; } EditorGUILayout.EndHorizontal(); GUILayout.Space(10); // 执行按钮 EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("开始导出", GUILayout.Height(40))) { ExportTextures(); } if (GUILayout.Button("清空目标目录", GUILayout.Height(40))) { ClearTargetFolder(); } EditorGUILayout.EndHorizontal(); GUILayout.Space(10); // 说明 scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); GUILayout.Label("说明:", EditorStyles.boldLabel); GUILayout.Label("1. 此工具会递归复制源目录下的所有文件"); GUILayout.Label("2. 会自动创建不存在的目录"); GUILayout.Label("3. 会同步复制 .meta 文件并生成新的GUID"); GUILayout.Label("4. 复制完成后会自动刷新 AssetDatabase"); GUILayout.Label(""); GUILayout.Label("注意:", EditorStyles.boldLabel); GUILayout.Label("• 如果目标文件已存在,会覆盖"); GUILayout.Label("• 请确保源目录路径正确"); GUILayout.Label("• 大量文件复制可能需要较长时间"); GUILayout.Label("• meta文件会自动生成新GUID避免冲突"); EditorGUILayout.EndScrollView(); } /// /// 画报图片导出标签页 /// private void DrawPictorialExportTab() { EditorGUILayout.HelpBox( "此功能会自动读取 ActivityPicturialData 目录中所有画报 JSON 文件\n" + "提取其中的资源路径(mainBackgroundPath、backgroundPath、imagePath)\n" + "将 Assets/Art/ 目录下的资源复制到 Assets/Art_Out/ 目录。", MessageType.Info ); GUILayout.Space(10); // 画报数据目录 GUILayout.Label("画报数据目录:", EditorStyles.boldLabel); pictorialDataFolder = EditorGUILayout.TextField(pictorialDataFolder); GUILayout.Space(10); // 执行按钮 if (GUILayout.Button("开始导出画报图片", GUILayout.Height(40))) { ExportPictorialTextures(); } GUILayout.Space(10); // 说明 scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); GUILayout.Label("说明:", EditorStyles.boldLabel); GUILayout.Label("1. 此工具会读取所有 .json 文件"); GUILayout.Label("2. 解析画报数据的资源路径字段"); GUILayout.Label("3. 自动去除子资源后缀 [SpriteName]"); GUILayout.Label("4. 将 Art 目录资源复制到 Art_Out 目录"); GUILayout.Label("5. 同步复制 .meta 文件并生成新GUID"); GUILayout.Label("6. 保持相同的目录结构"); GUILayout.Label(""); GUILayout.Label("支持的字段:", EditorStyles.boldLabel); GUILayout.Label("• mainBackgroundPath - 大背景路径"); GUILayout.Label("• backgroundPath - 普通背景路径"); GUILayout.Label("• items[].imagePath - 画报元素图片路径"); GUILayout.Label(""); GUILayout.Label("注意:", EditorStyles.boldLabel); GUILayout.Label("• 只会处理 Assets/Art/ 开头的路径"); GUILayout.Label("• 如果目标文件已存在,会覆盖"); GUILayout.Label("• 会自动创建不存在的目录"); GUILayout.Label("• meta文件会自动生成新GUID避免冲突"); EditorGUILayout.EndScrollView(); } private void ExportTextures() { if (!Directory.Exists(sourceFolder)) { EditorUtility.DisplayDialog("错误", $"源目录不存在:\n{sourceFolder}", "确定"); return; } bool confirm = EditorUtility.DisplayDialog( "确认导出", $"将要从:\n{sourceFolder}\n\n复制到:\n{targetFolder}\n\n是否继续?", "确定", "取消" ); if (!confirm) return; try { EditorUtility.DisplayProgressBar("导出UI贴图", "正在复制文件...", 0f); // 确保目标目录存在 if (!Directory.Exists(targetFolder)) { Directory.CreateDirectory(targetFolder); } // 获取所有文件 string[] files = Directory.GetFiles(sourceFolder, "*.*", SearchOption.AllDirectories); int totalFiles = files.Length; int copiedFiles = 0; Debug.Log($"[UI贴图导出] 开始导出,共 {totalFiles} 个文件"); foreach (string sourceFile in files) { // 跳过.meta文件,我们会手动处理 if (sourceFile.EndsWith(".meta")) continue; // 计算相对路径 string relativePath = sourceFile.Replace(sourceFolder, "").TrimStart('\\', '/'); string targetFile = Path.Combine(targetFolder, relativePath).Replace("\\", "/"); // 确保目标文件的目录存在 string targetDir = Path.GetDirectoryName(targetFile); if (!Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } // 复制文件 File.Copy(sourceFile, targetFile, true); copiedFiles++; // 复制并修改 .meta 文件 string sourceMetaPath = sourceFile + ".meta"; string targetMetaPath = targetFile + ".meta"; if (File.Exists(sourceMetaPath)) { // 读取源 meta 文件 string metaContent = File.ReadAllText(sourceMetaPath); // 生成新的 GUID string newGuid = GUID.Generate().ToString(); // 替换 GUID metaContent = System.Text.RegularExpressions.Regex.Replace( metaContent, @"guid: [a-f0-9]{32}", $"guid: {newGuid}", System.Text.RegularExpressions.RegexOptions.IgnoreCase ); // 写入目标 meta 文件 File.WriteAllText(targetMetaPath, metaContent); } // 更新进度条 float progress = (float)copiedFiles / totalFiles; EditorUtility.DisplayProgressBar( "导出UI贴图", $"正在复制: {Path.GetFileName(sourceFile)} ({copiedFiles}/{totalFiles})", progress ); Debug.Log($"[UI贴图导出] 已复制: {relativePath}"); } EditorUtility.ClearProgressBar(); // 刷新AssetDatabase Debug.Log("[UI贴图导出] 正在刷新 AssetDatabase..."); AssetDatabase.Refresh(); EditorUtility.DisplayDialog( "导出完成", $"成功复制 {copiedFiles} 个文件到:\n{targetFolder}", "确定" ); Debug.Log($"[UI贴图导出] 导出完成,共复制 {copiedFiles} 个文件"); } catch (System.Exception e) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("错误", $"导出失败:\n{e.Message}", "确定"); Debug.LogError($"[UI贴图导出] 导出失败: {e.Message}\n{e.StackTrace}"); } } private void ClearTargetFolder() { if (!Directory.Exists(targetFolder)) { EditorUtility.DisplayDialog("提示", $"目标目录不存在:\n{targetFolder}", "确定"); return; } bool confirm = EditorUtility.DisplayDialog( "确认清空", $"将要删除目录:\n{targetFolder}\n\n此操作不可恢复,是否继续?", "确定", "取消" ); if (!confirm) return; try { Directory.Delete(targetFolder, true); AssetDatabase.Refresh(); EditorUtility.DisplayDialog("清空完成", $"已删除:\n{targetFolder}", "确定"); Debug.Log($"[UI贴图导出] 已清空目录: {targetFolder}"); } catch (System.Exception e) { EditorUtility.DisplayDialog("错误", $"清空失败:\n{e.Message}", "确定"); Debug.LogError($"[UI贴图导出] 清空失败: {e.Message}\n{e.StackTrace}"); } } /// /// 导出画报图片 /// private void ExportPictorialTextures() { if (!Directory.Exists(pictorialDataFolder)) { EditorUtility.DisplayDialog("错误", $"画报数据目录不存在:\n{pictorialDataFolder}", "确定"); return; } try { EditorUtility.DisplayProgressBar("导出画报图片", "正在读取JSON文件...", 0f); // 获取所有JSON文件 string[] jsonFiles = Directory.GetFiles(pictorialDataFolder, "*.json", SearchOption.TopDirectoryOnly); Debug.Log($"[画报图片导出] 找到 {jsonFiles.Length} 个JSON文件"); // 收集所有资源路径 HashSet assetPaths = new HashSet(); foreach (string jsonFile in jsonFiles) { try { string jsonContent = File.ReadAllText(jsonFile); JObject json = JObject.Parse(jsonContent); // 提取大背景路径 string mainBgPath = json["mainBackgroundPath"]?.ToString(); if (!string.IsNullOrEmpty(mainBgPath)) { assetPaths.Add(RemoveSubAssetSuffix(mainBgPath)); } // 提取普通背景路径 string bgPath = json["backgroundPath"]?.ToString(); if (!string.IsNullOrEmpty(bgPath)) { assetPaths.Add(RemoveSubAssetSuffix(bgPath)); } // 提取画报元素图片路径 JArray items = json["items"] as JArray; if (items != null) { foreach (JObject item in items) { string imagePath = item["imagePath"]?.ToString(); if (!string.IsNullOrEmpty(imagePath)) { assetPaths.Add(RemoveSubAssetSuffix(imagePath)); } } } Debug.Log($"[画报图片导出] 已解析: {Path.GetFileName(jsonFile)}"); } catch (System.Exception e) { Debug.LogError($"[画报图片导出] 解析JSON失败: {Path.GetFileName(jsonFile)}, 错误: {e.Message}"); } } Debug.Log($"[画报图片导出] 共收集到 {assetPaths.Count} 个资源路径"); // 筛选出 Assets/Art/ 开头的路径 List artPaths = assetPaths .Where(p => p.StartsWith("Assets/Art/") && !p.StartsWith("Assets/Art_Out/")) .ToList(); Debug.Log($"[画报图片导出] 需要复制 {artPaths.Count} 个 Art 目录资源"); if (artPaths.Count == 0) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("提示", "没有找到需要复制的 Assets/Art/ 资源", "确定"); return; } // 显示确认对话框 bool confirm = EditorUtility.DisplayDialog( "确认导出", $"将要复制 {artPaths.Count} 个资源文件\n" + $"从 Assets/Art/ 到 Assets/Art_Out/\n\n" + $"是否继续?", "确定", "取消" ); if (!confirm) { EditorUtility.ClearProgressBar(); return; } // 复制文件 int copiedCount = 0; int skippedCount = 0; for (int i = 0; i < artPaths.Count; i++) { string sourcePath = artPaths[i]; string targetPath = sourcePath.Replace("Assets/Art/", "Assets/Art_Out/"); // 更新进度条 float progress = (float)i / artPaths.Count; EditorUtility.DisplayProgressBar( "导出画报图片", $"正在复制: {Path.GetFileName(sourcePath)} ({i + 1}/{artPaths.Count})", progress ); // 检查源文件是否存在 if (!File.Exists(sourcePath)) { Debug.LogWarning($"[画报图片导出] 源文件不存在,跳过: {sourcePath}"); skippedCount++; continue; } // 确保目标目录存在 string targetDir = Path.GetDirectoryName(targetPath); if (!Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } // 复制文件 File.Copy(sourcePath, targetPath, true); copiedCount++; // 复制并修改 .meta 文件 string sourceMetaPath = sourcePath + ".meta"; string targetMetaPath = targetPath + ".meta"; if (File.Exists(sourceMetaPath)) { // 读取源 meta 文件 string metaContent = File.ReadAllText(sourceMetaPath); // 生成新的 GUID string newGuid = GUID.Generate().ToString(); // 替换 GUID(查找 "guid: " 后面的值) metaContent = System.Text.RegularExpressions.Regex.Replace( metaContent, @"guid: [a-f0-9]{32}", $"guid: {newGuid}", System.Text.RegularExpressions.RegexOptions.IgnoreCase ); // 写入目标 meta 文件 File.WriteAllText(targetMetaPath, metaContent); Debug.Log($"[画报图片导出] 已复制并更新meta: {Path.GetFileName(sourceMetaPath)} (新GUID: {newGuid})"); } else { Debug.LogWarning($"[画报图片导出] 源meta文件不存在: {sourceMetaPath}"); } Debug.Log($"[画报图片导出] 已复制: {sourcePath} -> {targetPath}"); } EditorUtility.ClearProgressBar(); // 刷新AssetDatabase Debug.Log("[画报图片导出] 正在刷新 AssetDatabase..."); AssetDatabase.Refresh(); EditorUtility.DisplayDialog( "导出完成", $"成功复制 {copiedCount} 个文件\n" + (skippedCount > 0 ? $"跳过 {skippedCount} 个不存在的文件\n" : "") + $"\n请查看控制台日志了解详情。", "确定" ); Debug.Log($"[画报图片导出] 导出完成,复制 {copiedCount} 个文件,跳过 {skippedCount} 个"); } catch (System.Exception e) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("错误", $"导出失败:\n{e.Message}", "确定"); Debug.LogError($"[画报图片导出] 导出失败: {e.Message}\n{e.StackTrace}"); } } /// /// 去除子资源后缀 [SpriteName] /// private string RemoveSubAssetSuffix(string path) { if (string.IsNullOrEmpty(path)) return path; // 检查是否是子资源格式:"Assets/xxx.png[SpriteName]" if (path.Contains("[") && path.EndsWith("]")) { int bracketIndex = path.IndexOf('['); return path.Substring(0, bracketIndex); } return path; } }