使用 Unity 开发 Android VR 应用系列之三——实现场景管理器

0. 需求分析

绝大多数应用里,都会有类似的业务逻辑:从 A 页面跳转到 B 页面,执行一系列操作后返回 A 页面。

这看似寻常的操作流程,Unity 是不能天然支持的。Unity 的 SceneManager 和 Android 的 activity stacks 在”页面”管理策略上大概有以下不同:

  • 加载 B 场景,默认会销毁 A 场景

    这种情况下想实现返回功能,必须重新加载 A,并且还原 A 销毁前的所有状态(滚动条位置等等)——业务逻辑越复杂,要记住的状态越多,这个方案不具有普适性。
    可显式指定以 Additive 模式加载新场景,加载 B 后仍将 A 保留。但是:

  • Additive 模式加载 B 后,A 不会被隐藏

    绝大多数情况下,这也不是我们期望的行为。所以需要基于 Additive 逻辑更进一步,使 B 加载后 A 自动隐藏。

综上,SceneManager 并不满足常见需求,需要自己实现一个 “scene stack”,使多个场景形成 LIFO 的栈式结构。

1. 系统设计

以一组类图表示各参与者及其关系:

  • SceneStack

    以链表保存场景上下文信息,负责场景的加载与销毁。

    注意 SetFirstScene(string sceneName, ControllerBase controller) 函数。应用中第一个场景是由系统负责加载的,用户没有干预的余地,所以只能在其加载后通过此函数手工添加到栈里。

  • ControllerBase

    Controller 即是最上层 UI 元素(通常是一个 Canvas)关联的脚本(MonoBehaviour)组件。将 controller 同对应的场景同时保存到 SceneStack 中,即可通过 controller 函数控制场景的显示与隐藏。

如此,基本实现了”新场景加载时使旧场景隐藏而不销毁”的流程化、自动化。

2. 代码实现

完整的代码实现在这里,同时提供一个两个场景的简单测试用例。

3. 总结

其实,我一直在混用”场景”与 “UI” 的概念。以 MVC 模型解构 Unity 的 UI 系统,场景是 Controller,UI 是 View。所以准确地说,我上述所提到的显示/隐藏,目标不是场景,而是场景内的 UI 元素。

实际也确实如此,Scene 类是没有类似显示/隐藏的接口可用的,代码里操作的是场景内 UI 根元素。我之所以如此表述,只是单纯地想简化描述罢了——毕竟隐藏了所有 UI,就好像是场景隐藏了一样。

评论