Files
aibis-dream/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs
T

323 lines
8.9 KiB
C#

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.ResourceManagement.AsyncOperations;
using AibisDream.Framework;
namespace AibisDream
{
public class DirectorHandler : MonoBehaviour
{
private const string TimelinePrefix = "Timeline/";
public string timelineName;
[SerializeField] private PlayableDirector director;
private Action _onCompleteCallback;
private AsyncOperationHandle<PlayableAsset> _addressableHandle;
private PlayableAsset _savedPlayableAsset;
#if UNITY_EDITOR
/// <summary>
/// 在 Editor 中,当组件创建或 Inspector 变化时自动查找同物体上的 PlayableDirector
/// </summary>
private void OnValidate()
{
if (director == null)
{
director = GetComponent<PlayableDirector>();
}
}
#endif
private void Awake()
{
if (director == null)
{
director = GetComponent<PlayableDirector>();
}
}
private void Start()
{
TimelineCenter.Instance.RegisterDirector(this);
}
private void OnDestroy()
{
TimelineCenter.Instance.UnregisterDirector(this);
if (director != null)
{
director.stopped -= OnDirectorStopped;
}
ReleaseAddressableHandle();
}
private void ReleaseAddressableHandle()
{
if (!_addressableHandle.IsValid()) return;
if (director != null && director.playableAsset == _addressableHandle.Result)
{
director.playableAsset = _savedPlayableAsset;
}
ResourceKit.Release(_addressableHandle);
_addressableHandle = default;
}
/// <summary>
/// 播放 Timeline
/// </summary>
public void Play(Action onComplete = null)
{
if (director == null)
{
Debug.LogError($"DirectorHandler {timelineName}: PlayableDirector is null");
return;
}
// 先停止再播放
director.stopped -= OnDirectorStopped;
if (director.state == PlayState.Playing)
{
director.Stop();
}
_onCompleteCallback = onComplete;
// 如果有完成回调,订阅 stopped 事件
if (onComplete != null)
{
director.stopped += OnDirectorStopped;
}
director.Play();
}
/// <summary>
/// 异步播放 Timeline(协程版本)
/// </summary>
public IEnumerator PlayAsync()
{
if (director == null)
{
Debug.LogError($"DirectorHandler {timelineName}: PlayableDirector is null");
yield break;
}
// 先停止再播放
if (director.state == PlayState.Playing)
{
director.Stop();
}
director.Play();
// 等待播放完成
while (director.state == PlayState.Playing)
{
yield return null;
}
}
/// <summary>
/// 从 Addressables 加载 PlayableAsset 并播放(加载时自动加 "Timeline/" 前缀)
/// </summary>
public void PlayFromAddressable(string addressableKey, Action onComplete = null)
{
StartCoroutine(PlayFromAddressableCoroutine(addressableKey, onComplete));
}
/// <summary>
/// 从 Addressables 加载 PlayableAsset 并播放(协程版本,可等待播完)
/// </summary>
public IEnumerator PlayFromAddressableAsync(string addressableKey)
{
yield return PlayFromAddressableCoroutine(addressableKey, null);
}
private IEnumerator PlayFromAddressableCoroutine(string addressableKey, Action onComplete)
{
if (director == null)
{
Debug.LogError($"DirectorHandler {timelineName}: PlayableDirector is null");
yield break;
}
if (string.IsNullOrEmpty(addressableKey))
{
Debug.LogError($"DirectorHandler {timelineName}: addressableKey is null or empty");
yield break;
}
// 播放新 Addressable 前,先释放上一次
ReleaseAddressableHandle();
var loadKey = TimelinePrefix + addressableKey;
PlayableAsset loadedAsset = null;
AsyncOperationHandle<PlayableAsset> loadedHandle = default;
var loadDone = false;
ResourceKit.LoadAssetAsyncWithHandle<PlayableAsset>(loadKey, handle =>
{
loadedHandle = handle;
if (handle.Status == AsyncOperationStatus.Succeeded)
loadedAsset = handle.Result;
loadDone = true;
});
yield return new WaitUntil(() => loadDone);
if (loadedAsset == null)
{
Debug.LogError($"DirectorHandler {timelineName}: Failed to load PlayableAsset '{loadKey}'");
yield break;
}
director.stopped -= OnDirectorStopped;
if (director.state == PlayState.Playing)
{
director.Stop();
}
_savedPlayableAsset = director.playableAsset;
director.playableAsset = loadedAsset;
director.RebuildGraph();
_addressableHandle = loadedHandle;
_onCompleteCallback = onComplete;
if (onComplete != null)
{
director.stopped += OnDirectorStopped;
}
director.Play();
}
/// <summary>
/// 停止播放
/// </summary>
public void Stop()
{
if (director == null) return;
// 先取消事件订阅,避免 Stop() 触发回调
director.stopped -= OnDirectorStopped;
director.Stop();
_onCompleteCallback = null;
ReleaseAddressableHandle();
}
/// <summary>
/// 暂停播放
/// </summary>
public void Pause()
{
if (director == null) return;
if (director.state == PlayState.Playing)
{
director.Pause();
}
}
/// <summary>
/// 恢复播放
/// </summary>
public void Resume()
{
if (director == null) return;
if (director.state == PlayState.Paused)
{
director.Resume();
}
}
/// <summary>
/// 重置 Timeline
/// </summary>
public void Reset()
{
if (director == null) return;
// 先取消事件订阅,避免 Stop() 触发回调
director.stopped -= OnDirectorStopped;
director.Stop();
director.time = 0;
director.Evaluate();
_onCompleteCallback = null;
ReleaseAddressableHandle();
}
/// <summary>
/// 获取当前播放时间
/// </summary>
public double GetPlaybackTime()
{
if (director == null) return 0;
return director.time;
}
/// <summary>
/// 获取总时长
/// </summary>
public double GetDuration()
{
if (director == null) return 0;
return director.duration;
}
/// <summary>
/// 设置播放速度
/// </summary>
public void SetPlaybackSpeed(float speed)
{
if (director == null) return;
if (director.playableGraph.IsValid())
{
director.playableGraph.GetRootPlayable(0).SetSpeed(speed);
}
else
{
Debug.LogWarning($"DirectorHandler {timelineName}: PlayableGraph is not valid. Cannot set playback speed.");
}
}
/// <summary>
/// 跳转到指定时间点
/// </summary>
public void SeekToTime(double time)
{
if (director == null) return;
director.time = Math.Clamp(time, 0, director.duration);
director.Evaluate();
}
/// <summary>
/// Director 停止事件处理
/// </summary>
private void OnDirectorStopped(PlayableDirector director)
{
// 立即取消订阅,避免重复触发
director.stopped -= OnDirectorStopped;
var callback = _onCompleteCallback;
_onCompleteCallback = null;
ReleaseAddressableHandle();
callback?.Invoke();
}
}
}