527 lines
19 KiB
C#
527 lines
19 KiB
C#
using System.IO;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using Newtonsoft.Json.Linq;
|
||
|
||
/// <summary>
|
||
/// UI贴图导出工具 - 将Art目录的UI贴图复制到Art_Out目录
|
||
/// </summary>
|
||
public class UITextureExporter : EditorWindow
|
||
{
|
||
[MenuItem("Tools/*美术资源工具/UI贴图导出(Art -> Art_Out)")]
|
||
public static void ShowWindow()
|
||
{
|
||
GetWindow<UITextureExporter>("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();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 目录导出标签页
|
||
/// </summary>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 画报图片导出标签页
|
||
/// </summary>
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出画报图片
|
||
/// </summary>
|
||
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<string> assetPaths = new HashSet<string>();
|
||
|
||
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<string> 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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 去除子资源后缀 [SpriteName]
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
|