Tz2/Assets/Framework/Scripts/Runtime/Engine/Scene/SceneManager.cs

260 lines
8.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}
}