Skip to main content

资产预加载模块

在 World 构造之后,可以通过 World 的 getPreload 方法获取到预加载模块,对 World 下所有用到的资产进行预加载。

包含能力​

预加载模块在运行时由三个部分组成:资产下载、预处理、预创建。

1. 资产下载​

资产下载时会把资产都存储到浏览器的 IndexedDB 中,在 World 运行使用资产的时候就优先使用 IndexedDB 中的缓存资产,如果 IndexedDB 中未查询到资产会降级到使用网络资源,这会大大降低用户使用体验。所以建议使用预加载功能。

2.资产预处理​

资产预处理会提前缓存角色相关的 3D VAT 资源和 shader 预热,减缓进入场景之后的性能消耗。该功能支持单独关闭。

3.资产预创建​

资产预创建会提前创建场景内的前景资产,包括 richSurface、subSequence 类型等,可以很大程度上减缓进入场景后的低帧率和走动过程中的卡顿问题。该功能需要配合 setPrecreateClasses 接口使用,否则默认关闭该功能。

setPrecreateClasses 传入 actor 的构造类列表 SDK 会拉取当前actor配置文件内容,按照其 actor 类型及其构造类依次创建 actor。

如何生成 actor配置文件

通过在业务链接上拼写 open_collect_actor=1 开启 actor 扫描收集模式,此时 sdk 会按照真实业务场景收集到 actor 的配置信息,扫描完成之后自动上传云端。文件区分 sit/uat/prod 环境。

使用介绍​

1.开始执行预加载​

class Preload {
start(onProgress?: IPreloadProgress, options?: IPreloadStartOptions): Promise<void>
}

调用 start 方法就可以进行预加载,preload 异步调用完成,就代表着预加载完成。可以传入 onProgress 方法作为预加载进度的回调,每秒都会回调 1 次。

world
.getPreload()
.start((current, total) => {
console.log(current, total)
})
.then(() => {
// 完成
})

// 也可以await调用
await world.getPreload().on('progress', ({ current, total }) => {
const progressText = `(${current}/${total})`
})
// 完成

预加载进度也支持以事件形式通知:

world.getPreload().on('progress', ({ current, total }) => {
const progressText = `(${current}/${total})`
})

指定待加载资产​

通常情况下,一个项目下初始使用资产是少于该项目全部资产的。例如在有多个房间的情况下,首次进入页面只会消费当前房间的资产,如果用户此时推出应用,那么永远都不会使用到其他房间的资产。因此为了优化首次进房速度,我们可以选择加载首次必要的资源来节约一部分资产下载时间。

SDK 提供了三种方式从不同的资产维度来指定待加载资产,可以通过在 start 方法传入 options 选项来进行设置。

interface IPreloadStartOptions {
/**
* 符合该filter方法的资产会被过滤掉 方式1
*/
filterCallback?: (item: IPreloadItemConfig, index: number, array: IPreloadItemConfig[]) => boolean
/**
* 已经加载过的资产url列表,如果不为空的数组,则用它来和需要预加载的资源进行对比 方式2
*/
preloadedList?: string[]

/**
* 启用新版Xconsole分包 方式3
*/
openConsolePack?: false
/**
* 指定加载某一个 roomId 下的所有资产
*/
roomId?: string
}

a. 设置资产筛选方法​

通过 options 里的 filterCallback 字段传入,filterCallback 的背后是基于 Array.prototype.filter,它就是数组 filter 方法的参数,用于在一次下载过程中符合该过滤条件的资产。filterCallback 的第一个参数是单个资源的描述信息,它的数据结构如下,一般情况下可以根据 roomId 和 avatarId 就能实现一次预加载过程只下载某几个房间或者某几个 Avatar。后续分包过程可以按照自己的业务逻辑来过滤不同的资源。

//这里需要注意的是所有 Avatar 的资产的 `roomId` 属性为空字符串。所有房间资产的 `avatarId` 属性都为空字符串
export interface IPreloadItemConfig {
assetPath: string
assetSize: number
assetType: string
assetUrl: string
packName: string
roomId: string
avatarId: string
}

b. 指定不再加载的资产列表​

通过传入options 里的 preloadedList 字段设置,preloadedList 是一个string数组类型,表示已经加载过的资产 url 列表,如果不为空的数组,则用它来和需要预加载的资源进行对比,最终的加载列表会去除这些指定资产。

c. 指定房间来选择预加载资源包​

通过传入 options 里的 openConsolePack 字段设置为 true 开启按照房间分包功能,同时需要指定正确的 roomId来设置加载指定房间下的资产。注意在使用该功能时需要提前在 console 应用管理平台进行拆包配置。同样的,如果在 console 应用管理平台上进行了拆包配置,则业务开发使用预加载模块时务必传入正确的 roomId,否则将报错。 同时该方法与方法 1 不同时使用。

2.预加载报错处理​

预加载出错情况包括 indexDB 打开异常、资产下载失败(一般是网络原因),等原因,这些情况都会在start()方法直接 throw err:

world
.getPreload()
.on('progress', ({ current, total }) => {
const progressText = `(${current}/${total})`
})
.then(() => {
// 完成
})
.catch((err) => {
console.error(err)
// 切b面/重试
})

当然也可以await调用,这完全取决于开发者的业务需求:

try {
await world.getPreload().on('progress', ({ current, total }) => {
const progressText = `(${current}/${total})`
})
} catch (error) {
console.error(err)
// 切b面/重试
}

// 完成

3.事件监听​

弱网事件​

在运行中,存在网络较差,但又不会导致 http 下载失败的情况,这种情况下用户可能会陷入无休止的等待,体验非常差。因此 SDK 提供旁路检测机制,当连续15s没有一个资产下载完成时,就会触发poorNet事件,终止预加载行为。因此开发者必须监听该方法,并做相应处理:

world.getPreload().on('poorNet', (err) => {
// 切b面/重试
})

可以通过修改poorNetJudge参数来设置判定poorNet的秒数:

// 连续30s没有一个资产下载完成,才触发`poorNet`事件
world.getPreload().poorNetJudge = 30

预加载单个资产下载超时事件​

预加载在单个资产即将超时时会以事件形式通知,注意在网络情况差的场景下,可能会多次触发事件,业务侧在使用时可以根据使用场景进行节流等处理方式。

world.getPreload().on('singleAssetNearlyTimeout', ({ url, size }) => {
// do something
})

其他​

设置资产加载并发数​

支持通过修改 preload 里的concurrence成员来设置资产下载最大并发数,设置后将同时最多发起对应数量的http请求,具体做法为 world.getPreload().concurrence = n,若不传入,默认并发数为 6 个。

在不同 releaseId 之间切换​

因为 IndexedDB 的存储容量有限,所以在使用了预加载的不同应用或者不同 releaseId 之间切换时,SDK 内部会根据 IndexedDB 中已下载过的资产和当下需要下载的资产进行对比,并且清除掉不在该 releaseId 中的资产,只下载需要下载的资产。