正文
对于游戏来说,详细的资源加载进度展示是相当重要的。可以让玩家了解游戏加载的进度,避免了玩家长时间等待而感到沮丧或无聊。可以让玩家更好地了解游戏加载的时间和进度,更好地规划自己的时间,知道何时可以开始游戏,而不会中途离开或失去耐心。
1.Bundle介绍
从 v2.4 开始,Creator 正式支持 Asset Bundle 功能。
Asset Bundle 作为资源模块化工具,允许开发者按照项目需求将贴图、脚本、场景等资源划分在多个 Asset Bundle 中,然后在游戏运行过程中,按照需求去加载不同的 Asset Bundle,以减少启动时需要加载的资源数量,从而减少首次下载和加载游戏时所需的时间
Asset Bundle 可以按需求放置在不同地方,比如可以放在远程服务器、本地、或者小游戏平台的分包中
2.Bundle加载
assetManager.loadBundle('xxx', (err, bundle) => {
bundle.load('xxx');
});
通过以上代码,我们可以对Bundle进行加载;
需要注意的是此加载非彼加载,什么意思呢?
在通过 API 加载 Asset Bundle 时,引擎并没有加载 Asset Bundle 中的所有资源,而是加载 Asset Bundle 的 资源清单,以及包含的 所有脚本
也就是说,当我们调用了开头的loadBundle加载了Bundle后,如果使用到其中的某个资源,还需要单独对某个资源进行加载
bundle.load(`prefab`, Prefab, function (err, prefab) {
let newNode = instantiate(prefab);
director.getScene().addChild(newNode);
});
3.远程Bundle加载
来到了本文的关键部分
Bundle加载本身只是对资源清单和脚本进行加载,这个步骤其实并不会有太大的耗时,所以loadBundle接口并为提供加载进度的回调,但是前提是Bundle包是被存放再本地
当Bundle包被存放在远程服务器时。我们在调用loadBundle时,中间多了一步下载,首先将Bundle包下载到了本地,然后再进行加载
由于不存在进度的回调监听,所以在实际调用loadBundle时我们没办法获得Bundle下载的实时进度,只能耐心等待其成功回调的触发。就和前边提到的,这个毫无明确数据显示的加载会导致大量的玩家没有耐心去等待,从而流失。
这种情况最常见的就是有包体大小限制的小游戏平台,大多数的资源都是存放在服务器的,所以加载远程Bundle,并可以完美的显示其加载进度就尤为重要。
在宗宝的游戏中,对微信小游戏和字节小游戏进行了适配,借助平台自身的API和Cocos现有的API来实现一套可以获取远程下载实时进度的方案:
public downLoadBundle(name: string, onProgress: (progress: number, writeByte: number, allByte: number) => void, onComplete: (isSuccess) => void): void {
.....
}
1.初始化
1.初始化Bundle的远程服务器路径
2.初始化平台本地路径
3.初始化本地记录本版号和当前项目使用到的最新版本号
let remoteUrl: string = `${assetManager.downloader.remoteServerAddress}remote/${name}`;
let savePath: string = wx.env.USER_DATA_PATH + "/gamecaches/" + name; // 微信本地路径
//本地版本
let localVersion: string = sys.localStorage.getItem(name);
//最新对应
let version: string = assetManager.downloader.bundleVers[name];
2.版本匹配
1.进行版本匹配检测,如果相同,则本地已是最新版本,直接调用loadBundle进行加载
2.不匹配,则服务器存在最新版本需要更新
if (version === localVersion) {
// 已经是最新的版本了
console.log(`最新版本 ${name} bunder ${version}`);
this.loadBundle(savePath, onComplete);
} else {
...
}
3.清除旧版本
1.如果存在旧版本Bundle,在更新新版本前,将旧的资源进行删除
wx.getFileSystemManager().rmdirSync(savePath, true);
4.更新Config文件
1.下载config.json文件,下载成功后被存放在临时目录
let configFile: string = `/config.${version ? `${version}.` : ''}json`;
let downloadTask: any = wx.downloadFile({
url: remoteUrl + configFile,
success: (res) => {
if (res.statusCode === 200) {
if (onComplete) onComplete(res.tempFilePath);
}
},
fail: (res) => {
console.log(`下载 ${remoteUrl + configFile} 失败, err ${res}`);
if (onComplete) onComplete(null);
},
});
2.将临时目录的config.json文件移至本地路径下
wx.getFileSystemManager().saveFileSync(file, savePath + configFile);
let data = wx.getFileSystemManager().readFileSync(savePath + configFile, "utf8");
configData = JSON.parse(data);
5.更新资源文件
在宗宝的游戏中,所有的远程资源都是在Cocos Creator中对Bundle的进行配置,压缩类型选择的是ZIP,所以只需要下载对应版本的zip资源文件
1.对zip文件进行下载,下载成功后默认存放在临时目录
2.监听下载进度,可以获取整个包体的字节大小,以及已下载的字节大小和进度
let resFile: string = `/res.${resVersion ? `${resVersion}.` : ''}zip`;
console.log(`下载res zip 文件 ${resFile}`);
let downloadTask: any = wx.downloadFile({
url: remoteUrl + resFile,
success: (res) => {
if (res.statusCode === 200) {
console.log(`下载 ${remoteUrl + resFile} 成功,临时目录:${res.tempFilePath}`);
if (onComplete) onComplete(res.tempFilePath);
}
},
fail: (res) => {
console.log(`下载 ${remoteUrl + resFile} 失败, err ${res}`);
if (onComplete) onComplete(null);
},
});
downloadTask.onProgressUpdate((res) => {
if (onProgress) onProgress(res.progress, res.totalBytesWritten, res.totalBytesExpectedToWrite);
});
3.将临时目录下载zip资源移至与config.json相同的本地路径下
4.所有资源下载成功,保存版本号
wx.getFileSystemManager().saveFileSync(file, savePath + resFile);
console.log(`bundle文件 ${name} 已经从 ${file} 移动到 ${savePath + resFile}`);
sys.localStorage.setItem(name, version);
6.加载本地路径下的Bundle包
assetManager.loadBundle(savePath, (err, bundle) => {
if (err) {
console.warn(`杨宗宝:load bundle ${path} task err, err msg:${err}...`);
onComplete(false);
} else {
console.log(`杨宗宝:load bundle ${path} task success...`);
onComplete(true);
}
});
总结
以上是宗宝个人的一些经验,以及对于微信小游戏平台显示远程Bundle加载进度的实现方案。同理对于字节小游戏也是相同的逻辑,只需要简单进行调整即可。
希望宗宝的分享可以帮助到大家,欢迎大家一起讨论,一起进步。