321 lines
8.9 KiB
C#
321 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;
|
|
_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();
|
|
}
|
|
}
|
|
}
|