-
Notifications
You must be signed in to change notification settings - Fork 7
Home
这个项目简单地实现了一个网络移动的同步机制。 该机制的实现思路根据手游《王者荣耀》的移动效果反推而来(不保证完全一样)。
思路介绍如下:
客户端监听用户的移动操作,把移动方向发送给服务器端。(direct)
服务器端在每个逻辑帧计算单位移动,根据客户端的移动方向更新移动速度向量,并把最新的单位位置和移动速度向量发送给客户端。(pos,v)
global pos -- 单位的位置(pos.x,pos.y)
global v -- 单位的移动速度向量(v.x,v.y)
LogicFrameUpdate(dt): -- 服务器端逻辑帧调用,比如每秒5次
pos+=v*dt -- 计算单位的移动
v=UpdateVectorFromMessage() -- 在移动结束后,对客户端的操作消息进行解析,获取到新的移动速度向量
SendMessage(pos,v) -- 把单位的位置和移动速度向量发送给客户端
基于此,在服务器端的单位移动效果大致如图:
global pos -- 单位的位置
global v -- 单位的移动速度向量
OnMessage(msg): -- 客户端收到服务器端消息的调用
pos=msg.pos -- 更新单位位置
v=msg.v -- 更新单位移动速度向量
UIFrameUpdate(dt): -- 客户端渲染帧调用
pos+=v*dt -- 单位移动
基于此,在客户端的单位移动效果大致如图:
显然,在以上机制之下:
①如果网络稳定,那么服务器端的每段移动时间和客户端的每段移动时间一样,客户端将能够看到单位连续的移动。(废话)
②如果网络不稳定,那么服务器端有可能快于或慢于客户端的展示,客户端将不会是连续的效果,而是如上图那样的间断移动,在游戏中会呈现出一跳一跳的样子。
实际上,网络不稳定导致的移动不连续,源自于单位位置突然的偏移量,因为客户端的pos直接被赋值为服务器端消息中的pos,如图中的虚线所示:
所以针对这种情况,我们将这个突然的偏移量分散到后续的若干秒或若干帧(比如120帧)中即可,不妨将该值(120帧)称作偏移量分散时间。
更改客户端逻辑如下:
global pos -- 单位的位置
global v -- 单位的移动速度向量
global offset -- 与服务器消息中的单位位置之间的偏移量
global fakeFrame -- 剩余的偏移帧数
OnMessage(msg): -- 客户端收到服务器端消息的调用
offset=msg.pos-pos -- 每次收到位置消息之后重置偏移量与剩余偏移帧数
fakeFrame=120 -- 根据偏移量分散时间120帧设置fakeFrame
v=msg.v -- 更新单位移动速度向量
UIFrameUpdate(dt): -- 客户端渲染帧调用
if fakeFrame>0:
pos+=v*dt+offset/120 -- 单位移动,包含了分散偏移量
fakeFrame--
else:
pos+=v*dt -- 单位移动,不包含分散偏移量
玩家操作->客户端帧,响应并发送消息->服务器帧,响应并发送消息->客户端帧,响应再看到移动效果
所以玩家操作感受到的延迟大约为客户端帧时间+服务器帧时间+网络往返延时
①偏移量分散时间该如何设置?这取决于对玩家体验的选择:偏移量分散时间很小时,客户端能直接准确地呈现出服务器端的单位位置,但单位移动会有肉眼可见的不流畅。而偏移量分散时间很大时,单位移动流畅,但客户端会在一段时间里呈现不精确的单位位置。
此外,也可以根据具体的偏移量和移动速度动态地设置偏移量分散时间,本机制未具体考虑。
②本机制假设了玩家的单位一直处于移动状态,而当单位停止时可能需要做些额外处理。同时,本机制未考虑单位运动的动画效果、单位转身以及镜头该如何处理。
③《王者荣耀》在若干秒内一直未收到服务器端消息的情况下,会让单位停止移动而改为原地踏步,并一直等待服务器端消息。这个等待功能涉及到具体的帧数,本机制未实现。
④本机制没有测试过TCP的丢包延迟环境。。