Files
MinFt/Client/Assets/Editor/VideoTool/AudioGUIDTransfer.cs
Liubing\LB 3d8d4d18f3 first commit
先修复一下,错误的场景

删除不必要的钓场资产

修复into场景

update:更新meta文件,修复报错

update:修复资源

修复第一章节建造

修复建造第三章

update:删除多余内容

update:更新README.md

update:还原图标

update:更新README

update:更新配置
2026-04-28 02:17:22 +08:00

326 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEditor;
using UnityEngine;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// 音频GUID转移工具 - 将MP3/WAV文件的GUID转移到OGG文件
///
/// 此脚本解决了Unity Timeline中音频资源格式转换的问题
/// 当需要将Timeline中的WAV/MP3音频替换为OGG格式时直接替换文件会导致引用丢失。
/// 本工具通过转移GUID确保Timeline中的引用保持不变。
/// </summary>
public static class AudioGUIDTransfer
{
// 定义我们关心的原始音频扩展名
private static readonly HashSet<string> OriginalAudioExtensions = new HashSet<string>
{
".mp3",
".wav"
};
// 定义目标音频扩展名
private const string TargetAudioExtension = ".ogg";
// 在Unity菜单栏的 "Assets" 下添加一个菜单项
[MenuItem("Assets/AudioFormatTransfer")]
public static void TransferGUIDsInSelectedFolder()
{
// 获取当前在Project窗口中选中的对象
Object selectedObject = Selection.activeObject;
// --- 输入验证 ---
if (selectedObject == null)
{
EditorUtility.DisplayDialog("错误", "请在Project窗口中选择一个包含音频文件的文件夹。", "确定");
return;
}
string selectedPath = AssetDatabase.GetAssetPath(selectedObject);
if (!AssetDatabase.IsValidFolder(selectedPath))
{
EditorUtility.DisplayDialog("错误", "请选择一个有效的文件夹。", "确定");
return;
}
// --- 确认对话框(对于破坏性操作至关重要) ---
if (!EditorUtility.DisplayDialog("确认操作",
$"您确定要将文件夹 '{selectedPath}' 中所有MP3/WAV文件的GUID转移到对应的同名OGG文件并删除原始文件吗\n\n此操作不可逆请务必提前备份您的项目",
"是", "否"))
{
return; // 用户取消了操作
}
// --- 核心处理逻辑 ---
ProcessFolder(selectedPath);
// --- 完成消息 ---
EditorUtility.DisplayDialog("完成", "MP3/WAV GUID转移到OGG并删除原始文件的操作已完成。请检查Console窗口获取详细日志。", "确定");
}
/// <summary>
/// 处理给定文件夹将MP3/WAV文件的GUID转移到对应的OGG文件并删除原始文件。
/// </summary>
/// <param name="folderPath">要处理的文件夹的完整路径。</param>
private static void ProcessFolder(string folderPath)
{
// 获取选中文件夹中所有文件(仅限顶层目录,不包括子文件夹)
string[] allFilesInFolder = Directory.GetFiles(folderPath, "*.*", SearchOption.TopDirectoryOnly);
// 按基础名称(不含扩展名)对文件进行分组
Dictionary<string, List<string>> fileGroups = new Dictionary<string, List<string>>();
foreach (string filePath in allFilesInFolder)
{
// 跳过.meta文件因为我们只关心实际资源文件
if (filePath.EndsWith(".meta")) continue;
string fileName = Path.GetFileName(filePath);
string baseName = GetBaseNameWithoutExtensions(fileName);
if (!fileGroups.ContainsKey(baseName))
{
fileGroups.Add(baseName, new List<string>());
}
fileGroups[baseName].Add(filePath);
}
int processedCount = 0;
int skippedCount = 0;
// 遍历每个文件组
foreach (var group in fileGroups)
{
string baseName = group.Key;
List<string> filesInGroup = group.Value;
// 尝试找到一个原始文件MP3/WAV和一个OGG文件
string originalFilePath = null;
string oggFilePath = null;
foreach (string filePath in filesInGroup)
{
string ext = Path.GetExtension(filePath).ToLower();
if (ext == TargetAudioExtension)
{
if (oggFilePath != null)
{
Debug.LogWarning($"在 '{folderPath}' 文件夹中发现多个同名OGG文件 '{baseName}{TargetAudioExtension}'。跳过此组文件。");
oggFilePath = null;
break;
}
oggFilePath = filePath;
}
else if (OriginalAudioExtensions.Contains(ext))
{
if (originalFilePath != null)
{
Debug.LogWarning($"在 '{folderPath}' 文件夹中发现多个同名原始音频文件MP3/WAV'{baseName}'。跳过此组文件。");
originalFilePath = null;
break;
}
originalFilePath = filePath;
}
}
// 如果找到了一个有效的配对一个原始文件一个OGG文件
if (originalFilePath != null && oggFilePath != null)
{
// 转换为Unity资产路径格式
string assetOriginalPath = originalFilePath.Replace(Application.dataPath, "Assets").Replace('\\', '/');
string assetOggPath = oggFilePath.Replace(Application.dataPath, "Assets").Replace('\\', '/');
// 从原始文件的.meta文件中获取GUID
string originalMetaPath = originalFilePath + ".meta";
string originalGuid = GetGuidFromMetaFile(originalMetaPath);
if (string.IsNullOrEmpty(originalGuid))
{
Debug.LogError($"无法从原始文件 '{originalFilePath}' 的.meta文件中读取GUID。跳过此文件。");
skippedCount++;
continue;
}
Debug.Log($"原始文件 '{assetOriginalPath}' 的GUID为: {originalGuid}");
// 关键步骤1删除OGG文件的.meta文件如果存在
string oggMetaPath = oggFilePath + ".meta";
if (File.Exists(oggMetaPath))
{
try
{
File.Delete(oggMetaPath);
Debug.Log($"已删除OGG文件的.meta文件: '{oggMetaPath}'");
}
catch (System.Exception ex)
{
Debug.LogError($"删除OGG文件的.meta文件时出错: {ex.Message}");
skippedCount++;
continue;
}
}
// 关键步骤2删除原始文件MP3/WAV
// 注意我们不使用AssetDatabase.DeleteAsset因为它可能不会完全释放GUID
try
{
// 先删除原始文件的.meta文件
if (File.Exists(originalMetaPath))
{
File.Delete(originalMetaPath);
Debug.Log($"已删除原始文件的.meta文件: '{originalMetaPath}'");
}
// 再删除原始文件本身
if (File.Exists(originalFilePath))
{
File.Delete(originalFilePath);
Debug.Log($"已删除原始文件: '{originalFilePath}'");
}
}
catch (System.Exception ex)
{
Debug.LogError($"删除原始文件或其.meta文件时出错: {ex.Message}");
skippedCount++;
continue;
}
// 关键步骤3刷新资产数据库确保Unity知道文件已被删除
AssetDatabase.Refresh();
// 关键步骤4为OGG文件创建新的.meta文件使用原始文件的GUID
string metaContent = GenerateMetaFileContent(originalGuid);
try
{
File.WriteAllText(oggMetaPath, metaContent);
Debug.Log($"已为OGG文件创建新的.meta文件使用原始GUID: '{oggMetaPath}'");
}
catch (System.Exception ex)
{
Debug.LogError($"为OGG文件创建.meta文件时出错: {ex.Message}");
skippedCount++;
continue;
}
// 关键步骤5再次刷新资产数据库确保Unity导入新的.meta文件
AssetDatabase.Refresh();
// 关键步骤6验证OGG文件的GUID是否正确
string newOggGuid = AssetDatabase.AssetPathToGUID(assetOggPath);
if (newOggGuid == originalGuid)
{
Debug.Log($"<color=green>成功</color>: 文件 '{assetOggPath}' 现在使用原始文件的GUID: {originalGuid}");
processedCount++;
}
else
{
Debug.LogError($"GUID转移失败! OGG文件 '{assetOggPath}' 的GUID为 {newOggGuid},而不是预期的 {originalGuid}");
skippedCount++;
}
}
else if (originalFilePath == null && oggFilePath != null)
{
// 只有OGG文件没有对应的原始文件这是正常的不需要警告
continue;
}
else if (originalFilePath != null && oggFilePath == null)
{
Debug.LogWarning($"找到原始文件 '{originalFilePath}'但没有对应的OGG文件。跳过此文件。");
skippedCount++;
}
}
// 最终刷新确保所有更改都已反映在Unity中
AssetDatabase.Refresh();
Debug.Log($"处理完成:成功处理 {processedCount} 对文件,跳过 {skippedCount} 对文件。");
}
/// <summary>
/// 从文件名中提取基础名称,移除所有已知的音频扩展名。
/// </summary>
private static string GetBaseNameWithoutExtensions(string fileName)
{
string baseName = Path.GetFileNameWithoutExtension(fileName);
// 检查是否还有其他音频扩展名
foreach (string ext in OriginalAudioExtensions.Concat(new[] { TargetAudioExtension }))
{
if (baseName.EndsWith(ext, System.StringComparison.OrdinalIgnoreCase))
{
baseName = baseName.Substring(0, baseName.Length - ext.Length);
}
}
return baseName;
}
/// <summary>
/// 从Unity的.meta文件中提取GUID。
/// </summary>
private static string GetGuidFromMetaFile(string metaFilePath)
{
if (!File.Exists(metaFilePath))
{
Debug.LogError($"Meta文件不存在: {metaFilePath}");
return null;
}
string metaContent = File.ReadAllText(metaFilePath);
Match match = Regex.Match(metaContent, @"guid: ([0-9a-fA-F]{32})");
if (match.Success)
{
return match.Groups[1].Value;
}
return null;
}
/// <summary>
/// 生成包含指定GUID的.meta文件内容。
/// </summary>
private static string GenerateMetaFileContent(string guid)
{
// 创建一个基本的AudioImporter .meta文件内容
return $@"fileFormatVersion: 2
guid: {guid}
AudioImporter:
externalObjects: {{}}
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {{}}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
";
}
// 菜单项的验证方法
[MenuItem("Assets/Transfer MP3/WAV GUID to OGG in Selected Folder", true)]
public static bool ValidateTransferGUIDsInSelectedFolder()
{
// 仅当在Project窗口中选中单个文件夹时才启用菜单项
if (Selection.activeObject == null) return false;
string selectedPath = AssetDatabase.GetAssetPath(Selection.activeObject);
return AssetDatabase.IsValidFolder(selectedPath);
}
}