using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
namespace BaseGames.World.Streaming
{
///
/// 单个房间的流式加载状态包装器。
///
/// 由 创建和管理,封装以下职责:
///
/// - Addressables 加载 / 卸载句柄
/// - Dormantize:批量关闭 Renderer、暂停 IRoomLifecycle(AI、音效等)
/// - Activate:批量恢复渲染,分帧唤醒 IRoomLifecycle
/// - Deactivate:玩家离开后关闭本房间内容
///
///
///
public class RoomHandle
{
// ── 公开属性 ──────────────────────────────────────────────────────────────
public string RoomId { get; }
public RoomState State { get; private set; } = RoomState.Unloaded;
public int EstimatedMemKB { get; set; }
/// 上次处于 Active 状态的时间(Time.time)。用于 LRU 卸载优先级计算。
public float LastActiveTime { get; private set; }
/// 加载完成后由 RoomController.Start() 通过 IRoomStreamingManager.RegisterRoomController 注入。
public RoomController RoomController { get; set; }
// ── 私有字段 ──────────────────────────────────────────────────────────────
private AsyncOperationHandle _sceneHandle;
// 缓存的场景组件集合,Dormantize 后可快速 Activate
private Renderer[] _renderers;
private IRoomLifecycle[] _lifecycles;
private AudioSource[] _audioSources;
private Light[] _lights;
private ParticleSystem[] _particleSystems;
// 分帧激活的协程宿主(由 RoomStreamingManager 提供)
private MonoBehaviour _coroutineRunner;
private int _lifecycleActivatePerFrame;
// ── 构造 ──────────────────────────────────────────────────────────────────
public RoomHandle(string roomId, MonoBehaviour coroutineRunner, int lifecycleActivatePerFrame)
{
RoomId = roomId;
_coroutineRunner = coroutineRunner;
_lifecycleActivatePerFrame = lifecycleActivatePerFrame;
}
// ── 加载 / 卸载 ───────────────────────────────────────────────────────────
///
/// 开始后台异步加载(Additive)。
/// 加载完成后自动调用 将房间置于休眠状态。
///
public IEnumerator LoadAsync(string addressableKey)
{
if (State != RoomState.Unloaded)
{
Debug.LogWarning($"[RoomHandle] {RoomId} 非 Unloaded 状态,忽略重复加载请求。");
yield break;
}
State = RoomState.Loading;
_sceneHandle = Addressables.LoadSceneAsync(addressableKey, LoadSceneMode.Additive);
while (!_sceneHandle.IsDone)
yield return null;
if (_sceneHandle.Status != AsyncOperationStatus.Succeeded)
{
Debug.LogError($"[RoomHandle] {RoomId} 加载失败:{addressableKey}");
State = RoomState.Unloaded;
yield break;
}
// 收集场景内组件引用,然后置于休眠
CollectSceneComponents();
Dormantize();
}
/// 异步卸载本房间。完成后 State → Unloaded。
public IEnumerator UnloadAsync()
{
if (State == RoomState.Unloaded || State == RoomState.Unloading || !_sceneHandle.IsValid()) yield break;
// Active 状态先走 Deactivate,再设置 Unloading
bool needsDeactivate = State == RoomState.Active ||
State == RoomState.Activating ||
State == RoomState.Cooling;
if (needsDeactivate)
Deactivate();
State = RoomState.Unloading;
var op = Addressables.UnloadSceneAsync(_sceneHandle);
yield return op;
_renderers = null;
_lifecycles = null;
_audioSources = null;
_lights = null;
_particleSystems = null;
RoomController = null;
State = RoomState.Unloaded;
}
// ── Dormantize ────────────────────────────────────────────────────────────
///
/// 将已加载的房间置于休眠状态。
/// 关闭所有 Renderer 和 AudioSource,通知所有 IRoomLifecycle 组件进入休眠。
/// 场景内 GameObject 保持激活(便于快速恢复),但不消耗渲染和 AI 开销。
///
public void Dormantize()
{
// 允许从 Loading(初次加载完成后)、Active、Activating、Cooling 进入
if (State != RoomState.Loading &&
State != RoomState.Active &&
State != RoomState.Activating &&
State != RoomState.Cooling)
{
Debug.LogWarning($"[RoomHandle] {RoomId} 在 {State} 状态下调用 Dormantize,忽略。");
return;
}
if (_renderers != null)
foreach (var r in _renderers) if (r != null) r.enabled = false;
if (_audioSources != null)
foreach (var a in _audioSources) if (a != null) { a.Stop(); a.enabled = false; }
if (_lights != null)
foreach (var l in _lights) if (l != null) l.enabled = false;
if (_particleSystems != null)
foreach (var ps in _particleSystems) if (ps != null) ps.Pause();
if (_lifecycles != null)
foreach (var l in _lifecycles) l?.OnRoomDormant();
State = RoomState.Dormant;
}
// ── Activate ──────────────────────────────────────────────────────────────
///
/// 激活此房间(Dormant → Active)。
/// 恢复 Renderer、AudioSource,分帧唤醒 IRoomLifecycle,并设置玩家出生点。
/// 目标房间必须已处于 状态。
///
public IEnumerator Activate(SpawnContext context)
{
if (State != RoomState.Dormant)
{
Debug.LogError($"[RoomHandle] {RoomId} 不是 Dormant 状态(当前:{State}),无法激活。");
yield break;
}
State = RoomState.Activating;
// 先恢复渲染(让玩家立刻能看到新房间)
if (_renderers != null)
foreach (var r in _renderers) if (r != null) r.enabled = true;
if (_audioSources != null)
foreach (var a in _audioSources) if (a != null) a.enabled = true;
if (_lights != null)
foreach (var l in _lights) if (l != null) l.enabled = true;
if (_particleSystems != null)
foreach (var ps in _particleSystems) if (ps != null) ps.Play();
// 相机 + 出生点(由 RoomController 处理)
RoomController?.OnRoomActivate(context);
// 分帧激活 IRoomLifecycle(AI、特效等)
yield return _coroutineRunner.StartCoroutine(ActivateLifecyclesGradually(context));
State = RoomState.Active;
LastActiveTime = Time.time;
}
/// 将 IRoomLifecycle 组件分帧激活,避免单帧 CPU 峰值。
private IEnumerator ActivateLifecyclesGradually(SpawnContext context)
{
if (_lifecycles == null) yield break;
int activatedThisFrame = 0;
foreach (var lc in _lifecycles)
{
if (lc == null) continue;
lc.OnRoomActivate(context);
activatedThisFrame++;
if (activatedThisFrame >= _lifecycleActivatePerFrame)
{
activatedThisFrame = 0;
yield return null;
}
}
}
// ── Deactivate ────────────────────────────────────────────────────────────
///
/// 玩家离开后关闭此房间(Active / Activating → Dormant)。
/// 与 相同,但语义上表示从 Active 退出。
///
public void Deactivate()
{
if (State != RoomState.Active && State != RoomState.Activating && State != RoomState.Cooling)
{
Debug.LogWarning($"[RoomHandle] {RoomId} 在 {State} 状态下调用 Deactivate,忽略。");
return;
}
Dormantize();
}
// ── 冷却 ──────────────────────────────────────────────────────────────────
///
/// 标记为冷却中,同时休眠渲染和 AI 开销。
/// 玩家离开后不立即卸载,等待冷却计时结束后再卸载。
///
public void BeginCooling()
{
if (State != RoomState.Active && State != RoomState.Activating) return;
// 先休眠渲染和 AI(与 Dormantize 相同,但最终 State 设为 Cooling 而非 Dormant)
Dormantize();
State = RoomState.Cooling;
}
///
/// 冷却期间玩家返回,将状态从 Cooling 重置为 Dormant。
/// 渲染已在 BeginCooling 时关闭,无需重复操作。
///
public void ResetToDormant()
{
if (State == RoomState.Cooling)
State = RoomState.Dormant;
}
// ── 内部工具 ──────────────────────────────────────────────────────────────
///
/// 收集加载完成的场景中所有需要被管理的组件引用。
/// 在 LoadAsync 完成后、Dormantize 之前调用一次。
///
private void CollectSceneComponents()
{
if (!_sceneHandle.IsValid()) return;
Scene scene = _sceneHandle.Result.Scene;
GameObject[] roots = scene.GetRootGameObjects();
var rendererList = new List();
var lifecycleList = new List();
var audioSourceList = new List();
var lightList = new List();
var particleList = new List();
foreach (var root in roots)
{
rendererList.AddRange(root.GetComponentsInChildren(true));
lifecycleList.AddRange(root.GetComponentsInChildren(true));
audioSourceList.AddRange(root.GetComponentsInChildren(true));
lightList.AddRange(root.GetComponentsInChildren(true));
particleList.AddRange(root.GetComponentsInChildren(true));
}
_renderers = rendererList.ToArray();
_lifecycles = lifecycleList.ToArray();
_audioSources = audioSourceList.ToArray();
_lights = lightList.ToArray();
_particleSystems = particleList.ToArray();
}
public override string ToString() => $"RoomHandle[{RoomId}, {State}]";
}
}