cocos2dx 多人小游戏时间同步问题(简单版)

来源:互联网 发布:2017大数据概念股龙头 编辑:程序博客网 时间:2024/05/21 11:36

在多人版的游戏开发过程中,我们会经常碰到这样一个问题:由于每个客户端网络环境差异导致接收服务器消息的时间不同,就会导致多个客户端呈现的画面不同(即画面不同步),例如:以彩期开奖为例,客户端A已经收到开奖结果的推送了,但客户端B没有收到,如果不做任何处理,会导致后面画面的差异越来越大。

因为网络环境的差异是一个客观的问题,所以我们并不能保证每个客户端能在同一时间收到服务器推送的消息。但我们可以在代码逻辑上可以避免差异的扩大化,尽量保证之后的差异不受前面的影响。

另外一个大前提:既然是多人版,就应该用的是长连接。
短连接:是指通讯双方有数据交互时,就建立一个连接,数据发送完成后,则断开此连接,即每次连接只完成一项业务的发送。
长连接:多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是短连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,下次处理时直接发送数据包就OK了,不用建立TCP连接。
举一个很容易理解的例子:长短连接可比作为即时通话和语音。

本文以彩期下注开奖为例,做简单的同步,能基本保证画面同步。(长连接)
这里写图片描述
上图 gameStart为游戏开始(主动请求)、stopBet为停止下注(服务器推送)、gameResult为开奖结果(服务器推送)。
这里gameResult和gameComplete之间的时间是提供给客户端呈现开奖画面的时间(例如:转盘滚动到指定位置)
遇到的主要问题:1.倒计时不同步(start和result结果之间会有一个开奖倒计时),2.客户端由于网络延迟导致收到result消息晚了,导致画面不同步。(这里是收到结果之后才开始播动画)3.两局之间衔接得不是很好(之前的做法是等到了gameComplete,也就是开奖动画结束后才开始请求下一局游戏,如果这个请求延迟了就会导致刚开始就画面不同步的问题)

首先处理问题3:直接给请求下一局预留缓冲,在拿到当前Result时候就去请求下一局的信息并保存下来。假若gameResult和complete之间有20s的时间,那么给这个请求和接收预留的缓冲时间就是20S。再假设20S都没接收到、那么建议是提示玩家重新连接游戏。

问题1和2有一个共性的问题就是:网络延迟。具体一点就是…算了,举例来说吧:假设客户端A给服务器发了一个gameStart的请求,发送这个请求的时间是T0。服务器在S0时收到这个请求,给客户端A发送应答消息。而客户端A收到这个请求的应答时间是T1,在这个gameStart的response数据中,服务端会告诉你它的当前服务器时间S1以及开奖时间S2。(用来做倒计时用)

不难看出,因为服务器时间是一直在走动的,如果发送和接收的时间差比较大的话(例如发送和接收均消耗10S),可能造成你收到的服务器时间误差较大。(你收到的是S1,其实当前服务器时间是S1+10)

有很多人的做法是取 S1+(T1 - T0)/2 作为当前服务器时间,这样做有一定的效果,但是当发送和接收消耗的时间差距较大时(例如发送到服务端时间为1S,消息应答从服务器返回消耗了19S),按上述计算出当前服务器时间为S1+10,但实际却为S1+19。

    Service.gameStart(data,this._newGame,this);    this._sendTime = new Date().getTime();    _newGame:funciton(data){        this._receiveTime = new Date().getTime();           var currTime = data.currTime;//服务器当前时间        var curServerTime = 0;        var timeDiff = (this._receiveTime - this._sendTime)/2;        if(timeDiff  < 500 || Math.abs(currTime -   ServerTimeUtils.getServerTime()) > 1000){            curServerTime = ServerTimeUtils.updateServerTime(currTime+timeDiff);        }else{            curServerTime = ServerTimeUtils.getServerTime();        }    }

思路,即本地记录保存服务器时间LS,设置个clock让其一直走。在上述公式基础上,加上判断:如果发送接收时间差小于1S,就按公式来即S1+(T1 - T0)/2。如若大于1S,则服务器时间按LS来。
“Math.abs(currTime - ServerTimeUtils.getServerTime()) > 1000”这个判断只是为了第一次的LS初始化。

ServerTimeUtils(全局变量):

var ServerTimeUtils = {    _serverTime: new Date().getTime(),    serverTimeClock:function(){        var self = this;        global.timers.setInterval(function(){            self._serverTime = self._serverTime + 1000;        },1000)    },    getServerTime: function(){        return this._serverTime;    },    updateServerTime: function(newServerTime){        this._serverTime = newServerTime;    }}ServerTimeUtils.serverTimeClock();

问题2的遗留问题:即接收Result的快慢导致开奖转盘不同步问题,这里由于我的项目中转盘做的变减速运动、很难保证每个时间点转盘画面都同步。这里我是采取了一个折中的办法

    var turnTime = this._turnSeconds;    var timeDiff = resultTime - ServerTimeUtils.getServerTime();//resultTime是服务端返result时返的服务器当前时间    if(Math.abs(timeDiff) > 1000){        if(resultTime > ServerTimeUtils.getServerTime()){            turnTime = turnTime - Math.abs(timeDiff)/1000;        }    }else{        ServerTimeUtils.updateServerTime(resultTime);    }

思路:如果接收result时间慢了几秒,那么就让转盘转到目的位置的时间少消耗几秒。(即提高了速度)
这样保证了转盘指针到目的地的时间是一致的,即多个客户端即使受到result时间不同,到达目的地的时间也相同。这样做保证能同时进入下一局游戏。

时间画面同步个人感觉是一个比较复杂的问题、特别是前端逻辑以及动画比较复杂的环境。上述只能处理较简单的小游戏问题,而且还有部分极端情况未做处理(用重新登录去处理)。

0 0
原创粉丝点击