using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using BITKit.IO; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.SceneManagement; using YooAsset; using Debug = UnityEngine.Debug; using Object = UnityEngine.Object; // ReSharper disable Unity.LoadSceneWrongIndex namespace BITKit.SceneManagement { [Serializable] public class SceneServiceSingleton : SceneServiceImplement { protected override ISceneService _sceneServiceImplementation => SceneService.Singleton; } [Serializable] public class SceneServiceSingletonProxy:ISceneService { private ISceneService _sceneServiceImplementation => SceneService.Singleton; public bool InitializeMainSceneOnLoad=>SceneService.Singleton.InitializeMainSceneOnLoad; public string[] GetScenes(params string[] tags) { return SceneService.Singleton.GetScenes(tags); } public UniTask LoadSceneAsync(string sceneName, CancellationToken cancellationToken, LoadSceneMode loadSceneMode = LoadSceneMode.Additive, bool activateOnLoad = true)=>SceneService.Singleton.LoadSceneAsync(sceneName,cancellationToken,loadSceneMode,activateOnLoad); public UniTask UnloadSceneAsync(string sceneName, CancellationToken cancellationToken) { return _sceneServiceImplementation.UnloadSceneAsync(sceneName, cancellationToken); } public event Action OnLoadScene { add => SceneService.OnLoadScene += value; remove => SceneService.OnLoadScene -= value; } public event Action OnSceneLoadProgress { add => SceneService.OnSceneLoadProgress += value; remove => SceneService.OnSceneLoadProgress -= value; } public event Action OnSceneLoaded { add => SceneService.OnSceneLoaded += value; remove => SceneService.OnSceneLoaded -= value; } public void RegisterLoadTaskAsync(Func task) { _sceneServiceImplementation.RegisterLoadTaskAsync(task); } public void UnRegisterLoadTaskAsync(Func task) { _sceneServiceImplementation.UnRegisterLoadTaskAsync(task); } public event Action OnUnloadScene { add => _sceneServiceImplementation.OnUnloadScene += value; remove => _sceneServiceImplementation.OnUnloadScene -= value; } public event Action OnSceneUnloaded { add => _sceneServiceImplementation.OnSceneUnloaded += value; remove => _sceneServiceImplementation.OnSceneUnloaded -= value; } } public class SceneService : MonoBehaviour,ISceneService { [BITCommand] public static async void Map(string mapName) { await UniTask.SwitchToMainThread(Singleton.destroyCancellationToken); Singleton.LoadSceneAsync(mapName,Singleton.destroyCancellationToken).Forget(); } #if UNITY_EDITOR public static bool AllowInitialize=true; [RuntimeInitializeOnLoadMethod] private static void Initialize() { OnLoadScene =null; OnSceneLoadProgress = null; OnSceneLoaded = null; if (AllowInitialize && SceneManager.sceneCount is not 0) { SceneManager.LoadSceneAsync(0); } } #endif public static event Action OnLoadScene; public static event Action OnSceneLoadProgress; public static event Action OnSceneLoaded; internal static SceneService Singleton; #if UNITY_EDITOR [SerializeField] private Optional allowLoadDelay; #endif [SerializeField] private Optional allowLoadMainScene; [SerializeField] private Optional allowMenuScene; private readonly Dictionary LoadedObjects = new(); private CancellationToken _cancellationToken; private readonly ConcurrentDictionary _packages=new(); private void Awake() { Singleton = this; _cancellationToken = gameObject.GetCancellationTokenOnDestroy(); } private void Start() { if (allowMenuScene.Allow) { YooAssets .LoadSceneAsync(allowMenuScene.Value).ToUniTask(cancellationToken: _cancellationToken) .Forget(); } if (allowLoadMainScene.Allow is false) return; LoadSceneAsync(allowLoadMainScene.Value,_cancellationToken,LoadSceneMode.Single,false).Forget(); } public bool InitializeMainSceneOnLoad => true; public string[] GetScenes(params string[] tags) { try { _packages.Clear(); var list = new List(); foreach (var package in YooAssetUtils.RegisteredResourcePackages) { foreach (var assetInfo in package.GetAssetInfos(tags)) { list.Add(assetInfo.Address); _packages.TryAdd(assetInfo.Address,package); } } return list.ToArray(); } catch (Exception e) { BIT4Log.Warning(JsonHelper.Get(YooAssetUtils.RegisteredPackages) ); throw; } } public async UniTask LoadSceneAsync(string sceneName, CancellationToken cancellationToken, LoadSceneMode loadSceneMode = LoadSceneMode.Additive, bool activateOnLoad = true) { try { var stopwatchWatcher = new Stopwatch(); BIT4Log.Log($"正在加载场景:{sceneName}"); OnLoadScene?.Invoke(sceneName); await Task.Delay(100, destroyCancellationToken); stopwatchWatcher.Start(); #if UNITY_EDITOR if (allowLoadDelay.Allow) { var progress = 0f; while (progress < allowLoadDelay.Value) { OnSceneLoadProgress?.Invoke(sceneName, progress += 1 / allowLoadDelay.Value * Time.deltaTime); cancellationToken.ThrowIfCancellationRequested(); await UniTask.NextFrame(cancellationToken); } } #endif // var asyncOperation = Addressables.LoadSceneAsync(sceneName, loadSceneMode, activateOnLoad); // while (asyncOperation.IsDone is false) // { // await UniTask.NextFrame(cancellationToken); // var progress = asyncOperation.PercentComplete; // OnSceneLoadProgress?.Invoke(sceneName,progress); // } var sceneMode = UnityEngine.SceneManagement.LoadSceneMode.Single; var package = _packages[sceneName]; var handle = package.LoadSceneAsync(sceneName, sceneMode); while (handle.IsDone is false) { var progress = handle.Progress; await UniTask.NextFrame(cancellationToken); OnSceneLoadProgress?.Invoke(sceneName, progress); } LoadedObjects.Add(sceneName, handle.SceneObject); OnSceneLoadProgress?.Invoke(sceneName, 1); await Task.Delay(384, cancellationToken); foreach (var x in _onSceneLoadedAsyncList.ToArray()) { await x.Invoke(); if (destroyCancellationToken.IsCancellationRequested) return; } OnSceneLoaded?.Invoke(sceneName); stopwatchWatcher.Stop(); // if (activateOnLoad is false) // { // asyncOperation.Result.ActivateAsync().ToUniTask(cancellationToken: cancellationToken).Forget(); // _loadedObjects.Add(asyncOperation.Result); // } BIT4Log.Log($"场景:{sceneName}加载完成,耗时:{stopwatchWatcher.ElapsedMilliseconds}ms"); } catch (OperationCanceledException) { } } public async UniTask UnloadSceneAsync(string sceneName, CancellationToken cancellationToken) { await UniTask.SwitchToMainThread(); OnUnloadScene?.Invoke(sceneName); await Task.Delay(100, destroyCancellationToken); await UniTask.SwitchToMainThread(); if (LoadedObjects.TryRemove(sceneName) is false) return; //await SceneManager.UnloadSceneAsync(scene); SceneManager.LoadScene(1); destroyCancellationToken.ThrowIfCancellationRequested(); await UniTask.SwitchToMainThread(); OnSceneUnloaded?.Invoke(sceneName); } event Action ISceneService.OnLoadScene { add=>OnLoadScene+=value; remove=>OnLoadScene-=value; } event Action ISceneService.OnSceneLoadProgress { add=>OnSceneLoadProgress+=value; remove => OnSceneLoadProgress -= value; } event Action ISceneService.OnSceneLoaded { add=>OnSceneLoaded+=value; remove => OnSceneLoaded -= value; } private readonly List> _onSceneLoadedAsyncList=new(); public void RegisterLoadTaskAsync(Func task) { _onSceneLoadedAsyncList.Add(task); } public void UnRegisterLoadTaskAsync(Func task) { _onSceneLoadedAsyncList.Remove(task); } public event Action OnUnloadScene; public event Action OnSceneUnloaded; } }