〇它是干什么的
先花 30 秒建立直觉,再动手写。
await RevScene.LoadAsync("Battle"); // ① 异步切场景(推荐) RevScene.OnProgress += p => bar.value = p; // ② 进度挂一次,全局生效
更省心的写法是挂事件:OnLoadStart 开加载界面、OnLoaded 关加载界面 ——
业务里一个 await 都不用写。
剩下的(最短展示时长、按分组卸资源、诊断日志……)都是用到再看的增值项。
这个模块把这些坑一次填掉:你只管给场景名,进度、并发、清理、失败原因都由它兜住。
| 它替你解决的问题 | 怎么做的 |
|---|---|
| 进度条卡在 90% 不动,然后瞬间跳满 | 关掉 Unity 的自动激活:进度换算成 0~1,到 100% 才放行(见第三节) |
| 小场景加载太快,加载界面"闪一下" | 传 minSeconds:进度条至少走这么久,走完才允许切 |
| 同一个场景被点了两次,互相踩 | 正在切换时,后来的请求直接忽略并告警(不会黑屏 / 回不去) |
| 切换后旧场景的东西还在(池化实例 / 画面残留) | 默认自动清空对象池;UI / 音效 / 资源分组用 OnLoadStart 一行挂上 |
| 场景名写错 → 黑屏,只能猜 | 当场报人话:场景不存在,或没有加进 Build Settings… |
一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); // 按业务域卸资源 };
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.5s | await 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% 再瞬间跳满"。框架的做法:
- 关掉自动激活(
allowSceneActivation = false); - 把
[0, 0.9]归一化成[0, 1],并且只增不减(采样抖动不会让进度条回退); - 显示到 100%(且满足最短时长)才放行激活。
于是你拿到的 p / RevScene.Progress 永远可以直接喂 Slider.value,不用自己乘系数。
② 最短展示时长:minSeconds
小场景加载只要几十毫秒,加载界面会"闪一下"。传 minSeconds(或全局 DefaultMinSeconds)后:
- 进度条最多按"这么长的节奏"走到 100%;
- 到点之前不会激活场景 —— 所以是"真的多显示了一会儿",不是假进度。
③ 切之前清理:框架默认只清对象池,其余你一行挂上
| 要清什么 | 谁负责 |
|---|---|
| 对象池里的实例(旧场景的残留画面 / 占内存) | 框架默认帮你清(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 分钟定位
- 打开日志:
RevScene.VerboseLog = true→ 能看到"准备切换 → 场景名 / 开始异步切换 / 已进入场景 / 已清理对象池 N 个实例"。 - 看状态:
RevScene.State(Idle/Loading/Done/Failed)+RevScene.Progress+RevScene.IsLoading。 - 看失败原因:
RevScene.OnLoadFailed += why => RevLog.Error(why);—— 原因里会写明是"场景不存在"还是"没进 Build Settings"。 - 进度不动:正常现象是加载期间停在某个值(比如 40%),那是 Unity 还没到 0.9 平台期;真卡住不动要查是不是同步 IO 阻塞了主线程。
相关文档
- 同目录《场景系统 · 架构解析》:为什么关掉自动激活、为什么进度的换算是纯 C#、为什么只自动清对象池
- 《资源加载系统 · 使用说明》:场景切换时按业务域卸资源(
RevResBootstrap.Instance.Shutdown(group)) - 《UI 系统 · 使用说明》:加载界面用面板写(
RevUI),关全部界面用RevUI.ShutdownAll()