using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.SceneManagement; 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 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 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 { #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; private CancellationToken _cancellationToken; private readonly List _loadedObjects = new(); private void Awake() { Singleton = this; _cancellationToken = gameObject.GetCancellationTokenOnDestroy(); } private void Start() { if (allowLoadMainScene.Allow is false) return; LoadSceneAsync(allowLoadMainScene.Value,_cancellationToken,LoadSceneMode.Single,false).Forget(); } public bool InitializeMainSceneOnLoad => true; public async UniTask LoadSceneAsync(string sceneName,CancellationToken cancellationToken, LoadSceneMode loadSceneMode = LoadSceneMode.Additive, bool activateOnLoad = true) { try { var stopwatchWatcher = new Stopwatch(); stopwatchWatcher.Start(); BIT4Log.Log($"正在加载场景:{sceneName}"); OnLoadScene?.Invoke(sceneName); #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); } OnSceneLoadProgress?.Invoke(sceneName, 1); await Task.Delay(100, cancellationToken); 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); foreach (var x in _loadedObjects) { await Addressables.UnloadSceneAsync(x); } 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; } public event Action OnUnloadScene; public event Action OnSceneUnloaded; } }