Files

116 lines
3.4 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace AibisDream
{
public sealed class SceneReadinessResult
{
public bool Success { get; internal set; }
public bool Cancelled { get; internal set; }
public string Error { get; internal set; }
}
public static class SceneReadiness
{
public const float DefaultTimeoutSeconds = 15f;
public static IEnumerator WaitUntilReady(
Scene scene,
Func<bool> isCancellationRequested,
Action<SceneReadinessResult> completed,
float timeoutSeconds = DefaultTimeoutSeconds)
{
var result = new SceneReadinessResult();
// 等待场景内 Awake/Start 完成,再收集当前场景自己的 Gate。
yield return null;
if (isCancellationRequested?.Invoke() == true)
{
result.Cancelled = true;
result.Error = "Scene readiness was cancelled.";
completed?.Invoke(result);
yield break;
}
if (!scene.IsValid() || !scene.isLoaded)
{
result.Error = "Loaded scene is invalid or not loaded.";
completed?.Invoke(result);
yield break;
}
var gates = CollectGates(scene);
if (gates.Count == 0)
{
result.Success = true;
completed?.Invoke(result);
yield break;
}
var deadline = Time.unscaledTime + Mathf.Max(0f, timeoutSeconds);
while (true)
{
if (isCancellationRequested?.Invoke() == true)
{
result.Cancelled = true;
result.Error = "Scene readiness was cancelled.";
break;
}
var allReady = true;
foreach (var gate in gates)
{
if (gate is UnityEngine.Object unityObject && unityObject == null)
{
result.Error = "A scene dialogue gate was destroyed while waiting.";
completed?.Invoke(result);
yield break;
}
if (!gate.IsDialogueReady)
{
allReady = false;
}
}
if (allReady)
{
result.Success = true;
break;
}
if (Time.unscaledTime >= deadline)
{
result.Error = $"Scene readiness timed out after {timeoutSeconds:0.#} seconds.";
break;
}
yield return null;
}
completed?.Invoke(result);
}
private static List<ISceneDialogueGate> CollectGates(Scene scene)
{
var gates = new List<ISceneDialogueGate>();
foreach (var root in scene.GetRootGameObjects())
{
foreach (var behaviour in root.GetComponentsInChildren<MonoBehaviour>(true))
{
if (behaviour is ISceneDialogueGate gate)
{
gates.Add(gate);
}
}
}
return gates;
}
}
}