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 isCancellationRequested, Action 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 CollectGates(Scene scene) { var gates = new List(); foreach (var root in scene.GetRootGameObjects()) { foreach (var behaviour in root.GetComponentsInChildren(true)) { if (behaviour is ISceneDialogueGate gate) { gates.Add(gate); } } } return gates; } } }