Files
aibis-dream/Assets/Scripts/Game Loop/SceneLoader.cs
T

212 lines
7.0 KiB
C#

using System;
using System.Collections;
using AibisDream.FixSystem;
using AibisDream.Framework;
using AibisDream.Kit;
using AibisDream.UI;
using DG.Tweening;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
namespace AibisDream
{
public sealed class SceneOperationResult
{
public bool Success { get; internal set; }
public string SceneName { get; internal set; }
public string Error { get; internal set; }
}
public class SceneLoader : Singleton<SceneLoader>
{
private AsyncOperationHandle<SceneInstance> _loadHandle;
private string _currentSceneName;
private Scene _currentScene;
private bool _isLoading;
public string CurrentSceneName => _currentSceneName;
public Scene CurrentScene => _currentScene;
public bool IsLoading => _isLoading;
public IEnumerator LoadSceneAsync(
string targetScene,
Action<SceneOperationResult> completed = null)
{
if (_isLoading)
{
completed?.Invoke(Failure(targetScene, "SceneLoader is busy."));
yield break;
}
if (string.IsNullOrWhiteSpace(targetScene))
{
completed?.Invoke(Failure(targetScene, "Scene key is empty."));
yield break;
}
_isLoading = true;
SceneOperationResult result = null;
UIManager.Instance?.ShowPreferredLoading();
try
{
if (_loadHandle.IsValid())
{
SceneOperationResult unloadResult = null;
yield return UnloadCurrentSceneCore(value => unloadResult = value);
if (unloadResult?.Success != true)
{
result = unloadResult ?? Failure(_currentSceneName, "Current scene unload did not return a result.");
yield break;
}
}
ResourceSystem.CreateSceneLoader();
AsyncOperationHandle<SceneInstance> pendingHandle;
try
{
pendingHandle = Addressables.LoadSceneAsync(targetScene, LoadSceneMode.Additive);
}
catch (Exception ex)
{
ResourceSystem.ReleaseSceneLoader();
result = Failure(targetScene, ex.Message);
yield break;
}
while (!pendingHandle.IsDone)
{
UIManager.Instance?.SetPreferredLoadingProgress(pendingHandle.PercentComplete);
yield return null;
}
UIManager.Instance?.SetPreferredLoadingProgress(1f);
if (pendingHandle.Status != AsyncOperationStatus.Succeeded)
{
var error = pendingHandle.OperationException?.Message ?? "Addressables scene load failed.";
if (pendingHandle.IsValid())
{
Addressables.Release(pendingHandle);
}
ResourceSystem.ReleaseSceneLoader();
result = Failure(targetScene, error);
yield break;
}
var loadedScene = pendingHandle.Result.Scene;
if (!loadedScene.IsValid() || !loadedScene.isLoaded)
{
if (pendingHandle.IsValid())
{
yield return Addressables.UnloadSceneAsync(pendingHandle);
}
ResourceSystem.ReleaseSceneLoader();
result = Failure(targetScene, "Addressables returned an invalid SceneInstance.");
yield break;
}
_loadHandle = pendingHandle;
_currentSceneName = targetScene;
_currentScene = loadedScene;
EnumEventSystem.Global.Send(EventEnum.SceneLoad, targetScene);
result = Success(targetScene);
}
finally
{
_isLoading = false;
UIManager.Instance?.HidePreferredLoading();
completed?.Invoke(result ?? Failure(targetScene, "Scene load was interrupted."));
}
}
public IEnumerator UnloadSceneAsync(Action<SceneOperationResult> completed = null)
{
if (_isLoading)
{
completed?.Invoke(Failure(_currentSceneName, "SceneLoader is busy."));
yield break;
}
_isLoading = true;
SceneOperationResult result = null;
try
{
yield return UnloadCurrentSceneCore(value => result = value);
}
finally
{
_isLoading = false;
completed?.Invoke(result ?? Failure(_currentSceneName, "Scene unload was interrupted."));
}
}
private IEnumerator UnloadCurrentSceneCore(Action<SceneOperationResult> completed)
{
if (!_loadHandle.IsValid())
{
_currentSceneName = null;
_currentScene = default;
completed?.Invoke(Success(null));
yield break;
}
DOTween.KillAll();
FixSystemCenter.SystemDic?.Clear();
ResourceSystem.ReleaseSceneLoader();
AsyncOperationHandle<SceneInstance> unloadHandle;
try
{
unloadHandle = Addressables.UnloadSceneAsync(_loadHandle);
}
catch (Exception ex)
{
completed?.Invoke(Failure(_currentSceneName, ex.Message));
yield break;
}
yield return unloadHandle;
if (unloadHandle.Status != AsyncOperationStatus.Succeeded)
{
completed?.Invoke(Failure(
_currentSceneName,
unloadHandle.OperationException?.Message ?? "Addressables scene unload failed."));
yield break;
}
var unloadedSceneName = _currentSceneName;
_loadHandle = default;
_currentSceneName = null;
_currentScene = default;
completed?.Invoke(Success(unloadedSceneName));
}
private static SceneOperationResult Success(string sceneName)
{
return new SceneOperationResult
{
Success = true,
SceneName = sceneName
};
}
private static SceneOperationResult Failure(string sceneName, string error)
{
return new SceneOperationResult
{
Success = false,
SceneName = sceneName,
Error = error
};
}
}
}