From 7a40dcd165af026cd48f16a88c6ba66e2cd70282 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Wed, 25 Mar 2026 14:44:33 +0800 Subject: [PATCH] =?UTF-8?q?fix(scene-mgmt):=20=E4=BF=AE=E5=A4=8D=20Timelin?= =?UTF-8?q?e=20=E6=92=AD=E6=94=BE=E7=BB=93=E6=9D=9F=E5=90=8E=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E5=9B=9E=E5=88=B0=E8=B5=B7=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- .../TimelineKit/DirectorHandler.cs | 90 +++++++++++++++++-- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs b/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs index 3d62415f0..da472dcd1 100644 --- a/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs +++ b/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs @@ -20,9 +20,17 @@ namespace AibisDream [Tooltip("加载新 Timeline 时自动绑定 CinemachineBrain")] [SerializeField] private bool autoBindCinemachineBrain; + [Header("播放结束")] + [Tooltip("勾选:自然播放到结尾(PlayableDirector.stopped)后将时间设回 0;不勾选:停留在最后一帧(默认)")] + [SerializeField] private bool resetToStartWhenFinished; + [Tooltip("当 resetToStartWhenFinished 为真时,回到时间 0 前的等待秒数;0 表示立刻回到起点")] + [Min(0f)] + [SerializeField] private float resetToStartDelay; + private Action _onCompleteCallback; private AsyncOperationHandle _addressableHandle; private PlayableAsset _savedPlayableAsset; + private Coroutine _pendingResetToStartCoroutine; #if UNITY_EDITOR /// @@ -54,6 +62,8 @@ namespace AibisDream { TimelineCenter.Instance.UnregisterDirector(this); + CancelPendingResetToStartCoroutine(); + if (director != null) { director.stopped -= OnDirectorStopped; @@ -62,6 +72,58 @@ namespace AibisDream ReleaseAddressableHandle(); } + /// + /// 是否需要订阅 PlayableDirector.stopped(完成回调或需要播放结束行为时)。 + /// + private bool ShouldSubscribeStopped(Action onComplete) + { + return onComplete != null || resetToStartWhenFinished; + } + + private void CancelPendingResetToStartCoroutine() + { + if (_pendingResetToStartCoroutine == null) return; + StopCoroutine(_pendingResetToStartCoroutine); + _pendingResetToStartCoroutine = null; + } + + /// + /// 将 Director 时间设到 0 并刷新一帧(不 Stop、不释放资源)。 + /// + private void ApplyPlaybackTimeZero() + { + if (director == null) return; + director.time = 0; + director.Evaluate(); + } + + /// + /// 在 Release + 完成回调之后执行:按配置回到起点或延时回到起点。 + /// + private void ApplyEndBehaviorAfterStopped() + { + if (!resetToStartWhenFinished) return; + + CancelPendingResetToStartCoroutine(); + + var delay = Mathf.Max(0f, resetToStartDelay); + if (delay <= 0f) + { + ApplyPlaybackTimeZero(); + } + else + { + _pendingResetToStartCoroutine = StartCoroutine(ResetToStartAfterDelayCoroutine(delay)); + } + } + + private IEnumerator ResetToStartAfterDelayCoroutine(float delaySeconds) + { + yield return new WaitForSeconds(delaySeconds); + _pendingResetToStartCoroutine = null; + ApplyPlaybackTimeZero(); + } + private void ReleaseAddressableHandle() { if (!_addressableHandle.IsValid()) return; @@ -156,6 +218,7 @@ namespace AibisDream } // 先停止再播放 + CancelPendingResetToStartCoroutine(); director.stopped -= OnDirectorStopped; if (director.state == PlayState.Playing) { @@ -164,8 +227,7 @@ namespace AibisDream _onCompleteCallback = onComplete; - // 如果有完成回调,订阅 stopped 事件 - if (onComplete != null) + if (ShouldSubscribeStopped(onComplete)) { director.stopped += OnDirectorStopped; } @@ -187,12 +249,18 @@ namespace AibisDream yield break; } - // 先停止再播放 + CancelPendingResetToStartCoroutine(); + director.stopped -= OnDirectorStopped; if (director.state == PlayState.Playing) { director.Stop(); } + if (ShouldSubscribeStopped(null)) + { + director.stopped += OnDirectorStopped; + } + // 自动绑定 CinemachineBrain AutoBindCinemachineBrain(); @@ -259,6 +327,7 @@ namespace AibisDream yield break; } + CancelPendingResetToStartCoroutine(); director.stopped -= OnDirectorStopped; if (director.state == PlayState.Playing) { @@ -276,7 +345,7 @@ namespace AibisDream _addressableHandle = loadedHandle; _onCompleteCallback = onComplete; - if (onComplete != null) + if (ShouldSubscribeStopped(onComplete)) { director.stopped += OnDirectorStopped; } @@ -297,6 +366,8 @@ namespace AibisDream { if (director == null) return; + CancelPendingResetToStartCoroutine(); + // 先取消事件订阅,避免 Stop() 触发回调 director.stopped -= OnDirectorStopped; @@ -338,12 +409,13 @@ namespace AibisDream { if (director == null) return; + CancelPendingResetToStartCoroutine(); + // 先取消事件订阅,避免 Stop() 触发回调 director.stopped -= OnDirectorStopped; director.Stop(); - director.time = 0; - director.Evaluate(); + ApplyPlaybackTimeZero(); _onCompleteCallback = null; ReleaseAddressableHandle(); @@ -398,16 +470,18 @@ namespace AibisDream /// /// Director 停止事件处理 /// - private void OnDirectorStopped(PlayableDirector director) + private void OnDirectorStopped(PlayableDirector stoppedDirector) { // 立即取消订阅,避免重复触发 - director.stopped -= OnDirectorStopped; + stoppedDirector.stopped -= OnDirectorStopped; var callback = _onCompleteCallback; _onCompleteCallback = null; ReleaseAddressableHandle(); callback?.Invoke(); + + ApplyEndBehaviorAfterStopped(); } } }