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;
}
}