I. 教程案例框架描述

该套教程做了一个简单的汽车控制系统,没有用到物理模拟。用油门和方向控制汽车的加速度和转向,同时还有一些空气阻力和滚动摩擦力的设置增加了真实感。汽车的位置是通过加速度和时间等计算出来的。

关键的参数包括:加速度,速度,质量,最大驱动力,最小转弯半径等。

详细的计算方式就不细说了,重点是没有用到物理模拟。

II. 联网游戏要面对的两大问题,以及模拟这些困难的方法

1. 网络更新频率

在每一个Actor中都能设置其网络更新(Replicate)的频率(前提是该Actor需要Replicate)。

具体方法是在BeginPlay中加入以下语句:

    if (HasAuthority()){NetUpdateFrequency = 1;//默认值是10}

(注意NetUpdateFrequency是针对属性的Replicate的,和RPC无关)

这个频率和游戏运行帧率相比较来说要低很多。尽管这个值可以手动设置的更高,但是更新的越频繁,给网络带来的负担越大,在多人联网游戏中必须合理分配带宽,而不能无限制地提高某一个actor的数据传输量。

在一个对操作比较敏感的游戏中(例如极品飞车)如果不做任何处理,仅依靠0.1秒一次的更新是无法保证把本地的游戏对象的运动完美复制到另外一端的。玩家的操作是会随时发生很细腻的变化的,仅0.1秒更新一次输入会产生很不连续的跳动,其对操控对象造成的影响会产生更大的偏差,多种因素(例如油门和转向)造成的误差累积起来会相差很远。

正因为要克服这个问题,所以在开发时需要刻意将问题“夸大”,模拟这个困难,把更新频率调低,比如设置成1次/秒,更容易看出采取一些方法前后的效果差别。

2. 网络延迟

一方发出的操作指令通过路由到达另外一方需要一定的时间,这个时间就是网络延迟,通常以毫秒记。如果延迟过大,而不做任何处理,会看到操作对象有明显的“跳跃”。

延迟是网络环境决定的,在代码上无法避免延迟,但是我们可以做到让操作对象平滑地移动,虽然整体迟一些,但仍是连贯的。

为了夸大这个问题,我们可以输入以下console命令模拟网络延迟:Net PktLag=xxxx。 该值的单位是毫秒,例如我们可以设置为1000来模拟一秒钟的网络延迟。

III. Actor Role

教程中的一幅图对Actor Role总结的很好

图中上方绿色的框表示服务端,下方蓝色和红色的框分别表示两个客户端。

绿色方块代表一些在服务端控制其移动的对象,例如Listen Server自己控制的Pawn,或者移动的平台、机关等。

蓝色小人表示蓝色客户端的pawn,红色小人表示红色客户端的pawn。

IV. 三种Actor Role的同步方案

为了解决后面的一系列问题,将“输入”和“状态”分别进行封装。

“输入”封装为Move,包括油门数值、转向数值、DeltaTime、时间标签。

“状态”封装为State,包括Transform,速度,最后的输入。

这里只进行方法上的描述,具体代码就不列出了。

1. Authority

服务端所有的Actor都是Authority。自己控制移动的Actor当然是Authority,而其他玩家的Pawn的控制也是提交到服务端进行计算后再次同步给所有客户端的,而且服务器计算出来的就是标准,所以也称为Authority。

服务端自己控制的Actor自然可以在服务端本地让其平滑移动。但这个Actor在客户端怎么移动呢?实际上它在客户端的Role(即Remote Role)就变成了Simulated Proxy。

具体情况和处理方法见后面的Simulated Proxy。

2.Autonomous Proxy

客户端自己控制的Actor是Autonomous proxy(自治代理)。

Auonomouse Proxy它可以首先获取输入,所以在本地就可以平滑地模拟移动,然后在Tick中,创建一个Move,通过一个Reliable 的RPC函数SendMove将Move提交到服务端运行。由于是在Tick中,而且是Reliable函数,所以在服务端执行的频率和Tick是一致的,这是唯一比较耗费带宽的操作,带来的好处就是服务端也会平滑精确地响应其输入。

在该RPC函数中,会改变一个ServerState属性,是前面封装的State类型结构体。

而ServerState属性是一个OnRep函数,每次更新它,就会在客户端触发另外一个函数OnRep_ServerState(),在这个函数中覆盖客户端自己计算的汽车状态。

需要注意的是ServerState是属性,它的Replicate的频率并不是每个Tick一次,而是使用网络更新频率。虽然频率不高,但是在网络延迟不严重的情况下,服务端和客户端计算出来的位置应该不会有太大差别,所以这个覆盖行为也不会让玩家有明显的感觉。

但是如果考虑到网络延迟,上述方法仍不能完美应对。

可以手动将网络延迟模拟为1秒,就会明显感觉到车辆在移动和转弯时频繁地跳回之前的某个状态,非常难以控制,而且根本谈不上平滑。

原因也很好理解,因为根据上述方法的描述,服务端会定期覆盖本地的车辆状态,而因为网络延迟的原因,服务端的“反应”总是慢半拍。比如客户端车辆已经启动,并且走了一段距离了,服务端的车辆才刚刚启动,那么在同步时,服务端的车辆位置和客户端的车辆位置有一定的差距,生硬的去覆盖当然会产生一个跳跃,并且这个问题会持续下去,以至于根本无法进行正常的操作。

我们虽然无法避免网络延迟,但是我们有办法将操作变得平滑,不再跳跃。教程称之为:“Keeping ahead of the server”,但我宁愿称之为“缓存操作”。方法概述如下:

在客户端本地的Tick中创建Move(和之前是一样的),然后将Move缓存入一个数组(不同的地方),然后依照前面的方法,本地模拟,再通过RPC在服务端模拟。在同步ServerState时多做一些事情:在Autonomous Proxy中对比ServerState的LastMove中的时间标签和前面缓存的Move数组,时间早于LastMove的都清理掉(因为这些在服务端已经得到了执行)。然后通过一个For循环在本地瞬间模拟剩下的未执行的Move数组。

对比这个操作和直接生硬的从服务端往客户端覆盖,就会发现这个操作实际上把服务端还没接收到的一系列操作在本地瞬间重演了,并在服务端的结果上进行偏移给与客户端,这样虽然增加了计算量,但是保证了本地运动是平滑的,同时也最大程度的保证了服务器的权威性——因为仍是在服务器计算的结果之上进行的偏移。

3. Simulated Proxy

Simulated Proxy只存在于客户端(因为服务端的都是Authority),它无法受到自己控制,是从服务端同步来的(不管是谁控制的,可能是其他客户端,也可能是直接由服务器控制),所以叫做“模拟代理”。

方法1:

简单粗暴的做法是让其同步移动。

C++的做法是在构造函数中加入 bReplicateMovement = true;

蓝图中是在属性中搜索ReplicateMovement,将其打钩。

这样虽然可以同步其移动,但是因为同步频率的问题,在客户端会看到类似定格动画的移动,很不平滑。

方法2:

注意该方法需要设置bReplicateMovement=false。不让Movement自动同步,而是手动处理。

正常的处理方法是滞后一次更新,对物体位置进行Linear Interpolation,对旋转进行Slerp。这样Simulated Proxy的移动虽然会慢一点,但是好处是移动会平滑很多。

方法3:

注意该方法需要设置bReplicateMovement=false。不让Movement自动同步,而是手动处理。

更好的办法是利用Hermite Cubic Spline Interpolation。

重点公式:

Derivative(曲线斜率)= DeltaLocation/DeltaAlpha
Velocity (速度)= DeltaLocation/DeltaTime
DeltaAlpha = DeltaTime/TimeBetweenLastUpdates(两个点之间的时间差)
推倒得出:
Derivative = Velocity * TimeBetweenLastUpdates
注意虚幻里的速度默认单位是m/s,而位置的单位是cm,所以在速度转换为位置的时候一定要记得*100。

虚幻里提供了两个函数可以帮助我们模拟Hermite Cubic Spline,分别是:

FMath::CubicInterp()

FMath::CubicInterpDerivative()。

前者用来求插值的位置,后者用来求插值的速度,具体用法如下:

FVector NewLocation = FMath::CubicInterp(初始位置,初始Derivative,目标位置,目标Derivative,比例);
FVector NewDerivative = FMath::CubicInterpDerivative(初始位置,初始Derivative,目标位置,目标Derivative,比例);

求出的NewDerivative转换成速度也很简单,直接除以(TimeBetweenLastUpdates*100)即可。

但是如果讲Velocity的方向设置为旋转方向,倒车时候就会瞬间调转车头,这是我们不希望看到的。所以旋转上最好还是结合第二种方法来做。总之旋转上没有非常好的平滑方法。

需要知道,上述这写方法都是针对非常低的同步频率(测试使用的是1次/秒)来处理的,在正常同步频率下(10次/秒)的表现还都是非常好的。

转载于:https://www.cnblogs.com/AnKen/p/8318627.html

Online Game Development in C++ 第五部分总结相关推荐

  1. 五种 Ajax 反模式:避免常见的 Ajax 代码陷阱!

      developerWorks 中国  >  XML | Web development  > Ajax 和 XML: 五种 Ajax 反模式 避免常见的 Ajax 代码陷阱 文档选项 ...

  2. 当对项目强名时自动构建失败(TeamFoudationServer试用笔记)

    当对项目强名时(带密码保护)自动构建失败 原因为弹出密码输入框的造成自动构建任务不能继续! 临时解决办法为去掉密码保护,目前还没有更好的解决办法. 最近微软的TeamFoundationServer中 ...

  3. 一次安装tengine的经历

    1.Tengine介绍及安装说明 Tengine是由淘宝网发起的Web服务器项目.它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性.实验环境说明: [root@localhos ...

  4. java基础 DK JRE JVM 关系 JDK 下载和安装

    一 JDK JRE JVM 关系 二 JDK 下载和安装 下载JDK7 JDK的全称是JavaSE Development Kit,即java开发工具包,是sun公司提供的一套用于开发java应用程序 ...

  5. 3.1_2 JavaSE入门 P1 【Java基础】Java语言概述、JDK编译

    相关链接 Excel目录 目录 Part1 Java语言概述 1 Java语言概述 1.1 Java发展史 1.2 java应用平台 1.3 跨平台原理 1.4 JVM JRE JDK 1.5 Ora ...

  6. 【课程设计】UWP 开发入门小笔记(1)

    UWP 开发入门小笔记(1) 零.介绍 一.系列介绍[p1] 二.创建第一个属于自己的UWP应用[p2] (一)一个button的属性: (二)修改属性的三种方法 (三)字号(以后会细讲) 三.UWP ...

  7. Trello进行时间和项目的管理

    这一年来不知不觉中学习和使用了不少时间和项目管理的软件和方法.在年末的时候收获了的Trello这个在线白板觉得挺适合自己所以拿出来分享下.对于它的喜爱可以从几个工具说起: 番茄工作法 现在在工作或学习 ...

  8. 第七章、网络安全与主机基本防护: 限制端口, 网络升级与 SELinux

    通过第一篇的锻炼之后,现在你应该已经利用 Linux 连上 Internet 了.但是你的 Linux 现在恐怕还是不怎么安全的. 因此,在开始服务器设定之前,我们必须要让你的系统强壮些!以避免被恶意 ...

  9. 鸟哥的Linux私房菜(服务器)- 第七章、网络安全与主机基本防护: 限制端口, 网络升级与 SELinux

    第七章.网络安全与主机基本防护: 限制端口, 网络升级与 SELinux 最近更新日期:2011/07/21 通过第一篇的锻炼之后,现在你应该已经利用 Linux 连上 Internet 了.但是你的 ...

最新文章

  1. weibo4j中的 jar解释
  2. linux mount 内核镜像,在Linux live system中创建loop设备并挂载镜像文件
  3. Springboot验证表单数据和自定义验证
  4. 反思代码优化点:trycatch 验证类 封装用户变量
  5. 笨兔兔的故事——带你了解Ubuntu,了解Linux 杀毒 第十八章
  6. Python基础学习2--字符串
  7. IDEA快速升级模块版本号
  8. Android 反编译修改源码
  9. Android SVG矢量图/矢量动画、Vector和VectorDrawable矢量图及动画,减少App Size
  10. Java10-I/O
  11. 程序员你可以考虑安装的15款谷歌插件
  12. 第十三届蓝桥杯大赛软件赛省赛(Java 大学A组)
  13. latex 调整图片大小
  14. 云计算、社交网络和移动互联网
  15. Week-4-作业1
  16. 论文阅读(3):Image-Based 3D Object Reconstruction:State-of-the-Art and Trends in the Deep Learning Era
  17. 任正非首谈接班人制度
  18. [Windows实用软件推荐:1]本地搜索工具Everything
  19. 最伟大的程序员Richard Stallman主页
  20. Go实现的一站式云原生机器学习平台 | Gopher Daily (2020.12.08) ʕ◔ϖ◔ʔ

热门文章

  1. 【内推】字节跳动-头条番茄小说客户端
  2. double转int类型
  3. STM32F103最小板完成对SD卡的数据读取(fat文件模式)
  4. 用 KNN 对 IBM员工进行离职预测
  5. Qt基础之十:使用QVariant存储自定义类型
  6. Esper学习 -- 第一章 Esper初体验
  7. N97 必备软件,上网必装!UC浏览器 完美支持97触控大屏幕!
  8. 从Python角度理解mutex
  9. 从职业经理人到创业者,她创办公司估值达2亿,看她是如何转变的
  10. [3]传奇3服务器源码分析一 DBServer