1.为什么要同步机制
网络游戏中,由于网络延迟的不确定性,造成游戏玩家游戏内容的不一致性问题,因此需要同步机制进行处理。主要分为帧同步与状态同步两种。
2.帧同步
早先,在局域网中使用Peer-to-Peer模式,大致原理为:所有玩家将输入操作发送给与自身相连的客户端,让他们也去根据发送的操作模拟游戏世界。这样在网络情况好的情况下,表现很好,但是当有一定的延迟就会造成各个玩家之间的不一致性。
因此,很多早期的RTS游戏都采用了帧同步来作为网络同步的方案,这种模型就可以把游戏的过程分为一个一个的步,游戏的每一步都需要通过网络来收集所有玩家的操作输入,然后再往下执行,这也就是称为 lockstep(锁步,锁帧,囚徒模式)。
囚徒模式的帧同步,有一个致命的缺陷就是,若联网的玩家有一个网速慢了,势必会影响其他玩家的体验,因为服务器要等待所有输入达到之后再同步到所有的c端。一般采用“定时不等待”的乐观方式在每次Interval时钟发生时固定将操作广播给所有用户,不依赖具体每个玩家是否有操作更新。如此帧率的时钟在由服务器控制,当客户端有操作的时候及时的发送服务器,然后服务端每秒钟20-50次向所有客户端发送更新消息。
一些注意的地方: 为了同步,一般避免使用浮点数,因为容易造成不同机器不一致的问题;同时使用伪随机种子,保证整个游戏中的随机结果一致(暴击概率一致)。
3.状态同步
状态同步指的将所有玩家将操作数据发送给服务端,由服务端计算结果,将游戏状态广播返回给客户端们,客户端根据结果显示。比如,当玩家A使用了某个技能,对玩家B造成了伤害,A请求服务端:“我(A)用技能(skillid)打了B,请求判定伤害”,服务端收到后,进行一定的确认后,取出A这个技能的伤害值,再取出A/B的状态模型,更新服务端的A/B的状态模型(B减血量,也有可能被打死了,A加经验,死了加人头数),然后进行广播:“A用技能skillid对B造成了伤害(这句说不说不重要),现在A的状态模型时XX,B的状态模型是XX”,所有玩家收到后,在本地更新这些数据模型,进行一定的显示(血条变化,死亡动画)。
这里多说一句,发技能和造成伤害一般不是一回事,要发技能了,你按了个q,这个时候你请求服务端:“A玩家请求用q打B,可以吗?”,一般你没开挂,服务端就会广播:“A要用技能q打B了,你们快播放动画”;大家收到消息后,都开始修改A的动画状态机,让他播放动画。A也是如此。(确实有点尴尬,要是卡了的话,按了q,半天没反应,所以玩家就不高兴了,所以一般设计个技能前摇,这个时候就是在等服务端的数据),直到技能攻击动画播放到关键帧(比如孙悟空的棒子打了一半),这个时候就像上面那样请求服务端判定伤害。
4.客户端的输入预测和服务端的延迟补偿
将所有的逻辑放到服务器并经过服务器的模拟之后再将结果返回给客户端的过程会带来一些滞后感(A发出操作,需要经过A到服务端delta1和服务端广播传回来delta2),当玩家对操作的敏感度要求较高时,这显然不是一个很好的解决方案。因此,客户端的输入预测和服务端的延迟补偿开始得到应用。通过在客户端侧的输入预测(可以理解为发出操作命令后,服务端还没回复,但是为了防止等待的尴尬,瞎走一下,骗玩家,让他以为你已经开始动了,当收到服务端的数据后再矫正),可以让玩家的输入得到立刻的反馈。而延时补偿则保证了结果的正确性。这个过程可以基本概括为以下几个阶段:
当玩家按下按钮时,客户端立刻执行相应的操作例如开始播放某个动作或是开始移动。与此同时,客户端还会向服务器发送一条包含了时间戳的消息。
服务器经过一段延迟后收到了客户端发来的按钮被按下的消息,于是服务器会回滚到按钮被按下的时刻,在这个时刻执行按钮对应操作,之后再重新模拟到当前时刻。
之后服务器将当前的状态同步给客户端。
客户端收到服务器同步过来的数据,此时由于网络延迟的缘故,客户端收到服务器的消息时也已经过去一段时间。所以客户端同样需要回滚到服务器发出消息的时刻,并根据服务器发送的状态来修正自己的状态。
评论(1)
高深