游戏性能优化分享---装备熔炼/回收提示红点
因为笔者是一个客户端开发,平时也有玩过很多游戏。而且通过某些游戏的广告得知,应该大部分游戏都会有装备回收这个功能。那么说到装备回收,那在策划角度上,就会有回收提醒的问题。或者是弹窗,或者是红点,触发弹窗或者红点,就需要可回收的装备数量达到一定的条件。一般来讲,这个条件是可回收装备的数量。首先,回收的条件是每个装备部位保留最好的一套装备,(一般的游戏都会这么做的吧)获取可回收装备的数量,就要考虑背包
因为笔者是一个客户端开发,平时也有玩过很多游戏。而且通过某些游戏的广告得知,应该大部分游戏都会有装备回收这个功能。那么说到装备回收,那在策划角度上,就会有回收提醒的问题。或者是弹窗,或者是红点,触发弹窗或者红点,就需要可回收的装备数量达到一定的条件。一般来讲,这个条件是可回收装备的数量。
首先,回收的条件是每个装备部位保留最好的一套装备,(一般的游戏都会这么做的吧)
获取可回收装备的数量,就要考虑背包数据同步的问题,一般的,游戏登陆,服务器主动推送背包信息以供前端做初始化背包数据。后续背包中物品的增,删,更新,都是通过一条一条的通讯包来处理。那么,我们需要知道可回收装备数量是否达到了条件触发提示。
最简单可以想到的方法就是,当我获得一件装备的时候,遍历所有的装备,找到最好的装备,剩下的装备就是可以分解的装备。那在背包数据庞大的时候,更新数据很频繁(比方说我在打副本,爆出了很多装备,这个时候,服务器因为自身架构设计,装备数据逐条推送给前端而非整合到一起推送),计算量巨大,那么消耗自然很大,游戏就会卡顿(有些人会觉得自己手机不行了,得换一台新手机吧)。
有一种优化方式,减少更新的频率,比方说,做延迟判断,当我获得一件装备的时候,设置延迟1s之后做更新判断,那在这一秒之内获得的所有装备触发判断,都在上一次设置的延迟判断里做更新。可以省去一秒之内多余的触发导致的计算量。
下面是该方法的实现伪代码
enum type{
Equip,
Goods
}
class EquipRecycle{
/**获取后端推送信息时更新背包数据接口 */
setBagInfo(item:any){
//背包数据更新;
//。。。
//。。。
//判断是装备更新
if(item.type === type.Equip)
{
//设置一秒之后执行该函数
this.callLater(this.doRecycleTipCheck.bind(this),1000);
}
}
/**检测当前列表内是否已经有该方法 */
checkIsIn(f:Function):boolean{
let has = false;
//.....
return has;
}
update(){
for (let index = 0; index < this._handlers.length; index++) {
const element = this._handlers[index];
if(Date.now() < element.delay){
element.f(); // 执行代码
this._handlers.splice(index,1);//执行之后从列表中删除
}
}
}
_handlers:any[];
/**延迟执行方法,写了个伪代码,并非真实实现 */
callLater(f:Function,delayTime:number){
//当该方法已经加入列表时,直接返回,保证上次设置1s后只执行一次该函数
if(this.checkIsIn(f))return;
this._handlers.push({f:f,delay:Date.now() + delayTime});//这里可以保存为时间戳
}
/**检测装备回收接口 */
doRecycleTipCheck(){
//do something
}
}
显然,这种方式缺少了实时性,从策划的角度上不一定会接受,而且,并没有减少背包数据量庞大时的计算量。
那么深入思考这个问题。刚刚讲到的操作是。
当我获得一件装备的时候,遍历所有的装备,找到最好的装备,剩下的装备就是可以分解的装备。
其中一个很重要的点,是找到最好的装备。那么如果我保存这份最好的装备,再另外获得一件装备的时候,是不是可以直接和最好的装备做比较呢?
令新获得的装备为N(new),当前对应位置的最好装备是B(best)
比较结果
1.N更优于B,那B可以被回收,将B置换成N;
2.N不如B,那N可以被回收;
得到的结论是,当我获得一件装备的时候,无论好与坏,可回收数量都是增加1的。
那么当我删除一件装备的时候。理论上讲,不会删除最好的装备。
那么根本无须做判断,删除一件装备时可回收数量一定是自减1的。
那么就可以通过一次判断装备,维护一个数字类型就可以解决卡顿的问题,又保证了数据的实时性。
下面是这种方式实现的伪代码
class EquipRecycle2{
/**根据部位存放当前部位最好的装备数据 */
private _bestEquipMap:Map<number,any>;
/**当前可回收装备数量 */
private _curCanRecycleNum:number = 0;
get curCanRecycleNum(){
return this._curCanRecycleNum;
}
set curCanRecycleNum(value:number){
this._curCanRecycleNum = value;
if(this._curCanRecycleNum > 50){ // 假设大于50要进行提醒
this.doRecycleTip();
}
}
initBagInfo(){
this._bestEquipMap = new Map();
//初始化背包数据的时候直接找出对应部位最好的装备,存起来
//顺便找出其余可回收装备的数量,赋值
//this._bestEquipMap.set(x,xxx);
//this._curCanRecycleNum = xxx;
}
/**获取后端推送信息时更新背包数据接口 */
setBagInfo(item:any){
//背包数据更新;
//。。。
//。。。
//判断是装备更新
if(item.type === type.Equip && item.canRecycle)
{
this.curCanRecycleNum ++;
let best = this._bestEquipMap.get(item.position);
if(item.fight > best.fight){ // 如果新的更好,更新存储
this._bestEquipMap.set(item.position,item);
}
}
}
deleteBagInfo(item:any){
//可能有些装备是不能被回收的
if(item.type === type.Equip && item.canRecycle)
{
this._curCanRecycleNum --;
}
}
/**通知提示*/
doRecycleTip(){
//do something
}
}
更多推荐

所有评论(0)