# 场景流式加载
当加载一个具有很多个Prefab的大场景时,下载依赖资源大小有可能达到数十MB,整个下载的过程会较为漫长。为了解决该问题,微信小游戏框架提供了场景流式加载的方案,以实现场景中Prefab的动态按需加载。
流式加载方案的核心是StreamingLoadingComponent组件。
# 编辑以及烘培期
- 在编辑器中挂载 StreamingLoadingComponent 的节点,其直接子节点中是由 Prefab 创建的节点树会被视为是要动态加载的节点树。如下图中的 World 节点挂载了 StreamingLoadingComponent,则其下面的 floor、wall1、wall2 等子节点都是由 Prefab 创建的。
- StreamingLoadingComponent属性介绍
属性 | 功能 |
---|---|
loadingPrioirty | 表示这些子节点的加载优先级。 |
unloadType | 表示这些子节点在被动态卸载时应用怎样的操作。 |
Deactivate | 表示将这些节点的 active 置为 false。 |
Destroy | 表示将调用节点的 destroy 方法,即销毁。 |
unloadDebounce | 表示节点被判定要卸载时的一个防抖动等待时间,为了避免玩家在一个边界区域反复移动时造成的反复卸载。 |
- 在Project中右键该场景,点击 “烘培流式加载场景” 。
- 烘培场景的意思就是将场景中所有需要被动态加载的节点从场景 mygame.scene 中剔除,并另存为一个新的场景文件。开始烘培后需要等待一段时间,完成后又上角会有浮窗提示
以此处的 World 节点为例,其下 15 个子节点都是由 Prefab 创建的,所以新生成的 assets/bake/mygame.bake.scene 中,这 15 个子节点的信息被剔除了。assets/bake 目录下会生成 mygame.bake.scene 和 15 个子节点对应的 Prefab。
- 将输出烘培后文件的目录设置为打包入口,这样才生成的Prefab才能被动态加载。
- 将烘培后的文件作为入口发起构建
# 运行时
运行时需要设置 engine.game.streamingLoadingSystem 来管理 StreamingLoadingComponent。
# interval
streamingLoadingSystem 做的事情就是每隔一段时间检查 StreamingLoadingComponent 下的 StreamingLoadingObject(以上文 World 为例,StreamingLoadingObject 就是 15 个要被动态加载的 Prefab)是否需要被加载/卸载,这是 streamingLoadingSystem 的一次 update。
// 默认值 500,单位 ms
engine.game.streamingLoadingSystem.interval = 1000;
# Tester
判定物体是否要被加载是通过 engine.game.streamingLoadingSystem.tester 来控制的,tester 是一个实现了 needTest 和 test 两个方法的对象。这两个方法都是被 streamingLoadingSystem 在每次 update 时调用的回调函数。
needTest() {
return true
}
# Tester.prototype.needTest()
needTest 是一个没有参数的回调函数,表示是否需要进行后续的遍历,如视角/玩家位置变化不大,可以理解为玩家视野变化不大,因此无须遍历检查物体是否需要加载/卸载。
# Tester.prototype.test(object)
test 是一个传入 StreamingLoadingObject 的回调函数,StreamingLoadingObject 包含以下几项属性
test(object) {
// object.center, Vector3, 表示物体包围球的中心,是一个 worldPosition
// object.radius, 表示物体包围球的半径,也是在 world 坐标系里的
// object.priority 表示物体的加载优先级
// 返回 0 表示加载
return 0
// 返回 1 表示卸载
return 1
}
engine.game.streamingLoadingSystem.tester 的默认值是引擎实现的一个基于包围球相交检测的 tester,如果没有特殊需要,可以使用这个 tester。
const tester = engine.game.streamingLoadingSystem;
// 设置 center 来表示当前玩家的位置,center 是一个 Vector3,而且应该是一个 worldPosition.
tester.center.x = -131;
tester.center.y = 50;
tester.center.z = 80;
// 设置 radius 来表示玩家的视野范围,在这个范围内相交的物体会被加载,反之超出这个范围的则会被卸载
tester.radius = 50;
如果认为这样的 tester 不能满足要求,可以自行实现一个 tester
class CustomTester {
constructor() {
this.lastPosition = engine.Vector3.createFromNumber(0, 0, 0);
this.currentPosition = engine.Vector3.createFromNumber(0, 0, 0);
}
needTest() {
// 上次观测点距离本次观测点大于一定距离,才进行检测
if (this.currentPosition.distanceTo(this.lastPosition) > 20) {
this.lastPosition.x = this.currentPosition.x;
this.lastPosition.y = this.currentPosition.y;
this.lastPosition.z = this.currentPosition.z;
return true;
}
return false;
}
test(object) {
// 特殊需求,999 表示一定要加载永不卸载
if (object.priority === 999) {
return true
}
// ...
}
}
engine.game.streamingLoadingSystem.tester = new CustomTester()
完成场景流式烘培之后,动态加载的节点依赖的资源将不会在加载场景时被加载,假如场景的根节点就是上例中的 World,这个场景将没有任何依赖资源;所有节点都是动态加载的,在 streamingLoadingSystem 判定其需要被加载时,才会去下载 & 读取对应的 Prefab。