BITKit/Src/Unity/Scripts/Scenes/SceneService.cs

295 lines
8.5 KiB
C#

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<string> OnLoadScene
{
add => SceneService.OnLoadScene += value;
remove => SceneService.OnLoadScene -= value;
}
public event Action<string, float> OnSceneLoadProgress
{
add => SceneService.OnSceneLoadProgress += value;
remove => SceneService.OnSceneLoadProgress -= value;
}
public event Action<string> OnSceneLoaded
{
add => SceneService.OnSceneLoaded += value;
remove => SceneService.OnSceneLoaded -= value;
}
public void RegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.RegisterLoadTaskAsync(task);
}
public void UnRegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.UnRegisterLoadTaskAsync(task);
}
public event Action<string> OnUnloadScene
{
add => _sceneServiceImplementation.OnUnloadScene += value;
remove => _sceneServiceImplementation.OnUnloadScene -= value;
}
public event Action<string> OnSceneUnloaded
{
add => _sceneServiceImplementation.OnSceneUnloaded += value;
remove => _sceneServiceImplementation.OnSceneUnloaded -= value;
}
}
public class SceneService : MonoBehaviour,ISceneService
{
#if UNITY_EDITOR
[UnityEditor.MenuItem("Tools/Scenes/Toggle Scene Service Once")]
public static void ToggleSceneServiceOnce()
{
AllowInitialize = !AllowInitialize;
Debug.Log("允许初始化场景设置为:"+AllowInitialize);
}
#endif
[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<string> OnLoadScene;
public static event Action<string, float> OnSceneLoadProgress;
public static event Action<string> OnSceneLoaded;
internal static SceneService Singleton;
#if UNITY_EDITOR
[SerializeField] private Optional<float> allowLoadDelay;
#endif
[SerializeField] private Optional<string> allowLoadMainScene;
[SerializeField] private Optional<string> allowMenuScene;
private readonly Dictionary<string, Scene> LoadedObjects = new();
private CancellationToken _cancellationToken;
private readonly ConcurrentDictionary<string,ResourcePackage> _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<string>();
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<ISceneService>(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<SceneService>($"正在加载场景:{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<SceneService>($"场景:{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<string> ISceneService.OnLoadScene
{
add=>OnLoadScene+=value;
remove=>OnLoadScene-=value;
}
event Action<string, float> ISceneService.OnSceneLoadProgress
{
add=>OnSceneLoadProgress+=value;
remove => OnSceneLoadProgress -= value;
}
event Action<string> ISceneService.OnSceneLoaded
{
add=>OnSceneLoaded+=value;
remove => OnSceneLoaded -= value;
}
private readonly List<Func<UniTask>> _onSceneLoadedAsyncList=new();
public void RegisterLoadTaskAsync(Func<UniTask> task)
{
_onSceneLoadedAsyncList.Add(task);
}
public void UnRegisterLoadTaskAsync(Func<UniTask> task)
{
_onSceneLoadedAsyncList.Remove(task);
}
public event Action<string> OnUnloadScene;
public event Action<string> OnSceneUnloaded;
}
}