cocos creator 3.x使用官方帧同步服务开发实时对战微信小游戏教程(三)

上面两章展示了创建匹配规则以及根据规则进行游戏匹配,这章主要对帧同步做一些说明。

本章内容:

玩家确认开始游戏后,帧同步正式开启,帧同步服务便会按照匹配规则中设定的周期下发游戏帧,客户端进行处理

说到帧同步,就不得不提另一种同步模式:状态同步。目前比较容易理解的描述是帧同步用于同步操作,在客户端处理操作结果,打个比方1V1的对战游戏,A玩家点击了开火键,向游戏服务上传了一帧告诉服务我开火了,游戏服务会把这个帧实时发送给B,然后B这边处理A开火以后的逻辑比如B掉血等等。而状态服务则是A开火,把请求发给服务器,在服务器计算B的剩余血量,再由服务器下发给B告诉B你还剩多少血,B客户端显示个扣血动画就行。两者的主要差别是计算逻辑在哪里处理,帧同步告诉你操作,状态服务告诉你结果。

既然我们讲的是帧同步,看了上面的说明那么具体要什么时候上传帧大概也了解了,我们创建一个战斗场景,在左边放一个坦克,在右边放一个坦克,然后再放一个按钮用于坦克开火,像酱紫:
在这里插入图片描述
这个场景什么时候进来呢,上一章的eventTarget.on(‘battle’,function(){//游戏正式开始了,跳转至战斗场景})这边就是跳转到这个场景的地方啦。
这个场景的start()里面,我们需要根据玩家的selfClientId来确定是在左边还是右边,比如selfClientId = 1,坦克的position就设置到左边,selfClientId = 2,坦克就设置在右边。
然后我们增加开火按钮的事件:

onbtnFireClick(){
    
    
	this.gameserver.uploadFrame([
        JSON.stringify({
    
    
          r: 45//射击角度,
          i: 1, //子弹类型
          e: 'Fire',//帧类型,表示开火
          n: databus.selfClientId,//自己的clientId
         })
   ]);
}

我们可以看到,这个开火键没有任何本地逻辑,仅仅是向游戏服务上传了一帧,内容是一个json文本,里面包含了你想广播的内容比如我做了开火操作,角度多少,用的是什么子弹,还有我是房间第几个玩家(房间服务下发的ClientId)
那么其他玩家是怎么收到这个帧的呢,在实例化游戏服务的时候bindEvents()就做好了,如果有玩家(包含自己)上传了帧,onSyncFrame事件就会接收到

//监听收到同个房间的帧同步消息
    onSyncFrame(res) {
    
    
        if (!this.reconnecting) {
    
    
            (res.actionList || []).forEach(oneFrame => {
    
    
                let obj = JSON.parse(oneFrame);
                if (obj.e === 'Fire') {
    
    
                    switch (obj.n) {
    
    
                        case 1:
                            eventTarget.emit('leftTankFire', obj.r, obj.i);
                            break
                        case 2:
                            eventTarget.emit('rightTankFire', obj.r, obj.i);
                            break
                    }
                } 
            });
        }
    }

obj.e会筛选出类型是Fire也就是开火的帧,obj.n则是上传的ClientId,以此确定是左边的坦克开火还是右边的开火,剩余的参数则是角度和子弹类型。eventTarget.emit是cocos creator中的事件发射和监听功能,用来触发节点事件的,所以我们在左边的坦克节点上挂载脚本A,start()中eventTarget.on(‘leftTankFire’,function(){//当前节点的坦克开火}),在右边的坦克节点上挂载脚本B,start()中监听’rightTankFire’就大功告成了。
假如我们的clientId是1,点击了开火键上传帧,onSyncFrame同步到了该帧发射事件给了A脚本的坦克,如果对方点击了开火键,那么我们同步到clientId 2的帧会被发射给B脚本的坦克,双方客户端表现是一致的。

开火是搞定了,那么移动呢?按照帧同步的定义,同步操作,那么我控制了摇杆往左,要发送什么帧呢,是不是要发送类型是’Move’,同时还要携带移动方向和速度的参数?正常来说帧同步是这样的,但是我嫌麻烦(因为单机版摇杆功能已经实现了,不想再大改了…),结合了状态同步的概念,把摇杆移动的操作放到本地了,然后在update()里面把当前节点的xy坐标作为帧上传,类似于这样:

update(){
    
    
	this.gameserver.uploadFrame([
	                JSON.stringify({
    
    
	                    x: this.player.position.x,
	                    y: this.player.position.y,
	                    e: 'Move',
	                    n: databus.selfClientId,
	                })
	            ]);
}

接收帧的客户端根据clientId,把对应节点的position设置成帧内容的x和y就行了(即onSyncFrame方法中添加obj.e === 'Move’的逻辑,发射给对应节点,节点监听到以后修改自身)。

这样移动和开火的帧同步就差不多啦,微信小游戏的帧同步简单来说就是一个客户端上传json,同一个房间内的所有客户端都会实时收到这个json,就像广播一样,只要看到事件按预定的时间点触发就说明没问题,下面自行处理事件逻辑即可。2V2也仅仅是在case 1和2下面加3和4罢了,开始建议只做1V1或者2V2,如果优化不到位3V3或者5V5就卡的不行影响玩家体验,而且流量不够还很难凑一队…说多了都是泪哈哈。

这章先写这么多吧,下一章介绍帧同步的一些细节比如显示对方玩家的昵称、对方坦克用的什么皮肤、怎么结束帧同步并显示结算页面等等…

简单的线上样例:
微信小游戏《精英坦克手》1V1匹配

猜你喜欢

转载自blog.csdn.net/hangsky1990/article/details/131344468
今日推荐