先修复一下,错误的场景 删除不必要的钓场资产 修复into场景 update:更新meta文件,修复报错 update:修复资源 修复第一章节建造 修复建造第三章 update:删除多余内容 update:更新README.md update:还原图标 update:更新README update:更新配置
791 lines
27 KiB
C#
791 lines
27 KiB
C#
using UnityEngine;
|
||
using UnityEngine.Playables;
|
||
using UnityEngine.Timeline;
|
||
using UnityEditor;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
|
||
public class AdvancedTimelineAudioBinder : EditorWindow
|
||
{
|
||
[SerializeField] private List<PlayableDirector> timelineDirectors = new List<PlayableDirector>();
|
||
[SerializeField] private Vector2 scrollPosition;
|
||
[SerializeField] private Vector2 timelineScrollPosition;
|
||
|
||
// 音频源设置
|
||
private GameObject audioSourcePrefab;
|
||
private AudioSource sceneAudioSource;
|
||
private bool useSceneObject = false;
|
||
private bool autoCreateParent = true;
|
||
private string parentName = "TimelineAudioSources";
|
||
|
||
// 轨道数据
|
||
private Dictionary<string, AudioTrackInfo> allAudioTracks = new Dictionary<string, AudioTrackInfo>();
|
||
private Dictionary<string, bool> trackSelection = new Dictionary<string, bool>();
|
||
|
||
// 扫描设置
|
||
private bool includeInactiveObjects = true;
|
||
private bool scanOnOpen = true;
|
||
|
||
// UI状态
|
||
private bool showTimelineList = true;
|
||
private bool showTrackList = true;
|
||
private bool showSettings = false;
|
||
|
||
[System.Serializable]
|
||
private class AudioTrackInfo
|
||
{
|
||
public AudioTrack track;
|
||
public PlayableDirector director;
|
||
public string timelineName;
|
||
public AudioSource currentBinding;
|
||
}
|
||
|
||
[MenuItem("Tools/AudioTools/Timeline Audio Binder")]
|
||
public static void ShowWindow()
|
||
{
|
||
var window = GetWindow<AdvancedTimelineAudioBinder>("Timeline Audio Binder");
|
||
window.minSize = new Vector2(500, 600);
|
||
if (window.scanOnOpen)
|
||
{
|
||
window.ScanSceneForTimelines();
|
||
}
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
if (scanOnOpen)
|
||
{
|
||
ScanSceneForTimelines();
|
||
}
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
DrawHeader();
|
||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||
|
||
DrawTimelineSelectionSection();
|
||
DrawAudioSourceSection();
|
||
DrawTrackManagementSection();
|
||
DrawSettingsSection();
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
DrawFooter();
|
||
}
|
||
|
||
private void DrawHeader()
|
||
{
|
||
GUILayout.Space(10);
|
||
EditorGUILayout.LabelField("高级 Timeline Audio轨道批量绑定工具", EditorStyles.boldLabel);
|
||
EditorGUILayout.Space();
|
||
}
|
||
|
||
private void DrawTimelineSelectionSection()
|
||
{
|
||
EditorGUILayout.BeginVertical("box");
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
showTimelineList = EditorGUILayout.Foldout(showTimelineList, $"场景中的Timeline ({timelineDirectors.Count})", true);
|
||
if (GUILayout.Button("扫描场景", GUILayout.Width(80)))
|
||
{
|
||
ScanSceneForTimelines();
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
if (showTimelineList && timelineDirectors.Count > 0)
|
||
{
|
||
timelineScrollPosition = EditorGUILayout.BeginScrollView(timelineScrollPosition, GUILayout.Height(150));
|
||
|
||
for (int i = 0; i < timelineDirectors.Count; i++)
|
||
{
|
||
EditorGUILayout.BeginHorizontal();
|
||
|
||
// 显示选择框
|
||
bool isSelected = timelineDirectors[i] != null;
|
||
bool newSelected = EditorGUILayout.Toggle(isSelected, GUILayout.Width(20));
|
||
if (newSelected != isSelected && timelineDirectors[i] != null)
|
||
{
|
||
if (newSelected)
|
||
{
|
||
// 已选中,无需操作
|
||
}
|
||
else
|
||
{
|
||
timelineDirectors[i] = null;
|
||
}
|
||
}
|
||
|
||
// 显示Timeline信息
|
||
if (timelineDirectors[i] != null)
|
||
{
|
||
EditorGUILayout.LabelField($"{i + 1}. {timelineDirectors[i].name}", EditorStyles.boldLabel);
|
||
if (timelineDirectors[i].playableAsset != null)
|
||
{
|
||
EditorGUILayout.LabelField($"Asset: {timelineDirectors[i].playableAsset.name}", EditorStyles.miniLabel);
|
||
}
|
||
|
||
// 操作按钮
|
||
if (GUILayout.Button("选择", GUILayout.Width(40)))
|
||
{
|
||
Selection.activeObject = timelineDirectors[i].gameObject;
|
||
}
|
||
if (GUILayout.Button("扫描轨道", GUILayout.Width(60)))
|
||
{
|
||
ScanAudioTracksForDirector(timelineDirectors[i]);
|
||
}
|
||
if (GUILayout.Button("移除", GUILayout.Width(40)))
|
||
{
|
||
timelineDirectors.RemoveAt(i);
|
||
i--;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.LabelField("无效的Director", EditorStyles.miniLabel);
|
||
if (GUILayout.Button("移除", GUILayout.Width(40)))
|
||
{
|
||
timelineDirectors.RemoveAt(i);
|
||
i--;
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
EditorGUILayout.Space();
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("清空列表"))
|
||
{
|
||
timelineDirectors.Clear();
|
||
}
|
||
if (GUILayout.Button("移除无效项"))
|
||
{
|
||
timelineDirectors.RemoveAll(d => d == null);
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
else if (showTimelineList)
|
||
{
|
||
EditorGUILayout.HelpBox("未找到Timeline,点击\"扫描场景\"按钮查找", MessageType.Info);
|
||
}
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space();
|
||
}
|
||
|
||
private void DrawAudioSourceSection()
|
||
{
|
||
EditorGUILayout.BeginVertical("box");
|
||
EditorGUILayout.LabelField("音频源设置", EditorStyles.boldLabel);
|
||
|
||
// 音频源类型选择
|
||
EditorGUILayout.BeginHorizontal();
|
||
bool prevUseSceneObject = useSceneObject;
|
||
useSceneObject = GUILayout.Toggle(useSceneObject, "使用场景AudioSource", GUILayout.Width(150));
|
||
if (useSceneObject != prevUseSceneObject)
|
||
{
|
||
if (useSceneObject) audioSourcePrefab = null;
|
||
else sceneAudioSource = null;
|
||
}
|
||
|
||
useSceneObject = !GUILayout.Toggle(!useSceneObject, "使用Prefab创建", GUILayout.Width(150));
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.Space();
|
||
|
||
if (useSceneObject)
|
||
{
|
||
// 场景AudioSource模式
|
||
AudioSource newAudioSource = (AudioSource)EditorGUILayout.ObjectField(
|
||
"场景AudioSource", sceneAudioSource, typeof(AudioSource), true);
|
||
|
||
if (newAudioSource != sceneAudioSource)
|
||
{
|
||
sceneAudioSource = newAudioSource;
|
||
}
|
||
|
||
if (sceneAudioSource != null)
|
||
{
|
||
EditorGUILayout.HelpBox($"将绑定到: {sceneAudioSource.name}", MessageType.Info);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("定位对象"))
|
||
{
|
||
Selection.activeObject = sceneAudioSource.gameObject;
|
||
SceneView.lastActiveSceneView?.FrameSelected();
|
||
}
|
||
if (GUILayout.Button("创建新AudioSource"))
|
||
{
|
||
CreateNewSceneAudioSource();
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
else
|
||
{
|
||
if (GUILayout.Button("创建新AudioSource"))
|
||
{
|
||
CreateNewSceneAudioSource();
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Prefab模式
|
||
GameObject newPrefab = (GameObject)EditorGUILayout.ObjectField(
|
||
"AudioSource Prefab", audioSourcePrefab, typeof(GameObject), false);
|
||
|
||
if (newPrefab != audioSourcePrefab)
|
||
{
|
||
if (newPrefab != null)
|
||
{
|
||
PrefabAssetType prefabType = PrefabUtility.GetPrefabAssetType(newPrefab);
|
||
if (prefabType == PrefabAssetType.NotAPrefab)
|
||
{
|
||
if (EditorUtility.DisplayDialog("提示",
|
||
"选择的是场景对象,切换到场景对象模式?", "是", "否"))
|
||
{
|
||
useSceneObject = true;
|
||
sceneAudioSource = newPrefab.GetComponent<AudioSource>();
|
||
audioSourcePrefab = null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
audioSourcePrefab = newPrefab;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
audioSourcePrefab = null;
|
||
}
|
||
}
|
||
|
||
if (audioSourcePrefab != null)
|
||
{
|
||
// 验证Prefab
|
||
AudioSource audioSource = audioSourcePrefab.GetComponent<AudioSource>();
|
||
if (audioSource == null)
|
||
{
|
||
EditorGUILayout.HelpBox("Prefab不包含AudioSource组件!", MessageType.Error);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.HelpBox("Prefab验证通过", MessageType.Info);
|
||
}
|
||
|
||
if (GUILayout.Button("在Project中定位"))
|
||
{
|
||
EditorGUIUtility.PingObject(audioSourcePrefab);
|
||
}
|
||
}
|
||
|
||
// Prefab模式设置
|
||
autoCreateParent = EditorGUILayout.Toggle("自动创建父对象", autoCreateParent);
|
||
if (autoCreateParent)
|
||
{
|
||
parentName = EditorGUILayout.TextField("父对象名称", parentName);
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space();
|
||
}
|
||
|
||
private void DrawTrackManagementSection()
|
||
{
|
||
if (allAudioTracks.Count == 0) return;
|
||
|
||
EditorGUILayout.BeginVertical("box");
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
showTrackList = EditorGUILayout.Foldout(showTrackList, $"音频轨道列表 ({allAudioTracks.Count})", true);
|
||
|
||
// 批量操作按钮
|
||
if (GUILayout.Button("全选", GUILayout.Width(60)))
|
||
{
|
||
SelectAllTracks(true);
|
||
}
|
||
if (GUILayout.Button("反选", GUILayout.Width(60)))
|
||
{
|
||
InvertTrackSelection();
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
if (showTrackList)
|
||
{
|
||
EditorGUILayout.Space();
|
||
|
||
// 轨道列表
|
||
// 修复:创建键的副本以避免枚举时修改
|
||
var trackKeys = allAudioTracks.Keys.ToList();
|
||
|
||
foreach (var trackKey in trackKeys)
|
||
{
|
||
if (!allAudioTracks.ContainsKey(trackKey) || allAudioTracks[trackKey].track == null)
|
||
continue;
|
||
|
||
var trackEntry = allAudioTracks[trackKey];
|
||
|
||
EditorGUILayout.BeginVertical("box");
|
||
|
||
// 轨道头信息
|
||
EditorGUILayout.BeginHorizontal();
|
||
bool isSelected = trackSelection.ContainsKey(trackKey) && trackSelection[trackKey];
|
||
bool newSelected = EditorGUILayout.Toggle(isSelected, GUILayout.Width(20));
|
||
if (newSelected != isSelected)
|
||
{
|
||
trackSelection[trackKey] = newSelected;
|
||
}
|
||
|
||
EditorGUILayout.LabelField(trackEntry.track.name, EditorStyles.boldLabel);
|
||
EditorGUILayout.LabelField($"Timeline: {trackEntry.timelineName}", EditorStyles.miniLabel, GUILayout.Width(150));
|
||
|
||
// 绑定状态
|
||
AudioSource currentBinding = trackEntry.currentBinding;
|
||
if (currentBinding != null)
|
||
{
|
||
EditorGUILayout.LabelField($"已绑定: {currentBinding.name}", EditorStyles.miniLabel);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.LabelField("未绑定", EditorStyles.miniLabel);
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
// 单个轨道操作
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("定位轨道", GUILayout.Height(20)))
|
||
{
|
||
Selection.activeObject = trackEntry.director;
|
||
EditorGUIUtility.PingObject(trackEntry.director);
|
||
}
|
||
if (GUILayout.Button("解绑", GUILayout.Height(20)))
|
||
{
|
||
UnbindSingleTrack(trackKey);
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space();
|
||
}
|
||
|
||
// 批量操作按钮
|
||
int selectedCount = trackSelection.Values.Count(v => v);
|
||
bool canOperate = selectedCount > 0 && (
|
||
(useSceneObject && sceneAudioSource != null) ||
|
||
(!useSceneObject && audioSourcePrefab != null));
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
GUI.enabled = canOperate;
|
||
if (GUILayout.Button($"绑定选中轨道 ({selectedCount})", GUILayout.Height(30)))
|
||
{
|
||
BindSelectedTracks();
|
||
}
|
||
GUI.enabled = selectedCount > 0;
|
||
if (GUILayout.Button($"解绑选中轨道 ({selectedCount})", GUILayout.Height(30)))
|
||
{
|
||
UnbindSelectedTracks();
|
||
}
|
||
GUI.enabled = true;
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space();
|
||
}
|
||
|
||
private void DrawSettingsSection()
|
||
{
|
||
EditorGUILayout.BeginVertical("box");
|
||
|
||
showSettings = EditorGUILayout.Foldout(showSettings, "设置", true);
|
||
if (showSettings)
|
||
{
|
||
includeInactiveObjects = EditorGUILayout.Toggle("包含非激活对象", includeInactiveObjects);
|
||
scanOnOpen = EditorGUILayout.Toggle("打开时自动扫描", scanOnOpen);
|
||
|
||
EditorGUILayout.Space();
|
||
|
||
if (GUILayout.Button("扫描所有Timeline的音频轨道"))
|
||
{
|
||
ScanAllAudioTracks();
|
||
}
|
||
|
||
if (GUILayout.Button("清除所有绑定"))
|
||
{
|
||
if (EditorUtility.DisplayDialog("确认", "确定要清除所有Timeline的音频轨道绑定吗?", "是", "否"))
|
||
{
|
||
UnbindAllTracks();
|
||
}
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
private void DrawFooter()
|
||
{
|
||
EditorGUILayout.Space();
|
||
EditorGUILayout.HelpBox(
|
||
"使用说明:\n" +
|
||
"1. 点击\"扫描场景\"查找所有Timeline\n" +
|
||
"2. 选择音频源类型(场景对象或Prefab)\n" +
|
||
"3. 扫描音频轨道并选择要绑定的轨道\n" +
|
||
"4. 点击绑定按钮完成操作",
|
||
MessageType.Info);
|
||
}
|
||
|
||
private void ScanSceneForTimelines()
|
||
{
|
||
timelineDirectors.Clear();
|
||
|
||
// 查找场景中所有PlayableDirector组件
|
||
PlayableDirector[] directors = FindObjectsOfType<PlayableDirector>(includeInactiveObjects);
|
||
|
||
foreach (var director in directors)
|
||
{
|
||
if (director.playableAsset is TimelineAsset)
|
||
{
|
||
timelineDirectors.Add(director);
|
||
}
|
||
}
|
||
|
||
// 按名称排序
|
||
timelineDirectors = timelineDirectors.OrderBy(d => d.name).ToList();
|
||
|
||
Debug.Log($"找到 {timelineDirectors.Count} 个Timeline");
|
||
}
|
||
|
||
private void ScanAllAudioTracks()
|
||
{
|
||
allAudioTracks.Clear();
|
||
trackSelection.Clear();
|
||
|
||
int totalTracks = 0;
|
||
|
||
foreach (var director in timelineDirectors)
|
||
{
|
||
if (director == null || director.playableAsset == null) continue;
|
||
|
||
int tracksInDirector = ScanAudioTracksForDirector(director, false);
|
||
totalTracks += tracksInDirector;
|
||
}
|
||
|
||
Debug.Log($"扫描完成,共找到 {totalTracks} 个音频轨道");
|
||
Repaint();
|
||
}
|
||
|
||
private int ScanAudioTracksForDirector(PlayableDirector director, bool showDialog = true)
|
||
{
|
||
if (director == null || director.playableAsset == null) return 0;
|
||
|
||
TimelineAsset timeline = director.playableAsset as TimelineAsset;
|
||
if (timeline == null) return 0;
|
||
|
||
int trackCount = 0;
|
||
|
||
foreach (var track in timeline.GetOutputTracks())
|
||
{
|
||
if (track is AudioTrack audioTrack)
|
||
{
|
||
string trackKey = $"{director.GetInstanceID()}_{audioTrack.GetInstanceID()}";
|
||
|
||
// 获取当前绑定状态
|
||
AudioSource currentBinding = director.GetGenericBinding(audioTrack) as AudioSource;
|
||
|
||
allAudioTracks[trackKey] = new AudioTrackInfo
|
||
{
|
||
track = audioTrack,
|
||
director = director,
|
||
timelineName = director.name,
|
||
currentBinding = currentBinding
|
||
};
|
||
|
||
trackSelection[trackKey] = true; // 默认选中
|
||
trackCount++;
|
||
}
|
||
}
|
||
|
||
if (showDialog)
|
||
{
|
||
Debug.Log($"在 {director.name} 中找到 {trackCount} 个音频轨道");
|
||
}
|
||
|
||
return trackCount;
|
||
}
|
||
|
||
private void SelectAllTracks(bool select)
|
||
{
|
||
var keys = new List<string>(trackSelection.Keys);
|
||
foreach (var key in keys)
|
||
{
|
||
trackSelection[key] = select;
|
||
}
|
||
Repaint();
|
||
}
|
||
|
||
private void InvertTrackSelection()
|
||
{
|
||
var keys = new List<string>(trackSelection.Keys);
|
||
foreach (var key in keys)
|
||
{
|
||
trackSelection[key] = !trackSelection[key];
|
||
}
|
||
Repaint();
|
||
}
|
||
|
||
private void CreateNewSceneAudioSource()
|
||
{
|
||
GameObject audioObject = new GameObject("AudioTimeline");
|
||
AudioSource newAudioSource = audioObject.AddComponent<AudioSource>();
|
||
sceneAudioSource = newAudioSource;
|
||
|
||
// 定位到新创建的对象
|
||
Selection.activeObject = audioObject;
|
||
SceneView.lastActiveSceneView?.FrameSelected();
|
||
|
||
Debug.Log("创建新的AudioSource: " + audioObject.name);
|
||
}
|
||
|
||
private void BindSelectedTracks()
|
||
{
|
||
if (useSceneObject)
|
||
{
|
||
if (sceneAudioSource == null)
|
||
{
|
||
EditorUtility.DisplayDialog("错误", "请先选择场景中的AudioSource", "确定");
|
||
return;
|
||
}
|
||
BindToSceneAudioSource();
|
||
}
|
||
else
|
||
{
|
||
if (audioSourcePrefab == null)
|
||
{
|
||
EditorUtility.DisplayDialog("错误", "请先选择AudioSource Prefab", "确定");
|
||
return;
|
||
}
|
||
BindUsingPrefab();
|
||
}
|
||
}
|
||
|
||
private void BindToSceneAudioSource()
|
||
{
|
||
int boundCount = 0;
|
||
|
||
// 修复:创建键的副本以避免枚举时修改集合
|
||
var keysToProcess = new List<string>(allAudioTracks.Keys);
|
||
|
||
foreach (var key in keysToProcess)
|
||
{
|
||
// 检查键是否仍然存在于字典中
|
||
if (!allAudioTracks.ContainsKey(key) || !trackSelection.ContainsKey(key) || !trackSelection[key])
|
||
continue;
|
||
|
||
AudioTrackInfo trackInfo = allAudioTracks[key];
|
||
if (trackInfo.track == null || trackInfo.director == null) continue;
|
||
|
||
Undo.RecordObject(trackInfo.director, "Bind Audio Track");
|
||
trackInfo.director.SetGenericBinding(trackInfo.track, sceneAudioSource);
|
||
|
||
// 更新绑定状态 - 创建新对象而不是修改现有对象
|
||
allAudioTracks[key] = new AudioTrackInfo
|
||
{
|
||
track = trackInfo.track,
|
||
director = trackInfo.director,
|
||
timelineName = trackInfo.timelineName,
|
||
currentBinding = sceneAudioSource
|
||
};
|
||
|
||
boundCount++;
|
||
}
|
||
|
||
RefreshAllEditorWindows();
|
||
Debug.Log($"成功绑定了 {boundCount} 个音频轨道到 {sceneAudioSource.name}");
|
||
EditorUtility.DisplayDialog("完成", $"成功绑定了 {boundCount} 个音频轨道", "确定");
|
||
Repaint();
|
||
}
|
||
|
||
private void BindUsingPrefab()
|
||
{
|
||
int boundCount = 0;
|
||
GameObject audioParent = null;
|
||
|
||
if (autoCreateParent)
|
||
{
|
||
audioParent = GameObject.Find(parentName);
|
||
if (audioParent == null)
|
||
{
|
||
audioParent = new GameObject(parentName);
|
||
Undo.RegisterCreatedObjectUndo(audioParent, "Create Audio Parent");
|
||
}
|
||
}
|
||
|
||
// 修复:创建键的副本以避免枚举时修改集合
|
||
var keysToProcess = new List<string>(allAudioTracks.Keys);
|
||
|
||
foreach (var key in keysToProcess)
|
||
{
|
||
// 检查键是否仍然存在于字典中
|
||
if (!allAudioTracks.ContainsKey(key) || !trackSelection.ContainsKey(key) || !trackSelection[key])
|
||
continue;
|
||
|
||
AudioTrackInfo trackInfo = allAudioTracks[key];
|
||
if (trackInfo.track == null || trackInfo.director == null) continue;
|
||
|
||
// 创建或获取AudioSource实例
|
||
string sourceName = $"AudioSource_{trackInfo.timelineName}_{trackInfo.track.name}";
|
||
GameObject audioInstance = GameObject.Find(sourceName);
|
||
|
||
if (audioInstance == null)
|
||
{
|
||
audioInstance = (GameObject)PrefabUtility.InstantiatePrefab(audioSourcePrefab);
|
||
audioInstance.name = sourceName;
|
||
|
||
if (audioParent != null)
|
||
{
|
||
audioInstance.transform.SetParent(audioParent.transform);
|
||
}
|
||
|
||
Undo.RegisterCreatedObjectUndo(audioInstance, "Create Audio Source");
|
||
}
|
||
|
||
AudioSource audioSource = audioInstance.GetComponent<AudioSource>();
|
||
if (audioSource != null)
|
||
{
|
||
Undo.RecordObject(trackInfo.director, "Bind Audio Track");
|
||
trackInfo.director.SetGenericBinding(trackInfo.track, audioSource);
|
||
|
||
// 更新绑定状态 - 创建新对象而不是修改现有对象
|
||
allAudioTracks[key] = new AudioTrackInfo
|
||
{
|
||
track = trackInfo.track,
|
||
director = trackInfo.director,
|
||
timelineName = trackInfo.timelineName,
|
||
currentBinding = audioSource
|
||
};
|
||
|
||
boundCount++;
|
||
}
|
||
}
|
||
|
||
RefreshAllEditorWindows();
|
||
Debug.Log($"成功绑定了 {boundCount} 个音频轨道");
|
||
EditorUtility.DisplayDialog("完成", $"成功绑定了 {boundCount} 个音频轨道", "确定");
|
||
Repaint();
|
||
}
|
||
|
||
private void UnbindSelectedTracks()
|
||
{
|
||
int unboundCount = 0;
|
||
|
||
// 修复:创建键的副本以避免枚举时修改集合
|
||
var keysToProcess = new List<string>(allAudioTracks.Keys);
|
||
|
||
foreach (var key in keysToProcess)
|
||
{
|
||
// 检查键是否仍然存在于字典中
|
||
if (!allAudioTracks.ContainsKey(key) || !trackSelection.ContainsKey(key) || !trackSelection[key])
|
||
continue;
|
||
|
||
AudioTrackInfo trackInfo = allAudioTracks[key];
|
||
if (trackInfo.track == null || trackInfo.director == null) continue;
|
||
|
||
Undo.RecordObject(trackInfo.director, "Unbind Audio Track");
|
||
trackInfo.director.SetGenericBinding(trackInfo.track, null);
|
||
|
||
// 更新绑定状态 - 创建新对象而不是修改现有对象
|
||
allAudioTracks[key] = new AudioTrackInfo
|
||
{
|
||
track = trackInfo.track,
|
||
director = trackInfo.director,
|
||
timelineName = trackInfo.timelineName,
|
||
currentBinding = null
|
||
};
|
||
|
||
unboundCount++;
|
||
}
|
||
|
||
RefreshAllEditorWindows();
|
||
Debug.Log($"成功解绑了 {unboundCount} 个音频轨道");
|
||
EditorUtility.DisplayDialog("完成", $"成功解绑了 {unboundCount} 个音频轨道", "确定");
|
||
Repaint();
|
||
}
|
||
|
||
private void UnbindSingleTrack(string trackKey)
|
||
{
|
||
if (allAudioTracks.ContainsKey(trackKey))
|
||
{
|
||
AudioTrackInfo trackInfo = allAudioTracks[trackKey];
|
||
if (trackInfo.track != null && trackInfo.director != null)
|
||
{
|
||
Undo.RecordObject(trackInfo.director, "Unbind Audio Track");
|
||
trackInfo.director.SetGenericBinding(trackInfo.track, null);
|
||
|
||
// 更新绑定状态 - 创建新对象而不是修改现有对象
|
||
allAudioTracks[trackKey] = new AudioTrackInfo
|
||
{
|
||
track = trackInfo.track,
|
||
director = trackInfo.director,
|
||
timelineName = trackInfo.timelineName,
|
||
currentBinding = null
|
||
};
|
||
|
||
RefreshAllEditorWindows();
|
||
Debug.Log($"已解绑轨道: {trackInfo.track.name}");
|
||
Repaint();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void UnbindAllTracks()
|
||
{
|
||
int unboundCount = 0;
|
||
|
||
// 修复:创建键的副本以避免枚举时修改集合
|
||
var keysToProcess = new List<string>(allAudioTracks.Keys);
|
||
|
||
foreach (var key in keysToProcess)
|
||
{
|
||
if (!allAudioTracks.ContainsKey(key)) continue;
|
||
|
||
AudioTrackInfo trackInfo = allAudioTracks[key];
|
||
if (trackInfo.track == null || trackInfo.director == null) continue;
|
||
|
||
Undo.RecordObject(trackInfo.director, "Unbind Audio Track");
|
||
trackInfo.director.SetGenericBinding(trackInfo.track, null);
|
||
unboundCount++;
|
||
}
|
||
|
||
// 重新扫描更新状态
|
||
ScanAllAudioTracks();
|
||
RefreshAllEditorWindows();
|
||
|
||
Debug.Log($"已解绑所有 {unboundCount} 个音频轨道");
|
||
EditorUtility.DisplayDialog("完成", $"已解绑所有 {unboundCount} 个音频轨道", "确定");
|
||
}
|
||
|
||
private void RefreshAllEditorWindows()
|
||
{
|
||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||
|
||
// 刷新Timeline窗口
|
||
System.Type timelineWindowType = System.Type.GetType("UnityEditor.Timeline.TimelineWindow,Unity.Timeline.Editor");
|
||
if (timelineWindowType != null)
|
||
{
|
||
var timelineWindow = EditorWindow.GetWindow(timelineWindowType);
|
||
if (timelineWindow != null)
|
||
{
|
||
timelineWindow.Repaint();
|
||
}
|
||
}
|
||
|
||
// 立即应用修改
|
||
AssetDatabase.Refresh();
|
||
EditorApplication.RepaintHierarchyWindow();
|
||
}
|
||
} |