使用说明 · 从零到能写业务

场景系统 · 使用说明

这份文档只回答一个问题:我该怎么用它?(一行切场景、进度可接、切之前按约定清理)

读完你能做到:3 分钟切第一个场景 · 分清异步与同步 · 知道进度为什么不会卡在 90% · 知道切场景时该清什么

〇它是干什么的

先花 30 秒建立直觉,再动手写。

只学 2 个东西就能干活(真的)
await RevScene.LoadAsync("Battle");                          // ① 异步切场景(推荐)
RevScene.OnProgress += p => bar.value = p;                   // ② 进度挂一次,全局生效

更省心的写法是挂事件:OnLoadStart 开加载界面、OnLoaded 关加载界面 —— 业务里一个 await 都不用写。
剩下的(最短展示时长、按分组卸资源、诊断日志……)都是用到再看的增值项。

人话 场景切换 = 你看似只写一行"切到 Battle",其实坑都在暗处:进度卡 90%、小场景闪一下、点两次互相踩、旧场景的东西还留着。

这个模块把这些坑一次填掉:你只管给场景名,进度、并发、清理、失败原因都由它兜住。

它替你解决的问题怎么做的
进度条卡在 90% 不动,然后瞬间跳满关掉 Unity 的自动激活:进度换算成 0~1,到 100% 才放行(见第三节)
小场景加载太快,加载界面"闪一下"传 minSeconds:进度条至少走这么久,走完才允许切
同一个场景被点了两次,互相踩正在切换时,后来的请求直接忽略并告警(不会黑屏 / 回不去)
切换后旧场景的东西还在(池化实例 / 画面残留)默认自动清空对象池;UI / 音效 / 资源分组用 OnLoadStart 一行挂上
场景名写错 → 黑屏,只能猜当场报人话:场景不存在,或没有加进 Build Settings…
它只管"怎么切",不管"场景里放什么" 场景内容(UI、资源、战斗逻辑)不归它管 —— 它只保证:你给场景名,切换这件事就稳了。

一3 分钟跑起来(可粘贴)

不用初始化、不用挂脚本。

// ① 最常用:异步切场景(async 方法里直接 await)
async RevTask GoBattle()
{
    await RevScene.LoadAsync("Battle");
    RevLog.Info("已经在战斗场景了");            // 这一行执行时,新场景已激活
}

// ② 带进度条(回调参数是 0~1,单调不减,可直接喂 Slider)
await RevScene.LoadAsync("Battle", p => loadingBar.value = p);

// ③ 不想 await?挂事件就够了(挂一次,全局生效)
RevScene.OnLoadStart += name => loadingPanel.Open(name);
RevScene.OnProgress  += p    => loadingBar.value = p;
RevScene.OnLoaded    += name => loadingPanel.Close();
RevScene.OnLoadFailed += why  => RevLog.Error(why);
RevScene.LoadAsync("Login").Forget();        // Forget = 明确表示"故意不 await"

// ④ 死亡重开 / 断线重连:重开当前场景
await RevScene.ReloadAsync();

// ⑤ 最后:切场景时收自己的摊子(对象池框架已经帮你清了,这里只管你自己的)
RevScene.OnLoadStart += _ =>
{
    RevUI.ShutdownAll();                                       // 关掉所有界面
    RevSound.StopAll();                                        // 停掉音效
    RevResBootstrap.Instance.Shutdown(RevResGroup.Battle);     // 按业务域卸资源
};
加载界面"至少显示 0.5 秒"这样写

await RevScene.LoadAsync("Battle", p => bar.value = p, minSeconds: 0.5f);
或者全局设一次:RevScene.DefaultMinSeconds = 0.5f;

二"我要做 X" 对照表(全部 API)

左边找需求,右边抄一行。入口统一是 RevScene。

我想…这么写
异步切场景(推荐)await RevScene.LoadAsync("Battle")
不 await(挂事件)RevScene.LoadAsync("Battle").Forget()
带进度await RevScene.LoadAsync("Battle", p => bar.value = p)
加载界面至少显示 0.5sawait RevScene.LoadAsync("Battle", null, minSeconds: 0.5f)
全局统一最短时长RevScene.DefaultMinSeconds = 0.5f
按 buildIndex 切await RevScene.LoadAsync(1)
重开当前场景await RevScene.ReloadAsync()
同步切(会卡帧)RevScene.Load("Login")
当前场景名 / 序号RevScene.CurrentName / RevScene.CurrentIndex
正在切吗 / 进度 / 状态RevScene.IsLoading / RevScene.Progress / RevScene.State
接进度(事件式)RevScene.OnProgress += p => { }
开始 / 完成 / 失败RevScene.OnLoadStart · RevScene.OnLoaded · RevScene.OnLoadFailed
切之前清理业务域RevScene.OnLoadStart += _ => { … }
关掉自动清对象池RevScene.AutoClearPool = false
打开诊断日志RevScene.VerboseLog = true(走 RevLog,tag = Scene)

三三个关键约定(记住这三条就够了)

① 进度永远是 0~1,而且到 100% 才真正切

Unity 的 AsyncOperation.progress 在场景激活前最高只到 0.9 —— 直接喂给进度条就是"卡在 90% 再瞬间跳满"。框架的做法:

于是你拿到的 p / RevScene.Progress 永远可以直接喂 Slider.value,不用自己乘系数。

② 最短展示时长:minSeconds

小场景加载只要几十毫秒,加载界面会"闪一下"。传 minSeconds(或全局 DefaultMinSeconds)后:

③ 切之前清理:框架默认只清对象池,其余你一行挂上

要清什么谁负责
对象池里的实例(旧场景的残留画面 / 占内存)框架默认帮你清(RevScene.AutoClearPool = true)
UI 界面你挂:RevScene.OnLoadStart += _ => RevUI.ShutdownAll();
音效你挂:RevScene.OnLoadStart += _ => RevSound.StopAll();
资源分组 / 业务域你挂:RevScene.OnLoadStart += _ => RevResBootstrap.Instance.Shutdown(RevResGroup.Battle);
你自己挂在事件上的监听你挂:RevScene.OnLoadStart += _ => RevEvent.ClearAll();(或按 owner 摘)
为什么"只清对象池"而不把上面全清了?

因为"哪些该清"是业务决定:有的游戏跨场景保留 BGM、有的保留某个常驻界面。
框架不替你做主,但给了你一个必然被调用的时机(OnLoadStart),一行就能收摊。

四新手最容易踩的 4 个坑

坑正确做法
① 场景没加进 Build Settings,切过去黑屏日志会直接说"没有加进 Build Settings" → 菜单 File / Build Settings 把场景加进 Scenes In Build
② 正在切的时候又调一次,想"催一下"后一次会被忽略并告警(两个加载互相踩 = 黑屏)→ 用 RevScene.OnLoaded 接完成,别靠 IsLoading 轮询
③ 大场景用同步 RevScene.Load同步切换会卡住这一帧(加载多久卡多久)→ 一律 LoadAsync
④ 以为 OnLoaded 是"场景里所有 Awake/Start 都跑完了"它的语义是"新场景已激活"(Unity 的 LoadSceneAsync 完成时机)→ 要等首帧逻辑就用 await RevTaskScheduler.NextFrame()

五出问题怎么查 + 相关文档

3 分钟定位

  1. 打开日志:RevScene.VerboseLog = true → 能看到"准备切换 → 场景名 / 开始异步切换 / 已进入场景 / 已清理对象池 N 个实例"。
  2. 看状态:RevScene.State(Idle / Loading / Done / Failed)+ RevScene.Progress + RevScene.IsLoading。
  3. 看失败原因:RevScene.OnLoadFailed += why => RevLog.Error(why); —— 原因里会写明是"场景不存在"还是"没进 Build Settings"。
  4. 进度不动:正常现象是加载期间停在某个值(比如 40%),那是 Unity 还没到 0.9 平台期;真卡住不动要查是不是同步 IO 阻塞了主线程。

相关文档