Implement SceneManager with asynchronous load/unload routines ready for bootstrapper scene. Implement simple level switching
This commit is contained in:
183
Assets/Scripts/SceneManagerService.cs
Normal file
183
Assets/Scripts/SceneManagerService.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class SceneManagerService : MonoBehaviour
|
||||
{
|
||||
public static SceneManagerService Instance { get; private set; }
|
||||
|
||||
// Events for scene lifecycle
|
||||
public event Action<string> SceneLoadStarted;
|
||||
public event Action<string, float> SceneLoadProgress;
|
||||
public event Action<string> SceneLoadCompleted;
|
||||
public event Action<string> SceneUnloadStarted;
|
||||
public event Action<string, float> SceneUnloadProgress;
|
||||
public event Action<string> SceneUnloadCompleted;
|
||||
|
||||
private readonly Dictionary<string, AsyncOperation> _activeLoads = new();
|
||||
private readonly Dictionary<string, AsyncOperation> _activeUnloads = new();
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
// Load a single scene asynchronously (additive)
|
||||
public async Task LoadSceneAsync(string sceneName, IProgress<float> progress = null)
|
||||
{
|
||||
SceneLoadStarted?.Invoke(sceneName);
|
||||
var op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
|
||||
_activeLoads[sceneName] = op;
|
||||
while (!op.isDone)
|
||||
{
|
||||
progress?.Report(op.progress);
|
||||
SceneLoadProgress?.Invoke(sceneName, op.progress);
|
||||
await Task.Yield();
|
||||
}
|
||||
_activeLoads.Remove(sceneName);
|
||||
SceneLoadCompleted?.Invoke(sceneName);
|
||||
}
|
||||
|
||||
// Unload a single scene asynchronously
|
||||
public async Task UnloadSceneAsync(string sceneName, IProgress<float> progress = null)
|
||||
{
|
||||
var scene = SceneManager.GetSceneByName(sceneName);
|
||||
if (!scene.isLoaded)
|
||||
{
|
||||
Debug.LogWarning($"SceneManagerService: Attempted to unload scene '{sceneName}', but it is not loaded.");
|
||||
return;
|
||||
}
|
||||
SceneUnloadStarted?.Invoke(sceneName);
|
||||
var op = SceneManager.UnloadSceneAsync(sceneName);
|
||||
_activeUnloads[sceneName] = op;
|
||||
while (!op.isDone)
|
||||
{
|
||||
progress?.Report(op.progress);
|
||||
SceneUnloadProgress?.Invoke(sceneName, op.progress);
|
||||
await Task.Yield();
|
||||
}
|
||||
_activeUnloads.Remove(sceneName);
|
||||
SceneUnloadCompleted?.Invoke(sceneName);
|
||||
}
|
||||
|
||||
// Load multiple scenes asynchronously
|
||||
public async Task LoadScenesAsync(IEnumerable<string> sceneNames, IProgress<float> progress = null)
|
||||
{
|
||||
int total = 0;
|
||||
int done = 0;
|
||||
var ops = new List<AsyncOperation>();
|
||||
foreach (var name in sceneNames)
|
||||
{
|
||||
total++;
|
||||
var op = SceneManager.LoadSceneAsync(name, LoadSceneMode.Additive);
|
||||
_activeLoads[name] = op;
|
||||
ops.Add(op);
|
||||
SceneLoadStarted?.Invoke(name);
|
||||
}
|
||||
while (done < total)
|
||||
{
|
||||
done = 0;
|
||||
float aggregate = 0f;
|
||||
foreach (var op in ops)
|
||||
{
|
||||
aggregate += op.progress;
|
||||
if (op.isDone) done++;
|
||||
}
|
||||
float avg = aggregate / total;
|
||||
progress?.Report(avg);
|
||||
foreach (var name in sceneNames)
|
||||
SceneLoadProgress?.Invoke(name, avg);
|
||||
await Task.Yield();
|
||||
}
|
||||
foreach (var name in sceneNames)
|
||||
{
|
||||
_activeLoads.Remove(name);
|
||||
SceneLoadCompleted?.Invoke(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Unload multiple scenes asynchronously
|
||||
public async Task UnloadScenesAsync(IEnumerable<string> sceneNames, IProgress<float> progress = null)
|
||||
{
|
||||
int total = 0;
|
||||
int done = 0;
|
||||
var ops = new List<AsyncOperation>();
|
||||
foreach (var name in sceneNames)
|
||||
{
|
||||
total++;
|
||||
var op = SceneManager.UnloadSceneAsync(name);
|
||||
_activeUnloads[name] = op;
|
||||
ops.Add(op);
|
||||
SceneUnloadStarted?.Invoke(name);
|
||||
}
|
||||
while (done < total)
|
||||
{
|
||||
done = 0;
|
||||
float aggregate = 0f;
|
||||
foreach (var op in ops)
|
||||
{
|
||||
aggregate += op.progress;
|
||||
if (op.isDone) done++;
|
||||
}
|
||||
float avg = aggregate / total;
|
||||
progress?.Report(avg);
|
||||
foreach (var name in sceneNames)
|
||||
SceneUnloadProgress?.Invoke(name, avg);
|
||||
await Task.Yield();
|
||||
}
|
||||
foreach (var name in sceneNames)
|
||||
{
|
||||
_activeUnloads.Remove(name);
|
||||
SceneUnloadCompleted?.Invoke(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally: expose current progress for all active operations
|
||||
public float GetAggregateLoadProgress()
|
||||
{
|
||||
if (_activeLoads.Count == 0) return 1f;
|
||||
float sum = 0f;
|
||||
foreach (var op in _activeLoads.Values) sum += op.progress;
|
||||
return sum / _activeLoads.Count;
|
||||
}
|
||||
public float GetAggregateUnloadProgress()
|
||||
{
|
||||
if (_activeUnloads.Count == 0) return 1f;
|
||||
float sum = 0f;
|
||||
foreach (var op in _activeUnloads.Values) sum += op.progress;
|
||||
return sum / _activeUnloads.Count;
|
||||
}
|
||||
|
||||
// Tracks the currently loaded gameplay scene (not persistent/bootstrapper)
|
||||
public string CurrentGameplayScene { get; private set; } = "AppleHillsOverworld";
|
||||
|
||||
// Switches from current gameplay scene to a new one
|
||||
public async Task SwitchSceneAsync(string newSceneName, IProgress<float> progress = null)
|
||||
{
|
||||
// Load new scene
|
||||
await LoadSceneAsync(newSceneName, progress);
|
||||
// Unload previous scene (if not same)
|
||||
if (!string.IsNullOrEmpty(CurrentGameplayScene) && CurrentGameplayScene != newSceneName)
|
||||
{
|
||||
var prevScene = SceneManager.GetSceneByName(CurrentGameplayScene);
|
||||
if (prevScene.isLoaded)
|
||||
{
|
||||
await UnloadSceneAsync(CurrentGameplayScene);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"SceneManagerService: Previous scene '{CurrentGameplayScene}' is not loaded, skipping unload.");
|
||||
}
|
||||
}
|
||||
// Update tracker
|
||||
CurrentGameplayScene = newSceneName;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user