《网络多人游戏架构与编程》之延迟、抖动、可靠、可扩展性
/////////////////// 延 迟 ///////////////////////
虚拟现实游戏:<=20ms
格斗、射击游戏:16~150ms
RTS游戏:<=500ms
非网络延迟:
-
输入采样延迟:比如在每一帧开始时检测输入,如果在2ms按下操作,到下一帧才被检测到。可以在帧结束时再次采样。
-
渲染流水线延迟:CPU发送渲染命令后,先插入到缓冲区,GPU在将来某个时刻执行。如果有许多渲染任务,GPU渲染图像可能滞后CPU一帧。
-
多线程渲染流水线延迟:理由同上。
-
垂直同步:显示器在屏幕上更新图像是从上至下逐行更新,如果在绘制过程中变了,就会有撕裂效果。
-
显示延迟:显示器在真正显示图像之前会做一些处理,比如去隔行、降噪、自适应亮度、图像过滤等等。
-
像素响应时间:像素亮度的改变需要时间。老一点的显示器会比较明显。
网络延迟:
-
处理延迟:检查源地址和确定合适路由的时间。还包括NAT、加密时间。
-
传输延迟:向物理介质写比特流所花费的时间。
-
排队延迟:接收队列和传输队列的等待时间。//路由器仅检查数据包头部,所以发送少量大数据包代替大量小数据包可以减少排队延迟
-
传播延迟:在传播中花费的时间。//在对等网游中,匹配玩家时优先优化几何位置;在C/S游戏中,要保证游戏服务器离客户端近
/////////////////// 抖 动 ///////////////////////
定义:RTT一般围绕着一个基于平均延迟的特定值变化。这些延迟随着时间的推移会变化,导致RTT与期望值有偏差。这个偏差称为抖动。
缺点:抖动会影响RTT抑制算法,会导致数据包乱序到达。
解决:使用TCP来保证数据包按序到达;实现自定义的系统进行包重组。
/////////////////// 改进的延迟处理 ///////////////////////
保守算法:客户端作为沉默的终端,只管发送输入、接收结果、显示给用户,以服务器发出的状态为准,以延迟为代价,但至少是绝对正确的。
插值周期:客户端从旧状态插值到新状态需要的时间。
客户端插值:客户端收到一个新状态,使用本地感知过滤器根据时间平滑的插值到这个状态。
客户端预测:
-
通过推测算法,显示给玩家近似的最新状态。
-
为了推测当前状态,客户端必须能运行与服务器相同的代码。
-
为了保持近似,客户端每帧运行模拟,并显示给玩家。
-
为了执行推测,客户端首先要能粗略估计RTT,最简单的办法是服务器给数据包打上时间戳。
————————————————————————
怎样估计RTT:客户端给服务器发送一个基于客户端本地时间的时间戳数据包;服务器复制该时间戳到新的数据包并发送回客户端;客户端收到新数据包,从当前时间-旧时间戳,就得到了RTT。
航位推测法:不保守的算法,又叫乐观算法。基于实体继续做当前正在做的事情的这一假设,进行实体行为预测的过程。
本地模拟错误弥补:
-
即时状态更新:立即更新到最新状态。航位推测+最新状态来模拟额外的1/2RTT。
-
插值:三次样条插值创建路径。
-
二阶状态调整:如加速度。
/////////////////// 数据包丢失 ///////////////////////
原因:
- 不可靠的物理介质
- 不可靠的链路层
- 不可靠的网络层
解决:较少的路由器;较少的电缆;尽可能少的数据包。
/////////////////// 可靠性 ///////////////////////
TCP:优点:稳定的、可靠的、鲁棒的、有序的、拥塞控制;
缺点:
-
低优先级数据的丢失,干扰高优先级数据的接收;
-
两个单独可靠有序的数据流互相干扰;
-
过时游戏状态的重传;
也可以通过自定义UDP服务器,增加传输检验模块,来达到可靠性。
-
传输时,唯一标识数据包;
-
接收时,检查传入数据包,并发送一个确认;
-
发送端处理传入的确认,并通知依赖模块哪个数据包被接收了哪个被丢弃了。
/////////////////// 可扩展性 ///////////////////////
可见性裁剪:在渲染过程中尽可能早的剪掉不可见的对象。
静态区域:只有和玩家在同一个静态区域的对象才被认为是相关的。比如同一个地图的对象是相关的。
视锥和基于距离的系统:距离内或视锥内的对象被认为是相关的。
道路能见距离:如果服务器知道玩家的位置,那么也可以预判玩家能看见的范围。
可见集PVS:世界可以被划分为凸多边形的集合,玩家位于的凸多边形集合作为潜在可见的对象集。
层次裁剪技术:适用于树状数据结构划分的世界中的对象,方法有:二叉树、四叉树、八叉树...
服务器分区:
- 有一个主进程决定什么时候在哪台机器上创建服务器进程。
- 游戏结束时,服务器进程在退出前写任何永久性数据。
- 当玩家决定开始一个新的比赛,主进程可以确定最小负载的机器是哪台,并在这台机器上创建新的服务器进程。
实例化:一个特定区域玩家数量达到上限,再分出来第二个实例。