260 lines
8.7 KiB
C#
260 lines
8.7 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Threading;
|
||
using Cysharp.Threading.Tasks;
|
||
using MotionFramework;
|
||
using UnityEngine;
|
||
using UnityEngine.SceneManagement;
|
||
|
||
namespace Framework.Scripts.Runtime.Engine.Scene
|
||
{
|
||
/// <summary>
|
||
/// 场景管理器
|
||
/// </summary>
|
||
public class SceneManager : ModuleSingleton<SceneManager>, IModule
|
||
{
|
||
private string _currentSceneName;
|
||
private string _nextSceneName;
|
||
private bool _isLoading;
|
||
private float _loadingProgress;
|
||
private Action<float> _onLoadingProgressCallback;
|
||
private Action _onLoadingCompleteCallback;
|
||
private HashSet<string> _preloadedScenes = new HashSet<string>();
|
||
private CancellationTokenSource _loadingCancellation;
|
||
private bool _isProcessingScene;
|
||
|
||
public string CurrentSceneName => _currentSceneName;
|
||
public bool IsLoading => _isLoading;
|
||
public float LoadingProgress => _loadingProgress;
|
||
|
||
public void OnCreate(object createParam)
|
||
{
|
||
_currentSceneName = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
|
||
_loadingCancellation = new CancellationTokenSource();
|
||
}
|
||
|
||
public void OnUpdate()
|
||
{
|
||
}
|
||
|
||
public void OnDestroy()
|
||
{
|
||
_loadingCancellation?.Cancel();
|
||
_loadingCancellation?.Dispose();
|
||
_loadingCancellation = null;
|
||
}
|
||
|
||
public void OnGUI()
|
||
{
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置加载状态
|
||
/// </summary>
|
||
private void ResetLoadingState()
|
||
{
|
||
_isLoading = false;
|
||
_loadingProgress = 0f;
|
||
_onLoadingProgressCallback = null;
|
||
_onLoadingCompleteCallback = null;
|
||
|
||
// 取消之前的加载操作
|
||
if (_loadingCancellation != null)
|
||
{
|
||
if (!_loadingCancellation.IsCancellationRequested)
|
||
_loadingCancellation.Cancel();
|
||
_loadingCancellation.Dispose();
|
||
}
|
||
_loadingCancellation = new CancellationTokenSource();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载场景并等待初始化完成
|
||
/// </summary>
|
||
/// <param name="sceneName">场景名称</param>
|
||
/// <param name="onSceneInitialized">场景初始化完成后的回调,在这里执行场景加载后的初始化逻辑</param>
|
||
/// <param name="onProgress">加载进度回调</param>
|
||
/// <param name="onComplete">场景加载完成的回调</param>
|
||
public async UniTask LoadSceneAndWaitAsync(
|
||
string sceneName,
|
||
Action<float> onProgress = null,
|
||
Action onComplete = null)
|
||
{
|
||
if (string.IsNullOrEmpty(sceneName))
|
||
{
|
||
Debug.LogError("场景名称不能为空!");
|
||
return;
|
||
}
|
||
|
||
if (_isProcessingScene)
|
||
{
|
||
Debug.LogError("已有场景正在处理中,请等待完成!");
|
||
return;
|
||
}
|
||
|
||
_isProcessingScene = true;
|
||
|
||
try
|
||
{
|
||
// 创建一个TaskCompletionSource来等待场景加载完成
|
||
var tcs = new UniTaskCompletionSource();
|
||
|
||
// 加载场景
|
||
LoadSceneAsync(sceneName, onProgress, () => tcs.TrySetResult());
|
||
|
||
// 等待场景加载完成
|
||
await tcs.Task;
|
||
|
||
// 等待一帧确保场景已经完全准备好
|
||
await UniTask.NextFrame();
|
||
|
||
|
||
onComplete?.Invoke();
|
||
}
|
||
finally
|
||
{
|
||
_isProcessingScene = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 异步加载场景
|
||
/// </summary>
|
||
/// <param name="sceneName">场景名称</param>
|
||
/// <param name="onProgress">加载进度回调</param>
|
||
/// <param name="onComplete">加载完成回调</param>
|
||
public async UniTaskVoid LoadSceneAsync(string sceneName, Action<float> onProgress = null, Action onComplete = null)
|
||
{
|
||
if (string.IsNullOrEmpty(sceneName))
|
||
{
|
||
Debug.LogError("场景名称不能为空!");
|
||
return;
|
||
}
|
||
|
||
// 如果正在加载,先重置状态
|
||
if (_isLoading)
|
||
{
|
||
Debug.LogWarning("上一个场景加载被中断!");
|
||
ResetLoadingState();
|
||
}
|
||
|
||
_nextSceneName = sceneName;
|
||
_onLoadingProgressCallback = onProgress;
|
||
_onLoadingCompleteCallback = onComplete;
|
||
_isLoading = true;
|
||
|
||
try
|
||
{
|
||
await LoadSceneAsyncInternal().AttachExternalCancellation(_loadingCancellation.Token);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
Debug.LogWarning($"场景 {sceneName} 加载已取消");
|
||
ResetLoadingState();
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"加载场景 {sceneName} 失败: {e}");
|
||
ResetLoadingState();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 预加载场景
|
||
/// </summary>
|
||
/// <param name="sceneName">场景名称</param>
|
||
public async UniTask PreloadSceneAsync(string sceneName)
|
||
{
|
||
if (string.IsNullOrEmpty(sceneName))
|
||
{
|
||
Debug.LogError("场景名称不能为空!");
|
||
return;
|
||
}
|
||
|
||
if (_preloadedScenes.Contains(sceneName))
|
||
{
|
||
return;
|
||
}
|
||
|
||
var operation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
|
||
if (operation != null)
|
||
{
|
||
operation.allowSceneActivation = false;
|
||
await operation.ToUniTask(Progress.Create<float>(progress =>
|
||
{
|
||
Debug.Log($"预加载场景 {sceneName}: {progress}");
|
||
}));
|
||
_preloadedScenes.Add(sceneName);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 卸载预加载的场景
|
||
/// </summary>
|
||
/// <param name="sceneName">场景名称</param>
|
||
public async UniTask UnloadPreloadedSceneAsync(string sceneName)
|
||
{
|
||
if (_preloadedScenes.Contains(sceneName))
|
||
{
|
||
await UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync(sceneName).ToUniTask();
|
||
_preloadedScenes.Remove(sceneName);
|
||
}
|
||
}
|
||
|
||
private async UniTask LoadSceneAsyncInternal()
|
||
{
|
||
try
|
||
{
|
||
// 开始加载前等待一帧,让UI有时间更新
|
||
await UniTask.Yield(_loadingCancellation.Token);
|
||
|
||
// 开始异步加载场景
|
||
var operation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(_nextSceneName);
|
||
if (operation == null)
|
||
{
|
||
throw new Exception($"开始加载场景失败: {_nextSceneName}");
|
||
}
|
||
|
||
operation.allowSceneActivation = false;
|
||
|
||
// 等待加载完成
|
||
while (operation.progress < 0.9f)
|
||
{
|
||
_loadingProgress = operation.progress;
|
||
_onLoadingProgressCallback?.Invoke(_loadingProgress);
|
||
await UniTask.Yield(_loadingCancellation.Token);
|
||
}
|
||
|
||
// 当进度达到0.9时,场景已经加载完成,等待激活
|
||
operation.allowSceneActivation = true;
|
||
|
||
// 等待场景完全加载完成
|
||
await operation.ToUniTask(Progress.Create<float>(progress =>
|
||
{
|
||
_loadingProgress = progress;
|
||
_onLoadingProgressCallback?.Invoke(progress);
|
||
}), PlayerLoopTiming.Update, _loadingCancellation.Token);
|
||
|
||
// 确保新场景已经被激活
|
||
await UniTask.WaitUntil(() =>
|
||
UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == _nextSceneName,
|
||
PlayerLoopTiming.Update, _loadingCancellation.Token);
|
||
|
||
// 更新当前场景名称
|
||
_currentSceneName = _nextSceneName;
|
||
_loadingProgress = 1f;
|
||
|
||
// 调用加载完成回调
|
||
_onLoadingProgressCallback?.Invoke(1f);
|
||
_onLoadingCompleteCallback?.Invoke();
|
||
}
|
||
finally
|
||
{
|
||
_isLoading = false;
|
||
// 清理回调
|
||
_onLoadingProgressCallback = null;
|
||
_onLoadingCompleteCallback = null;
|
||
}
|
||
}
|
||
}
|
||
} |