diff --git a/Assets/AddressableAssetsData/AssetGroups/ScriptalObjects.asset b/Assets/AddressableAssetsData/AssetGroups/ScriptalObjects.asset index 24e7c7f71..de53018c8 100644 --- a/Assets/AddressableAssetsData/AssetGroups/ScriptalObjects.asset +++ b/Assets/AddressableAssetsData/AssetGroups/ScriptalObjects.asset @@ -33,11 +33,6 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 - - m_GUID: 9a92289d23953c445ba2d385f1698f30 - m_Address: BlockPuzzleValidtor - m_ReadOnly: 0 - m_SerializedLabels: [] - FlaggedDuringContentUpdateRestriction: 0 m_ReadOnly: 0 m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2} m_SchemaSet: diff --git a/Assets/AddressableAssetsData/AssetGroups/石头维修相关资源.asset b/Assets/AddressableAssetsData/AssetGroups/石头维修相关资源.asset index 06edba259..84d19f384 100644 --- a/Assets/AddressableAssetsData/AssetGroups/石头维修相关资源.asset +++ b/Assets/AddressableAssetsData/AssetGroups/石头维修相关资源.asset @@ -23,7 +23,7 @@ MonoBehaviour: m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 3bd393f446c1ad24bb37d23570d508fd - m_Address: "Timeline/\u5361\u6263\u89E3\u9501" + m_Address: "Timeline/\u7EF4\u4FEE\u9762\u677F/\u5361\u6263\u89E3\u9501" m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 @@ -43,7 +43,12 @@ MonoBehaviour: m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 03e5e9a1437535042ae48b2ba034dd93 - m_Address: BlockShape + m_Address: BlockPuzzle/BlockShape + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 9a92289d23953c445ba2d385f1698f30 + m_Address: BlockPuzzleValidtor m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 diff --git a/Assets/Scenes/网格测试.unity b/Assets/Scenes/网格测试.unity index 8bf882013..b30b90b08 100644 --- a/Assets/Scenes/网格测试.unity +++ b/Assets/Scenes/网格测试.unity @@ -5353,7 +5353,15 @@ MonoBehaviour: m_EditorClassIdentifier: timelineName: "\u7EF4\u4FEE\u9762\u677F" director: {fileID: 1492619800} - timelineList: [] + timelineList: + - m_AssetGUID: a541678fe3c38c645b11bb878371e602 + m_SubObjectName: + m_SubObjectType: + m_EditorAssetChanged: 0 + - m_AssetGUID: 3bd393f446c1ad24bb37d23570d508fd + m_SubObjectName: + m_SubObjectType: + m_EditorAssetChanged: 0 --- !u!1 &1532984430 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Framework/ResourceKit/ResourceKit.cs b/Assets/Scripts/Framework/ResourceKit/ResourceKit.cs index 01f27ab84..3da8bec7a 100644 --- a/Assets/Scripts/Framework/ResourceKit/ResourceKit.cs +++ b/Assets/Scripts/Framework/ResourceKit/ResourceKit.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using UnityEngine; using UnityEngine.AddressableAssets; @@ -94,6 +94,31 @@ namespace AibisDream.Framework }; } + /// + /// 异步加载 Addressable 资源,callback 接收完整 handle。调用方需在适当时机调用 Release 释放。 + /// + public static void LoadAssetAsyncWithHandle(string key, Action> callback) where T : UnityEngine.Object + { + var handle = Addressables.LoadAssetAsync(key); + handle.Completed += operation => + { + if (operation.Status != AsyncOperationStatus.Succeeded) + { + Debug.Log($"资源{key}不存在"); + } + callback(operation); + }; + } + + /// + /// 释放 Addressable 加载的 handle + /// + public static void Release(AsyncOperationHandle handle) + { + if (handle.IsValid()) + Addressables.Release(handle); + } + #if UNITY_EDITOR public static bool GetAssetAddressableKey(T asset, out string addressableKey) where T : UnityEngine.Object { diff --git a/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs b/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs index 962fcda19..423dfed25 100644 --- a/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs +++ b/Assets/Scripts/SceneManagement/TimelineKit/DirectorHandler.cs @@ -1,21 +1,22 @@ using System; using System.Collections; -using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; -using UnityEngine.AddressableAssets; +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; - [Header("多条Timeline")] - [SerializeField] private AssetReference[] timelineList; - - private Dictionary _timelineDict = new(); + private Action _onCompleteCallback; + private AsyncOperationHandle _addressableHandle; + private PlayableAsset _savedPlayableAsset; #if UNITY_EDITOR /// @@ -36,9 +37,6 @@ namespace AibisDream { director = GetComponent(); } - - // timelineList转字典 - } private void Start() @@ -54,6 +52,21 @@ namespace AibisDream { 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; } /// @@ -103,7 +116,7 @@ namespace AibisDream } director.Play(); - + // 等待播放完成 while (director.state == PlayState.Playing) { @@ -111,18 +124,92 @@ namespace AibisDream } } + /// + /// 从 Addressables 加载 PlayableAsset 并播放(加载时自动加 "Timeline/" 前缀) + /// + public void PlayFromAddressable(string addressableKey, Action onComplete = null) + { + StartCoroutine(PlayFromAddressableCoroutine(addressableKey, onComplete)); + } + + /// + /// 从 Addressables 加载 PlayableAsset 并播放(协程版本,可等待播完) + /// + 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 loadedHandle = default; + var loadDone = false; + + ResourceKit.LoadAssetAsyncWithHandle(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(); + } + /// /// 停止播放 /// public void Stop() { if (director == null) return; - + // 先取消事件订阅,避免 Stop() 触发回调 director.stopped -= OnDirectorStopped; - + director.Stop(); _onCompleteCallback = null; + ReleaseAddressableHandle(); } /// @@ -157,15 +244,16 @@ namespace AibisDream public void Reset() { if (director == null) return; - + // 先取消事件订阅,避免 Stop() 触发回调 director.stopped -= OnDirectorStopped; - + director.Stop(); director.time = 0; director.Evaluate(); - + _onCompleteCallback = null; + ReleaseAddressableHandle(); } /// @@ -221,10 +309,11 @@ namespace AibisDream { // 立即取消订阅,避免重复触发 director.stopped -= OnDirectorStopped; - + var callback = _onCompleteCallback; _onCompleteCallback = null; - + + ReleaseAddressableHandle(); callback?.Invoke(); } diff --git a/Assets/Scripts/SceneManagement/TimelineKit/TimelineCenter.cs b/Assets/Scripts/SceneManagement/TimelineKit/TimelineCenter.cs index b118c3eec..24de6c6c7 100644 --- a/Assets/Scripts/SceneManagement/TimelineKit/TimelineCenter.cs +++ b/Assets/Scripts/SceneManagement/TimelineKit/TimelineCenter.cs @@ -34,84 +34,121 @@ namespace AibisDream _timelineDict.Remove(directorHandler.timelineName); } + /// + /// 解析 timelineName,返回 (directorName, addressableKey 或 null) + /// 格式:仅 DirectorName 或 DirectorName/AddressableKey + /// + private static (string directorName, string addressableKey) ParseTimelineName(string timelineName) + { + var idx = timelineName.IndexOf('/'); + if (idx < 0) + return (timelineName, null); + return (timelineName.Substring(0, idx), timelineName.Substring(idx + 1)); + } + // -------------------------- 播放控制 -------------------------- /// - /// 播放 Timeline(带完成回调的重载) + /// 播放 Timeline。timelineName 支持两种格式:仅 DirectorName(播放自带)或 DirectorName/AddressableKey(从 Addressables 加载播放) /// - public void PlayTimeline(string timelineName, Action onComplete = null) + public void PlayTimeline(string timelineName) { - if (!_timelineDict.TryGetValue(timelineName, out var directorHandler)) + var (directorName, addressableKey) = ParseTimelineName(timelineName); + + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {timelineName} not found"); + Debug.LogError($"Timeline {timelineName} not found (directorName: {directorName})"); return; } - + directorHandler.gameObject.SetActive(true); - directorHandler.Play(onComplete); + + if (addressableKey == null) + { + directorHandler.Play(); + } + else + { + directorHandler.PlayFromAddressable(addressableKey); + } } public IEnumerator PlayTimelineAsync(string name) { - if (!_timelineDict.TryGetValue(name, out var directorHandler)) + var (directorName, addressableKey) = ParseTimelineName(name); + + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {name} not found"); + Debug.LogError($"Timeline {name} not found (directorName: {directorName})"); yield break; } directorHandler.gameObject.SetActive(true); - yield return directorHandler.PlayAsync(); + + if (addressableKey == null) + { + yield return directorHandler.PlayAsync(); + } + else + { + yield return directorHandler.PlayFromAddressableAsync(addressableKey); + } } public void StopTimeline(string timelineName) { - if (!_timelineDict.TryGetValue(timelineName, out var directorHandler)) + var (directorName, _) = ParseTimelineName(timelineName); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {timelineName} not found"); + Debug.LogError($"Timeline {timelineName} not found (directorName: {directorName})"); return; } - + directorHandler.Stop(); } public void PauseTimeline(string timelineName) { - if (!_timelineDict.TryGetValue(timelineName, out var directorHandler)) + var (directorName, _) = ParseTimelineName(timelineName); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {timelineName} not found"); + Debug.LogError($"Timeline {timelineName} not found (directorName: {directorName})"); return; } - + directorHandler.Pause(); } public void ResumeTimeline(string timelineName) { - if (!_timelineDict.TryGetValue(timelineName, out var directorHandler)) + var (directorName, _) = ParseTimelineName(timelineName); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {timelineName} not found"); + Debug.LogError($"Timeline {timelineName} not found (directorName: {directorName})"); return; } - + directorHandler.Resume(); } public void ResetTimeline(string timelineName) { - if (!_timelineDict.TryGetValue(timelineName, out var directorHandler)) + var (directorName, _) = ParseTimelineName(timelineName); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {timelineName} not found"); + Debug.LogError($"Timeline {timelineName} not found (directorName: {directorName})"); return; } directorHandler.Reset(); } - + public void HideTimeline(string timelineName) { - if (!_timelineDict.TryGetValue(timelineName, out var directorHandler)) + var (directorName, _) = ParseTimelineName(timelineName); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {timelineName} not found"); + Debug.LogError($"Timeline {timelineName} not found (directorName: {directorName})"); return; } @@ -122,23 +159,25 @@ namespace AibisDream public double GetPlaybackTime(string name) { - if (!_timelineDict.TryGetValue(name, out var directorHandler)) + var (directorName, _) = ParseTimelineName(name); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {name} not found"); + Debug.LogError($"Timeline {name} not found (directorName: {directorName})"); return 0; } - + return directorHandler.GetPlaybackTime(); } public double GetDuration(string name) { - if (!_timelineDict.TryGetValue(name, out var directorHandler)) + var (directorName, _) = ParseTimelineName(name); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {name} not found"); + Debug.LogError($"Timeline {name} not found (directorName: {directorName})"); return 0; } - + return directorHandler.GetDuration(); } @@ -148,23 +187,25 @@ namespace AibisDream public void SetPlaybackSpeed(string name, float speed) { - if (!_timelineDict.TryGetValue(name, out var directorHandler)) + var (directorName, _) = ParseTimelineName(name); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {name} not found"); + Debug.LogError($"Timeline {name} not found (directorName: {directorName})"); return; } - + directorHandler.SetPlaybackSpeed(speed); } public void SeekToTime(string name, double time) { - if (!_timelineDict.TryGetValue(name, out var directorHandler)) + var (directorName, _) = ParseTimelineName(name); + if (!_timelineDict.TryGetValue(directorName, out var directorHandler)) { - Debug.LogError($"Timeline {name} not found"); + Debug.LogError($"Timeline {name} not found (directorName: {directorName})"); return; } - + directorHandler.SeekToTime(time); } @@ -203,7 +244,7 @@ namespace AibisDream public interface ITimelineCenter { - void PlayTimeline(string timelineName, Action onComplete = null); + void PlayTimeline(string timelineName); IEnumerator PlayTimelineAsync(string name); void StopTimeline(string timelineName); void PauseTimeline(string timelineName);