因为笔者是一个客户端开发,平时也有玩过很多游戏。而且通过某些游戏的广告得知,应该大部分游戏都会有装备回收这个功能。那么说到装备回收,那在策划角度上,就会有回收提醒的问题。或者是弹窗,或者是红点,触发弹窗或者红点,就需要可回收的装备数量达到一定的条件。一般来讲,这个条件是可回收装备的数量。

首先,回收的条件是每个装备部位保留最好的一套装备,(一般的游戏都会这么做的吧)
获取可回收装备的数量,就要考虑背包数据同步的问题,一般的,游戏登陆,服务器主动推送背包信息以供前端做初始化背包数据。后续背包中物品的增,删,更新,都是通过一条一条的通讯包来处理。那么,我们需要知道可回收装备数量是否达到了条件触发提示。

最简单可以想到的方法就是,当我获得一件装备的时候,遍历所有的装备,找到最好的装备,剩下的装备就是可以分解的装备。那在背包数据庞大的时候,更新数据很频繁(比方说我在打副本,爆出了很多装备,这个时候,服务器因为自身架构设计,装备数据逐条推送给前端而非整合到一起推送),计算量巨大,那么消耗自然很大,游戏就会卡顿(有些人会觉得自己手机不行了,得换一台新手机吧)。

有一种优化方式,减少更新的频率,比方说,做延迟判断,当我获得一件装备的时候,设置延迟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
    }
}
Logo

更多推荐