物理游戏的同步模型

梦想游戏人
目录:
游戏开发

该篇分析的环境是:服务器不接入物理引擎,纯物理游戏(偏向于强操作)开房间。

分析点主要是:同步模型,服务器框架,防作弊,主客户端热切换(保证游戏任何玩家掉线都不会影响游戏)

该篇记录更多的是个人思考的记录。

同步模型:

服务器不接入物理引擎的话,那么只有依赖于选举一个客户端(主客户端)来做物理处理,然后把状态同步到其他客户端,其他客户端做插值处理。(服务器也可以接入逻辑来控制取代主客户端的部分功能,甚至取消主客户端这个角色,服务器记录来自客户端的状态快照然后逻辑处理)。

因此其他客户端的“物理效果”表现力和插值算法和同步频率有关:

客户端物理表现方案1:主客户端全物理,每个玩家都是物理控制下,其他玩家吧操作发送给主客户端,然后接收来自主客户端的状态信息,然后做插值。好处是游戏控制权都在主客户端,降低了同步模型设计的难度,主客户端只需要接收其他玩家的操作信息即可,因此,其他玩家的操作,主客户端就要处理为操作状态的改变,而不是收到一个操作就处理一个操作,因为同步频率远低于游戏帧数,因此会出现操作不连贯,发生的现象比如移动一走一停,因此主客户端应该处理为输入状态的改变,比如某客户端的移动移动发送给主客户端的数据应该是开始左移动,停止左移动,而不是左移动,左移动。

客户端物理表现方案2:每个客户端的玩家自身的物理效果可以实时物理(自身是一系列的刚体的力的组合,玩家的移动操作等直接作用于自身刚体,得到更好的物理表现实时效果), 让玩家看到自己的表现很完美(有点像常规网络的客户端预处理,表现先行的思想), 然后定时把自己的状态(BodyPart的position rotation )发送给主客户端,主客户端对该玩家的信息插值后,做逻辑判定。这种方案对于所有客户端,玩家自身的物理状态都是独立控制的,自己看自己的角色物理表现,比如动作动画,很完美,只不过主客户端扮演的角色是逻辑仲裁,比如伤血,捡武器,道具产生。相对的代码实现复杂度也不高,

可以考虑服务器接入部分逻辑,来修正客户端的同步错误

对抗网络抖动& 延迟:

先用TCP快速实现功能(这种实际上是属于优化层面了,费时费力因此先实现功能为主),再用可靠UDP加速传输,在接入可靠UDP时,使用了UDT,但是发现效果不好(看了文档没发现有什么调优的方法,没继续研究下去),Android真机WIFI下,整体延时的确下降了,但是游戏体验也不是很大提高。

思路1:采用更适合的可靠UDP方案,KCP似乎是一个比UDT更好的选择,

思路2:灵感突来,发现在同步模型2下,position rotation这种同步频率很高,而且很频繁的消息时,如果发生了丢包,为了保证可靠性而带来的一个tick内收到多个包,导致一次性更新下来角色明显出现抖动之类的不平滑效果,因此考虑逻辑层选择性的丢掉一些包,用最新的包即可,在这个思路下,萌发了另外一个思路,对于这种消息可以考虑用纯UDP,丢包了无所谓,反正我也是不需要的,保证了可靠,为了不表现出抖动我也要丢掉,既然这样那为什么要保证可靠?因此出现了思路3。

思路3:消息分为3类(chanel),1保证到达,2保证按顺序到达,3无需保证可靠性,,,第三种可以完全用纯UDP实现(允许时间在tick内即可),甚至在可靠UDP上做极限优化,比如只重发一次等,,,第二种可以考虑TCP或者可靠UDP,第一种也可以是可靠UDP的不保证顺序模式,,,因此一次游戏,会有多个socket,各自负责自己的消息Chanel,甚至第三种消息可以用客户端P2P来直发(减少服务器中转后丢包的可能性)。。第一种消息可以是(掉血,道具生成,子弹发射),第二种消息可以是(逻辑前后有强顺序性的消息,),第三种消息可以是(位置,旋转)等。在深入思考,其实这种方案就类似于raknet发送的模式,有好几个选项,。一开始就想接这个方案,无奈Android IOS接入到C#遇到一些问题,绕了一大圈,好像又回到这种方案了。

个人最理想的方案是(asio+kcp)or(go+kcp)(协程很好用啊)组合,加入消息分类策略,如果效果还是不够好,在考虑P2P,毕竟P2P也有一定的问题,打洞(NAT穿透)不一定能100% OK,因此需要服务器中转,为那些不能打洞的客户端服务。可能会丧失服务器验证功能,

服务器架构:

游戏分为战斗部分和非战斗部分,战斗的消息同步,应该考虑用可靠UDP实现基本协议,非战斗同步消息,常规手法TCP(RPC or 消息)都可以。

游戏逻辑上2大块,非战斗和战斗部分,

非战斗:非战斗主要是匹配,商城,好友,任务等常规需求,并且一个玩家大部分游戏时间不不会是非战斗。因此常规RPC or 消息模型即可。称为LogicServer,该类型服务器较为综合和常规

战斗:战斗涉及到大量消息转发,主要负载是流量,

防作弊:

1。服务器在战斗同步上扮演的角色只是中间件,只负责转发消息,对于防作弊手段可以是这样,所有客户端都是一个仲裁者,负责对一些逻辑上限进行仲裁和有效验证,比如瞬移,无限产生道具,秒杀,这种极度极端的网络消息,可以进行验证,但是这种防止不了所有客户端都作弊,并且还存在恶意攻击,让其他客户端是别人作弊(类似于GTA5的某玩家开作弊让服务器认为是你在作弊),这种防作弊机制就提供了这种的可能性,

2。在1的基础上吧这部分的验证代码移至服务器,即服务器也是一个客户端,进行消息仲裁,为了节省开发时间,可以接入C#的客户端代码用mono来跑,也就是说客户端也可以专门针对这种验证写一个模块,服务器和客户端公用,也可以只放在服务器(要把物理引擎去掉),但是实现都是让客户端来实现,来防作弊。基本模式是一个房间转发的同时做消息合法性防作弊验证。这种模式意味着客户端游戏全局的状态会分为2大块,物理输出和状态描述(可以只是对象类型和position)。验证模式下只需要有状态描述这个集合即可,无需要物理输出,状态描述可以是状态快照也是物理输出的快照,一个新的问题又来了,这份状态描述信任那哪个客户端的?都有可能作弊,那么作为作弊验证的依据状态描述部分,该信任谁?或者信任一部分客户端?。。

3。针对2的问题,进一步优化,其实服务器可以接入部分逻辑的,只不过吧客户端的mono代码去掉物理引擎,然后逻辑一样是在C#层跑,比如什么时候该产生一个道具(不能无限产生道具),扣血的合法性不能秒杀(),位移的合法性(不能瞬移),由服务器告知主客户端,非法的消息,服务器直接丢掉,或者认为是作弊。主客户端不唯一这也可以是一种方案,即主客户端会随机切换,比如该局是玩家1,下一局可能是玩家2,甚至一局中也切换, // TODO

///TODO

Scroll Up