2019独角兽企业重金招聘Python工程师标准>>>
目前在做一个放置类的微信小游戏,时间过去了一个多月,大的功能已经很少了,是时候来一波优化了。策划那边还是在浏览器上查看游戏,反映了几个问题,主要是操作延迟。这个分为几个方面:
- 网络延迟:包括资源服务器延迟,小程序的资源大部分都是放在资源服务器上的,加载这些资源会出现延迟;另外还有游戏逻辑服务器的延迟
- IO延迟:从手机存储读取文件是很慢的,尤其是一堆文件的时候
- 渲染延迟:组件太多、太深,渲染压力大
- 性能问题:复杂的逻辑、计算密集型的任务导致主循环被持续占用,反映出来就是ui响应慢
路要一步一步走,饭要一口一口吃。我们今天就先来聊聊如何解决网络延迟的问题。
预下载所需资源
首先我们要看下,creator在小游戏环境中是如何下载资源的。creator提供了一个wxDownloader对象用来处理远程资源的加载,步骤如下:
- 1、检查资源是否在小游戏包内
- 2、不存在则查询本地缓存资源
- 3、如果没有缓存就从远程服务器下载
- 4、下载后保存到小游戏应用缓存内供再次访问时使用 具体请看官方文档 官方的实现是将wxDownloader作为加载资源的第一个管道,依旧是按需下载。但是,这样会导致首次访问慢的问题。现在,我们的工作就是将按需下载替换成提前预下载。这里需要在我们的loading页下载所有需要的资源,下载完毕之后再进入游戏。这里有几个问题:
- 1、如何知道需要预下载的资源 这里我们需要再发布远程资源的时候附带生成一个manifest文件,用于记录该版本所有需要预加载的资源。
- 2、并发地执行下载,但需要注意的是,同时超过10个网络请求会报错,所以需要限制你的并发数量 具体代码很简单,可以参照官方的wxDownloader的实现。并发那里我自己用promise封装了一下,将所有的处理步骤作流式处理,按照需要建立多个管道。大概是这样的:
preload(items){
if(!items){
this._onCompleted();
}
// 最后一条不读
this._totalCount = items.length - 1;
this._items = items;
const pipeCount = Math.max(1, Math.min(this._totalCount, this.maxPipe));
for(let i = 0; i < pipeCount; i++){
this._nextHandle();
}
},
_nextHandle(){
const path = 'res' + this._items[this._processCount];
// ttf不读
if (path.substr(-3, 3) === 'ttf') {
this._addProgress();
} else {
this.handle(path);
}
},
_onCompleted(){
if(this._completedFlag){
return;
}
this._completedFlag = true;
console.log('completed state:', this._savedCount, this._processCount, this._totalCount);
console.log('fail to process:', this._failPathes);
if(typeof this._onCompleteCallback === 'function'){
this._onCompleteCallback();
}
},
_addProgress(){
this._savedCount++;
this._process();
if(this._processCount >= this._totalCount){
this._onCompleted();
}
},
_addFail(path){
this._failPathes.push(path);
this._process();
if (this._processCount >= this._totalCount) {
this._onCompleted();
}
},
_process(){
if (this._processCount >= this._totalCount) {
return;
}
console.log('state:', this._savedCount, this._processCount, this._totalCount);
this.stateLabel.string = this._processCount + '/' + this._totalCount;
this._processCount++;
this._nextHandle();
},
handle(path){
console.log('handle:', path);
this.existPromise(path).then(()=>{
console.log(`${path} exist!!`);
}, ()=>{
return this.downloadFilePromise(path);
}).then(()=>{
this._addProgress();
}).catch(()=>{
this._addFail();
})
},
转圈动画
是的,加一个转圈动画,或者延迟加一个转圈动画对玩家的感受真的不一样。不要笑!!之前做过一个手游项目,由于服务器的问题,导致玩家经常会感知到转圈,客服收到无数投诉,大家拼命催促后端小哥优化性能。这时候我灵机一动,把特定rpc的转圈动画出现延迟了0.5秒,发出热更,结果玩家纷纷发帖表示网络好了(我当时就笑趴在了桌子上),直到几个特定rpc的性能问题被修复都再没有玩家向我们投诉这个问题