unreal4引擎程序架构 - CSDN
精华内容
参与话题
  • 知乎用户,Machinist of Games 知乎用户、妍青、莱牧 等人赞同 ...这是一个很难的问题,而且不容易回答,很容易...一、关于Unreal4和Unity 很不幸,我并没有看过Unity代码,我们没有购买,而我也并不是特别

    知乎用户,Machinist of Games

    知乎用户、妍青莱牧 等人赞同
    谢邀。

    这是一个很难的问题,而且不容易回答,很容易引起争论,老实说我并不想在公开场合评论到底哪个更好或者更坏,这并不明智,其实每个人心底都有自己的答案。

    我只想聊一些我的看法。

    一、关于Unreal4和Unity

    很不幸,我并没有看过Unity代码,我们没有购买,而我也并不是特别想看。或许有人说:装!嗯,其实写了很多年代码了,什么没见过?看过并不一定能写出那样的产品,没看过也不代表你不能写出超越它的代码。很多人对待引擎代码的态度其实和追女孩差不多,没追到的时候,天天时时刻刻想着,到手了,就呆在硬盘里,其实真正每个文件都读过的人,凤毛麟角,读懂的人可能更少。

    在刚开始学习游戏引擎的时候,需要读一定数量的代码,但积累了一定的代码量和经验之后,需要的更多是观察、思考、总结。所以比较资深的程序员,会花更大量的时间思考,而不是学(chao)习(xi)别人的代码。

    回到这两者的对比上,我没看过,所以就不知道怎么对比了,只能说各有各的好,咸鱼青菜各有所爱,而且游戏界的金科玉律就是,谁能出产品,谁能大卖,谁就是赢家。

    至今Unreal3已经被证明了是一个伟大的引擎,有足够的title说明问题了。Unity有不少作品,但还缺乏一锤定音的作品,和AAA不沾边有关系吧。但用户足够多,增长率高也可以说明问题。Unreal4还欠缺证明自己的作品,等战争机器吧,Epic还需要向别人演示怎么使用这个引擎。

    二、关于题主提出的几个对比

    很直接说一句,那都不是什么问题,或者可以说,根本不是比较的重点。Unity那种写法,上世纪就已经有人在用,不见得是什么高明的写法,这种c/c++的奇技淫巧,只有初学者或者刚刚学会一点儿的人会感兴趣。我顺手可以拈来好多类似的,比如gcc的tree node结构,初读感觉那个精妙,后来发现也就那样儿。这种union我大概10年前在写高速raytrace引擎的时候已经用过,而且用在xmm上面,即现在DirectXMath的写法。至于你说不明白为什么Unreal这么写,看不懂if。为什么?很简单啊,因为这是“历史问题”。那个年代过来的代码都是这么写的,就一直这么写了。

    很多人搞错了一点,以为数学库要效率高,这也是我刚开始写引擎和图形程序时候的错觉。后来发现,不是的,游戏引擎的数学库,不是全都要求效率高的,数学库最重要的是“稳定”。因为真正在跑的,AAA游戏,或者重度的MMORPG,最重要的,不是数学运算,而是“架构”。反而数学运算,需要稳。稳到什么程度?havok曾经推销他们的物理引擎,最引以为傲的的,并不是它有多快,而是它的所有物理运算在所有平台上的计算结果都完全一致(评论有朋友修正了这个说法,应该是同样的计算在同一平台上每次计算都一样)。这简直吊炸天啊有木有!!!如果你不明白这句话背后的意义,那么我想你没必要讨论数学库了。

    回到问题本身,一个字,编译器优化,可以回答一切问题。

    现代游戏引擎的瓶颈,有两处,一处是runtime的执行效率,一处是content creation的pipeline生产效率。后者太庞大太宽泛,没实战过的人,不理解,这里不展开细说,只讨论第一点,runtime。

    runtime部分,现代引擎面对的问题,主要是越来越复杂的场景,越来越多的drawcall,越来越重的资源。这里面最麻烦的就是提交。所以新一代的API,都注重提高提交效率,即更薄的driver层。这就是DX12标榜的十万drawcall的意义。

    这里我必须提一点,这两个引擎,Unreal4和Unity,都有一个架构上的严重欠缺,就是没有从原生上支持多线程。即使Unity5,也只是用了一种thread pool的分发的策略,把一部分集中的繁重的事务分发到其它线程计算,但它并不是真正的“原生多线程”,即任意的不相关的任务都可以随意分发到不同的线程上。考虑每个entity自己更新自己的逻辑,可以并行,有100核心就可以几乎真的并行更新100个entity,这才是真正的原生多线程。

    这种引擎,目前公开的只有Frostbite 3是这么做的。(当然我不会告诉你,我们……)。

    至于Component架构,这方面我有另一种看法。游戏引擎是一个很重度的工程项目,而非科研项目,干净与否并不能成为评判标准,实用性才是金科玉律。我认为unreal这么做(把业务相关的东西放到底层),是基于一种发展的眼光看。很简单,我也会这么做,如果我开发了一套非常超前的架构,但我又对它没有足够的底,不知道接下来应该怎么写,我认为总体方向是对的,但我不清楚业务和他如何结合,那么我就采取折中的方式来处理,即把一部分业务嵌到架构中,先把业务应用起来,验证架构的正确性,然后通过重构和迭代,逐步把业务细节划分出去。

    显然UObject是继承Unreal3而来的,Unreal4非常大胆,blueprint是一种非常超前的想法,而且非常有前途,也非常好,但这个改变太庞大了,太重了,要一下子割裂和Unreal3的关系是不切实际的,稳妥的方式是逐步修改过来,并且验证之后再完全迁移过去。所以迁入一部分实际业务的想法很现实。而且要考虑,引擎开发本身应该依附具体项目。我认为Unreal4应该依附在战争机器游戏本身的开发身上,才可以保证它不会走弯路。所以在底层重度嵌入一部分FPS相关代码,无可厚非也没有任何问题。

    老实说,我有代码洁癖,但我也会这么做,因为引擎不是独立产品,是依附游戏的附带产物,一切应该以游戏业务为优先。这也是为什么我一直对Unity有一些负面看法的原因,它的开发商本身并没有任何游戏,一切反馈是来自于用户,二手数据。所以Unity看起来似乎很“干净”,但就是太干净,干净得出奇,变得不接地气,这才是为什么它缺乏AAA。

    三、渲染质量之谈

    题主似乎不断在强调不要被Unreal4的渲染质量吓唬住了,我觉得这句话是很有问题的,直接地说,这句话是不专业的,一点都不professional的。显然题主是有倾向性的,因为Unity本身就是一直被Unreal4的渲染所吓唬住了,所以每个版本的升级都在重点强调自己的渲染特性得到了提升。

    老实说,这并不明智。因为Unity的提交效率。。。

    上面提到了一点,现代引擎的瓶颈一个很重要的点就在于渲染批次的提交上面。这里请看我一直所赞誉的Frostbite 3,的数据——BF4一些场景的DP数量达到了5000~10000。吊炸天啊!这需要非常高的提交效率,非常优异的渲染组织。这就是引擎真正的架构技术核心所在。另一个碉堡了的是CryEngine,提交效率很高。

    所谓“渲染质量”是什么鬼东西?搞了差不多二十年图形,我很少听到专业人士提到这个词。我听过渲染效率,听过画面质量,听过各种shading model。同一张显卡,同样的shading model如果还用同样的模型,同样的算法,有什么质量不质量的?难道Unreal4算的 1 + 1 = 2 比 Unity 算的 1 + 1 = 2 质量要高?那个 2 要更好看?

    不是的,这是业余的看法。专业的看法是,你要对画面有取舍,有调控。引擎是一个大的系统,系统设计最重要的一环是控制和分配。图形学没什么算法是不公开的,Unreal4用到的所有算法都是公开的,所有的siggraph paper你都能access到,没什么秘方,没什么magic code。关键是,你的取舍,你花多少资源在哪个部分,省了哪个地方的东西。

    这部分的功力,是源自于游戏开发本身,源自于积累,源自于TA。这也是Unity欠缺的地方,因为Epic自己开发游戏。所以Epic在资源的调配上,有取舍,有经验,在开发战争机器的时候,在开发游戏的时候,有各种纠结,填了不少坑。写好代码只是开始,教会TA和美术使用引擎、正确使用你写的技术、配合并创造出美丽的画面,才是真正有绝对价值和意义的工作,这占了99%。

    图形学是工程的图形学,是trick和cheat的集合。正如你堆钱买奢侈品是不能除掉身上浓浓的杀马特山寨风的,必须读书、读书、读书、思考、思考、思考、沉淀、沉淀、沉淀。光靠堆一些feature,不是正确的姿势。这就是为什么Unity 5堆了那么多feature,看起来还是远不如Elemental Demo。

    四、双生

    对比Unreal、Unity,同样的例子,请看CGFX领域的MetalImage和Pixar。MentalRay非常屌,有很多先进设计,一直坚持用raytrace,早就开发出电影质量的全局光渲染,但它一直被死对头Pixar的RenderMan所压制。Pixar的RenderMan,采用“落后的”渲染算法REYES,直到13.0版本之前一直不支持raytrace,且价格昂贵,更新缓慢,但广受电影制作喜爱,上世纪好莱坞特效渲染标配渲染器。为什么?很简单,Pixar自己是做电影的,RenderMan本身一直为Pixar的电影服务,获得各种第一手电影制作需求。而反观MentalImage一直只是一家软件公司,没有电影业务,只卖软件,只卖技术,所以无论改进得多好,一直不懂电影制作者的心。

    我对Unreal和Unity的看法,也基本基于此案例。当然,现在disney有更强的渲染器,可以完全干死RenderMan,不过这货是不卖的,正如Frostbite,也是不卖的。世界上最好的引擎技术,不会出现在商业引擎里面,只会在In house engine中。

    五、寄望

    我聊了许多,然而这并没有什么卵用,工作中该用哪款的,还是要用哪款,这不是技术人员能够决定的。不过我一直以来的信念,都是我们始终是要做自己的引擎的,并不是为国争光什么的狗屁高大上理由。很简单,技术人员存在的意义,就在于用技术碾压对手。你的技术,到底是用在给别人打补丁上,还是给自己充实力量上?

    所以我对所有这些第三方引擎的态度,都是批判地学习。好的,我兹词,不好的,我谨记并绕开。其它不参合任何个人感情,也不需要卖任何情怀,只需要记住,所有的这些——Unreal、Unity、CryEngine、Frostbite、SnowDrop、Fox等等等等,都是他人的嫁衣裳,我们真正需要的,是自己的遮羞布。

    题主的批判态度,已经带有点私人感情了,这并不理智,希望你可以成长,客观看待这些东西。

    ————————————————————

    补充一点,题主说Unreal4不如ogre,我觉得这真有点太过了。ogre实在是渣渣渣渣渣。不多说,自己领悟吧。

    ————————————————————

    再最后补充一点,评判这些引擎的时候,如果你不是对自己的实力有足够的把握,如果你不是很清楚到底自己是否真的彻底了解这些设计背后的原本意义,那么千万千万千万别开上帝视角

    要很清楚这些引擎是世界最顶尖的脑袋架构出来的,一些显而易见的问题,比如用if是否很2b,难道你以为可以架构出blueprint、能够做到c++hot load这种吊炸天的人,他们想不到?

    不可能的事情!去看看Unreal4的Multicast delegate怎么设计,怎么构建,C++用的多好,多纯熟,对语言的压榨多极致,能够有这种把握深度的人,会不知道用书本上写的条条框框“千万别用if啊”什么的?不可能的事情!

    Tim Sweeney这些人的脑袋太好用了,太厉害了。比如Component的结构,坑超级多,你想到的,别人不会没想到。我遇到这种情况,不会第一时间想别人是否2b了,而会先考虑、是否自己没想到更深刻的问题?或许还有其他坑?就连Voxel Cone Tracing这种屌炸天的算法都能想到,不可能想不到你看出来的小问题。

    所以,每当要开启上帝视角,请换一下角度,如果是你,你怎么设计,你怎么处理,真的就没有问题?如果你真的设计的比他们好,请别犹豫,站出来,战胜他们。编辑于 2015-06-08
     42 条评论 感谢 
    分享
     收藏  没有帮助  
    举报
      禁止转载
    展开全文
  • 作者 |Jerish来源 | 游戏开发那些事我从16年开始接触 Unreal,到如今已经4年了。最近看了不少关于网络同步的论文和书籍,总算是理解了 Doom 和 Quake 这种古董级...

    作者 | Jerish

    来源 | 游戏开发那些事

    我从16年开始接触 Unreal,到如今已经4年了。最近看了不少关于网络同步的论文和书籍,总算是理解了 Doom 和 Quake 这种古董级游戏的发展历史,对其网络架构也有了更深一层的认识。这次想根据自己的工作和学习经验,以一个全局的视角来重新回顾一下虚幻的网络模块,并总结一些我们常见的问题,相信对 UE 同步细节模糊不清的你看完后一定会醍醐灌顶。

    开始前,我先给初学者一个建议。如果你打算看UE4的同步源码,最好先大致阅读一遍这本书——《网络多人游戏架构与编程》,里面基本涵盖了UE4同步框架的大部分内容,可以让你少走不少弯路。

    下面进入正题:

    网络同步,就是使各个客户端上的角色表现保持一致,属于游戏引擎的高级功能,所以一般我们都将其归类于Gameplay模块当中。不过具体的实现方案其实会深刻影响到底层的网络架构(甚至是整个游戏架构)。我们既要决定通过哪种网络协议来完成,又要决定游戏各个模块的循环执行顺序,这已经不单单是 “Gameplay” 层面的东西了。

    虚幻引擎属于标准的 CS 架构(经过无数次改版的),内置状态同步功能,其同步频率与游戏的帧率相同,属于变长步更新。由于帧率完全受 CPU、GPU 性能的影响,所以网络同步的频率与整个项目的性能息息相关。不过,有一点我们要认识到,unreal 已经是尽可能的按照自己最快的速度进行数据的发送与接收了,只要我们做好各方面的性能优化即可。

    RPC与属性同步

    在 Unreal 里面,同步有两种手段,即 RPC 与属性同步(很多服务器引擎都是如此)。与其说 RPC 是同步手段,不如说他是一种传输数据的方式,好处就是可以直接通过类的函数形式书写,方便理解。同时不需要你直接写Socket,也不需要你处理封包和拆包。在计算机网络的概念里面,RPC 叫做“远程过程调用”,本质上就是一种传递数据的手段,而其实现方式既可以是应用层的 Http,也可以是传输层的 TCP/UDP。

    在虚幻里面,由于很多游戏的同步(比如 FPS)对网络延迟要求比较苛刻,所以我们放弃了需要三次握手的 TC P而改用UDP(更不可能考虑 HTTP了)。RPC 既可以标记为可靠,也可以标记为不可靠。

    可靠的RPC最终一定会到达目标终端,但不可靠的 RPC 除了在网络拥挤的环境下丢失,也可能在引擎限流的情况下被提前拦住。RPC本身并不是一个可以持续存在的对象,我们只能通过 RPC 参数“一次性”的将数据从一端发送到另一端,所以每个 RPC 调用只能“只执行一次”(换句话说,他的生命周期只有一瞬间)

    如果 RPC 消息从网络中丢失,那么他就会永久的丢失(这里指不可靠的 RPC),所以并不适合游戏世界各种对象的状态恢复,必须要结合可以保持对象状态的属性才行。此外,UE4 里面 RPC 并不支持回调,所有RPC函数的返回类型都是 void。

    属性同步,本质上属于一个比较上层的功能特性,是以每个对象为单位处理的(不支持更细粒度的同步,但理论上可以通过条件属性做部分调整,详见AACtor::PreReplicate)。unreal 的服务器会按照一定频率的去执行同步对象属性的数据发送和接收,同时处理回调函数。

    属性同步的产生是为了维持对象的状态,是一个从概念上非常贴近“同步”二字的功能,一旦服务器上的同步属性发生了变化,就一定会发送给客户端(注意:属性同步只是服务器向客户端的同步,不存在客户端向服务器流通),也许中间会丢包会延迟( actor 首次同步时是 reliable 的),但是其内置的机制会保证属性的值最终送达到客户端。借用一句经典的话来说就是,同步数据也许会迟到,但是永远不会缺席

    无论是 RPC,还是属性同步,你会发现他都是基于 UObject 的,或者更确切的讲都是基于 Actor 的(以及其附属组件)。因为这两种功能一个是利用类中的函数,另一个是利用类对象的属性,他们都需要与某一个具体的对象作为媒介,而在 UE 的架构中,设计都是面向对象的,每个 Actor 都可以理解为游戏世界的对象。

    既然是基于 Actor 的,那么整个同步就与 GamePlay 框架紧密相连。由于我们在发送同步数据的时候需要知道这个数据应该发向哪个客户端,而客户端与服务器的链接信息(IP等)又在 Playercontroller 里面,所以同步的逻辑与 playercontroller 密切相关。

    很多刚接触 unreal 的朋友经常会遇到RPC 数据发不出去或者收不到的问题,就是没有认识到playercontroller其实是包含客户端与服务器的连接信息的。最典型的,假如你有服务器上连着10个玩家客户端,服务器上有一辆车,让他执行Client RPC,他怎么知道发给哪个客户端?当然是通过这个车找到控制他的playercontroller,然后找到对应客户端的IP,如果这个车不被任何客户端控制,那他就不知道要发给谁。

    当然,RPC与属性同步的实现原理不同也决定了他们有很多差异。由于属性同步是跟着每一个实例对象走的,所以不存在“随用随发”。也就是说,属性同步需要在每帧特定的时机通过统一的引擎接口写到发送缓存(sendbuffer)里面。

    这样带来的问题就是,你在同一帧里面修改的属性只有最后的那个值会传到客户端那里,进而导致你的回调函数也只会执行一次。而RPC不同,每次执行时都会立刻将数据塞到发送缓存里面,从而保证不会丢失任何一次RPC的调用(假如RPC是可靠的)。

    另外,这里面还有一个深坑,就是关于Actor以及Component的同步顺序问题。一个对象的同步首先要给客户端上的对象与服务器上的对象建立关联,这样服务器的A变化了才会告诉客户端上的A也去变化。但是A是一个对象,对象也是需要同步的,一个场景里面有那么多的对象,同步肯定是按顺序的来的。

    这样就会经常出现A的对象里面有很多指向B对象的同步指针属性,但是A对象出现的时候B还没同步过来,所以在A的Beginplay里面访问B是不行的。那么如何解决这个问题?答案是用属性回调,一旦执行了属性回调,就可以确保A的B指针是存在的。

    不过,属性回调并不能解决所有问题。假如B对象还有C对象的指针,回调的时候C还没同步过来,你想用B去访问C发现又是空指针。这问题目前在现在的虚幻引擎里面还没有完美的解决方案,所以我们要尽可能的避免这种情况(我本人正在尝试实现一些可行的方法)。类似引发的更细节的问题还有很多,后面我会列举一些。

    移动同步理解

    两种同步手段已经介绍完毕,我们现在把视角锁定在网络同步的解决方案上。游戏中同步本质上是同步客户端之间的表现,而RPC与属性同步都只是数据上的同步,我们需要将其与画面表现结合起来。画面表现说白了就是物体的显示与隐藏、动画、位置等,其中位置同步就是最复杂的一项,因为游戏中的角色可能是每帧都在移动的,移动组件(movementcomponent)就是为了解决这个问题而诞生的。

    移动组件很复杂,他需要考虑到各种情况的延迟、抖动,需要解决不同客户端不同角色的流畅性问题,需要实现各种插值手段。在网络同步中,始终存在三种形式的角色,分别是本地玩家控制的、服务器控制的以及其他玩家控制的,在unreal中分别对应着Autonomous、Authority与Simulate。这三种类型的存在本质上代表着角色的控制者是谁(哪个端可以直接通过命令操作他),而从另一个角度讲这种分类其实是代表着玩家的操作是否有网络延迟以及延迟的大小。

    对于本地控制的Autonomous角色,他可以在本地直接响应你的操作,如果想把操作发给服务器,则需要经历一个client——server的延迟,而服务器想把这个操作同步给其他客户端又需要一个server——client的延迟。

    同步中最难的其实就是如何有效的对抗这种延迟。所以,会诞生诸如延迟补偿这种同步策略,即本地客户端收到其他客户端消息的时候将本地的所有角色回滚到【当前时间 - 网络延迟时间】时的位置再进行消息的处理和计算。

    (UE4默认引擎里面没有这种操作,虚幻竞技场里面有。如下图,红色是当前端的具体位置,黄色是回滚预测的位置)。

    移动组件本地客户端到服务器采用的是不可靠的RPC,而服务器到其他客户端采用的是属性同步。为什么使用RPC?因为客户端向服务器发送消息只能通过RPC,属性同步只是用来服务器同步给客户端用的。

    unreal在同步位置时记录了各个客户端以及服务器的时间戳,通过位置buffer缓存、每帧不停的发送位置、判断时间戳调整位置与回滚等操作实现比较理想的效果,本质上守望先锋的帧同步+状态同步是相同的。不过虚幻并没有采用ECS,并不能在架构上很好的支持所有逻辑的回滚。

    网络同步发展至今,其实基本已经成型。从早期的Lockstep到指令流水线化再到预测回滚TimeWarp,大体的同步优化手段都是这些,现在的趋势就是状态同步与帧同步里的各种机制互相借鉴互相促进。除了移动同步,其他的诸如动作同步和隐藏显示我们一般要求不那么苛刻,因为他们不需要每帧都做处理,一般采用RPC做一次性的通知修改就可以了。

    关于同步,还有一个大家平时不是很在意的细节,那就是同步频率。前面提到了UE4会按照尽可能快的速度去发送同步数据,如果客户端的性能非常好帧数非常高,那么一帧就会产生非常多的移动RPC。

    理论上来说,如果没有丢包的话,即使服务器帧率很低,服务器也会按照客户端发来数据逐个模拟,最后两端结果相同,仍然是流畅的。但是,如果中间丢失了部分移动的RPC(引擎内部就会对发送进行限流),就可能造成服务器计算结果与客户端不同进而不断拉回客户端,造成卡顿。

    总的来说,RPC与属性同步有些场景是可以相互替代的。对于简单且实时性要求不高的使用RPC就可以,而对于需要服务器实时保有主控权且持续性同步的状态我们就可以使用属性同步。

    属性同步本身已经做了优化消耗没有那么大,你可以通过各种条件来设置他的同步规则。但是注意,量变产生质变,如果不加节制的全部使用属性同步,那么actor(以及属性)遍历的开销与会相当可观,所以还是合理的使用还是非常重要的。

    这块理论上有很多可以优化的地方,比如Actor可以设置同步的范围(类似AOI),距离玩家很远的对象不需要同步;Actor可以根据一些规则关闭对某些客户端的属性复制功能(Dormancy),同时关闭ActorChannel并从NetConnection里移除;采用replicationgraph对空间进行划分,剔除相关性不强的对象从而减少带宽的占用(但是这个方案只适合大世界类型的游戏)。

    理论上,我们还可以添加更多的优化方式以及更细的粒度来进行调整,不过具体方案就要根据游戏类型来灵活处理了。

    Replicationgraph示意,每个宝箱被放置到他所影响的所有格子里面。玩家只有进入这些格子里面才会收到宝箱的同步信息

    回放系统

    回放看起来是个很高大上的功能,但其实早在上世纪90年代就随着Lockstep算法一起诞生了。UE4内置了一套Demonetdriver系统来处理回放和录制,但由于采用的是状态同步而不是帧同步,所以实现起来比较复杂。基本思路就是在本地创建一个虚拟的服务器,录制的时候本地当成一个服务器,回放的时候本地又当做一个客户端。

    在游戏进行的时候,本地开始录制并把回放相关的数据序列化到数据流里面(可以是内存、磁盘或者是网络包),播放的时候再去对应的数据流里面读出来。虽然框架是有的,但还处于一个未完成的阶段,用起来坑也是相当的多(比如过期的多播事件在回放中不会被执行到)。

    对于死亡回放以及精彩镜头这种实时切换的需求,涉及到的逻辑要更复杂一些(比如真实世界和回放世界的切换与隐藏),这块有时间我会再写文章来仔细讲讲。

    (官方射击游戏Demo——ShooterGame中就含有一个简单的回放演示功能)

    底层框架

    说完了上层的网络同步,再简单谈谈底层。虚幻引擎诞生于90年代,也肯定参考了很多其他游戏的设计,比如“雷神之锤(Quake)”,“星际围攻:部落(Tribe)”等。

    当时Quake是最早一批采用基于“CS架构状态同步”的游戏,而Tribe将模块进行拆分和封装,是第一个构建了比较完善的网络同步架构的游戏。UE4的架构与Tribe很像,通过NetDriver + NetConnection + Channel + Actor/Uobject抽象分层实现了目前的同步方式。

    很多人总是抱怨虚幻引擎把底层搞得太复杂,但这其实有很多历史原因以及技术上的权衡,官方团队在过去的20年里肯定也无数次地思考过这种问题,这里也不过多赘述。总之,从网络层面上说,UE4高度耦合的网络框架不适合帧同步(这里指lockstep),同时也很难改造成ECS架构。

    不过,我个人也同样觉得很多游戏没必要非要追求帧同步,两种同步开发各有各的坑,真做起来游戏其实都没那么简单(也许踩UE官方的坑可能会让你更不爽一点,毕竟不是自己写的)。

    关于网络协议,游戏界经过大量的测试很早就公认——对于高频同步的游戏,使用UDP同步的效果要好于TCP。因此,Unreal使用的就是UDP协议,但是为了保证数据的可靠性,需要在上层封装一个可靠的UDP,也就是NetDriver + NetConnection + Channel那一套。

    里面的逻辑很复杂而且涉及到很多模块,确实有一些冗余。此外,虽说是可靠的,但是在属性同步和RPC的处理方式上并不相同,属性同步只保证最后的数据是可靠的,中间的结果可能会丢失,而RPC则可以保证消息一定按序送达。针对其内置的RUDP的重发机制,UE其实已经做过很多次的优化和调整了,之前任何的丢包和乱序都会立刻触发重发,4.24里面已经添加了循环队列来收包矫正收包的次序,一定程度上减少了不必要的重传。

    消息的接收和发送默认还是在主线程处理的(我们可以决定是否启用多线程),由于UDP不需要监听多个Socket而且针对收包采用多线程意义也不大,所以也没有采用iocp或者其他异步IO的方式。

    在虚幻引擎中,网络包的更新顺序是“收数据——逻辑更新——发数据”,但并不是所有的同步更新逻辑都在收包的时候做,UObject类型同步属性的更新可能就是在发包前更新的(这块是一个坑,要注意),具体可以参考我的知乎文章“《Exploring in UE4》网络同步原理深入(下)” 中的第五部分第8小节。

    到此,我已经比较全面的把虚幻引擎的网络模块重新梳理一遍,更多的细节请参考文章 “ 游戏角色移动原理 ” 以及我的知乎专栏《Exploring in UE4》相关文章。

    https://zhuanlan.zhihu.com/c_164452593 

    《Exploring in UE4》网络同步原理深入(上)

    《Exploring in UE4》网络同步原理深入(下)

    《Exploring in UE4》网络同步的理解与思考

    《Exploring in UE4》移动组件详解

    《Exploring in UE4》回放系统分析(待更)

    最后,我们再总结一些在同步中经常会遇到的问题,这些都是我踩了无数坑才总结出来的,拿大家的 “在看” 或 “转发” 换一下不过分吧。

    • Client的RPC并不能保证一定在客户端执行。在服务器上,如果有一个没有connection信息的actor(比如不是同步的,完全由AI控制的。或者说他的remote role等于none),那么他的clientRPC只会在自己的客户端上面执行。最后可能造成的后果就是函数调用栈的无限循环进而崩溃。

    • beginplay在客户端服务器都会执行,如果在beginplay执行另外一个actor的生成。可能会触发客户端和服务器都生成一遍自己的actor,结果客户端存在了两个Actor(一个自己生成的,一个服务器生产的)。之后在调用RPC的时候很可能会出现RPC执行失败,因为本地生成的Actor没有任何connection信息。

    • 客户端上对象的Beginplay是可能执行多次。在unreal中,如果一个actor是服务器创建并同步给客户端,那么服务器可以随时关闭这个对象的同步。一旦这个对象距离玩家角色非常远或者服务器主动关闭同步,客户端上的对象就会被删除掉。后期如果玩家又靠近了这个对象,那么就会重新同步到客户端,再执行一次Beginplay。这样某些数据进行两次初始化,可能不是我们想要的。

    • 我们经常会遇到“游戏状态恢复”的场景,比如网络游戏中的断线重连。然后你就可能会遇到一些对象在重连后状态不对,因为很多对象的变化是通过RPC去做的,RPC是一次性的。当你重连后,RPC不会再执行一次,所以客户端重连的状态与服务器其实是不同的。这时候需要使用属性同步来解决问题,但是属性回调在断线重连的时候你也并不一定想执行,所以要重新审视一下回调函数里面的内容。

    • 不要把随时可能被destroyed的对象传进RPC的参数里面,RPC参数里面没有判断对象是否是合法的。如果传递的过程中对象被destroy掉,后续可能触发序列化找不到NETGUID的相关崩溃。

    • 一般情况下,同步顺序在一个character内是严格按照属性的声明顺序的,不同actor无法保证。

    • 一般回调会调到的函数,要注意里面有没有判空return的情况,这个时候其他actor的指针是有可能为空的。

    • 一个UObject指针类型的数组属性,可能会触发多次回调,最后一次可以确保所有指针都有值。

    • 属性回调执行的前提是客户端与服务器的值不同,如果你本地先修改一个值,然后服务器修改的与客户端相同,那么是不会触发回调的。

    • 一般来说当Actor与PC解绑后,Actor就无法保证RPC的执行了。这种情况往往发生在角色死亡后执行unpossess时,所以在这时应该注意RPC的执行情况。

    • 如果属性没有同步到客户端或者不执行回调,注意一下是否使用了自定义的条件属性

    • 所有设置定时器来判断同步属性是否收到的逻辑都是不规范的,一旦服务器或者客户端变卡(一开始没有表现,但是随着游戏内容的增加可能出现各种诡异的bug)就可能导致信息丢失。

    更多精彩推荐
    ☞滴滴技术总监受贿 1000 万,列入招聘黑名单,互联网大厂反腐有多强?
    ☞中国第一代程序员潘爱民的 30 年程序人生
    ☞天猫京东618下单金额近万亿;苹果或选择印度组装新款iPhone SE;Adobe将于12月31日终止支持Flash| 极客头条
    ☞用 Python 训练自己的语音识别系统,这波操作稳了!
    ☞大数据 Spark :利用电影观看记录数据,进行电影推荐 | 原力计划
    ☞区块链如何解决食品安全问题?
    你点的每个“在看”,我都认真当成了喜欢
    
    展开全文
  • 首先根据目录文件夹整体看一下结构,版本4.2 Binaries 里面是一些配置文件和第三方的配置文件,全部是xml,从这可以看到虚幻4都用了什么第三方工具,有各个平台的图形库,还有声音库,物理库(PhysX3),VR,图形显卡...

    首先根据目录文件夹整体看一下结构,版本4.2

    Binaries

    里面是一些配置文件和第三方的配置文件,全部是xml,从这可以看到虚幻4都用了什么第三方工具,有各个平台的图形库,还有声音库,物理库(PhysX3),VR,图形显卡,Mono,,等等

    Build

    各个平台编译需要的,有一些批处理和xml文件等等

    有安卓,IOS,TVOS,HTML5等等

    Config

    各个平台的参数设置

    Documentation

    文档

    Extras

    额外的第三方工具

    有Maya动画Ragging工具,VSDebug等等

    Plugins

    插件,每个插件都有一个.uplugin文件来表示版本号和信息

    2D(Paper2D):虚幻的基于Sprite的系统

    https://docs.unrealengine.com/en-us/Engine/Paper2D

    AI:我们可以在AISupportModule类中看到有哪些类是AI模块,包括导航系统等等

    PublicDependencyModuleNames.AddRange(
    new string[] {
      "Core",
      "CoreUObject",
      "Engine",
      "NavigationSystem",
      "AIModule"
    }
    );

    Animation(LiveLink):LiveLink是能够在外部建模软件(比如maya)修改动画,然后在Unreal中能够实时显示修改后的动画的功能

    https://docs.unrealengine.com/en-us/Engine/Animation/Live-Link-Plugin

    Blendable(Light Propagation Volume):是一种实时GI方法(格子和球谐)

    https://wiki.unrealengine.com/Light_Propagation_Volumes_GI

    https://docs.unrealengine.com/en-us/Engine/Rendering/LightingAndShadows/LightPropagationVolumes

    Compositiong:Composure插件,将游戏与现实中影像混合的插件,

    https://docs.unrealengine.com/en-us/Engine/Composure

    lens distortion插件,镜头畸变,内部还有shader代码,本质都是变换uv

    openCV lens distortion插件

    Developer:Blank Plugin一个空白的插件,可以提供给开发者开发插件参考

    CLion 插件

    https://blog.jetbrains.com/clion/2016/10/clion-and-ue4/

    codelite插件

    Git源码管理插件

    https://wiki.unrealengine.com/Git_source_control_(Tutorial)

    KDevelop插件

    OneSky本地化服务插件

    Perforce版本控制插件

    https://api.unrealengine.com/CHN/Engine/Basics/SourceControl/Perforce/index.html

    RenderDoc着色器调试器

    源码:https://github.com/Temaran/UE4RenderDocPlugin

    https://wiki.unrealengine.com/RenderDoc_plugin

    https://forums.unrealengine.com/development-discussion/rendering/17347-new-plugin-shader-debugging-made-easy-with-renderdoc-for-ue4

    等等还有一些VS、Xcode等编译器插件

    Editor:一些编辑器编辑器插件

    资源管理、蓝图材质贴图节点、面部动画编辑器,Gameplay Tags编辑器,mesh编辑器、Mobile Launcher Profile Wizard,SpeedTree导入插件,等等

    Experimental:

    Alembic文件导入插件(.abc插件)

    https://docs.unrealengine.com/en-us/Engine/Content/AlembicImporter

    苹果图片工具、苹果视觉插件、蓝图统计插件、角色AI插件、代码浏览等等插件

    FX:特效相关插件

    Houdini Niagara插件

    Media:媒体相关插件

    Messaging:网络相关插件,TCP、UDP

    Performance:Performance Monitor性能监控器

    RunTime:

    Google AR Core、Apple AR Kit、Apple Movie Player、Android Moview Player、Oculus VR等等

    Programs

    一些工具的参数,内部全是.ini文件

    打补丁工具,shader编译,等等

    Shaders

    所有着色器,分为private,public,standalone

    Source

    真正源码所在

    Developer开发部分

    Editor编辑器部分

    Programs程序部分

    Runtime核心部分

    ThirdParty第三方

     

    ------by wolf96 2018.9.9

    欢迎访问个人网站:点击链接

    展开全文
  • 作者 |Jerish来源 | 游戏开发那些事我从16年开始接触Unreal,到如今已经4年了。最近看了不少关于网络同步的论文和书籍,总算是理解了Doom和Quake这种古董级游戏的发展...

    作者 | Jerish

    来源 | 游戏开发那些事

    我从16年开始接触Unreal,到如今已经4年了。最近看了不少关于网络同步的论文和书籍,总算是理解了Doom和Quake这种古董级游戏的发展历史,对其网络架构也有了更深一层的认识。这次想根据自己的工作和学习经验,以一个全局的视角来重新回顾一下虚幻的网络模块,并总结一些我们常见的问题,相信对UE同步细节模糊不清的你看完后一定会醍醐灌顶。

    开始前,我先给初学者一个建议。如果你打算看UE4的同步源码,最好先大致阅读一遍这本书——《网络多人游戏架构与编程》,里面基本涵盖了UE4同步框架的大部分内容,可以让你少走不少弯路。

    下面进入正题:

    网络同步,就是使各个客户端上的角色表现保持一致,属于游戏引擎的高级功能,所以一般我们都将其归类于Gameplay模块当中。不过具体的实现方案其实会深刻影响到底层的网络架构(甚至是整个游戏架构)。我们既要决定通过哪种网络协议来完成,又要决定游戏各个模块的循环执行顺序,这已经不单单是“Gameplay”层面的东西了。

    虚幻引擎属于标准的CS架构(经过无数次改版的),内置状态同步功能,其同步频率与游戏的帧率相同,属于变长步更新。由于帧率完全受CPU、GPU性能的影响,所以网络同步的频率与整个项目的性能息息相关。不过,有一点我们要认识到,unreal已经是尽可能的按照自己最快的速度进行数据的发送与接收了,只要我们做好各方面的性能优化即可。

    RPC与属性同步

    在Unreal里面,同步有两种手段,即RPC与属性同步(很多服务器引擎都是如此)。与其说RPC是同步手段,不如说他是一种传输数据的方式,好处就是可以直接通过类的函数形式书写,方便理解。同时不需要你直接写Socket,也不需要你处理封包和拆包。在计算机网络的概念里面,RPC叫做“远程过程调用”,本质上就是一种传递数据的手段,而其实现方式既可以是应用层的Http,也可以是传输层的TCP/UDP。在虚幻里面,由于很多游戏的同步(比如FPS)对网络延迟要求比较苛刻,所以我们放弃了需要三次握手的TCP而改用UDP(更不可能考虑HTTP了)。RPC既可以标记为可靠,也可以标记为不可靠。可靠的RPC最终一定会到达目标终端,但不可靠的RPC除了在网络拥挤的环境下丢失,也可能在引擎限流的情况下被提前拦住。RPC本身并不是一个可以持续存在的对象,我们只能通过RPC参数“一次性”的将数据从一端发送到另一端,所以每个RPC调用只能“只执行一次”(换句话说,他的生命周期只有一瞬间)如果RPC消息从网络中丢失,那么他就会永久的丢失(这里指不可靠的RPC),所以并不适合游戏世界各种对象的状态恢复,必须要结合可以保持对象状态的属性才行。此外,UE4里面RPC并不支持回调,所有RPC函数的返回类型都是void。

    属性同步,本质上属于一个比较上层的功能特性,是以每个对象为单位处理的(不支持更细粒度的同步,但理论上可以通过条件属性做部分调整,详见AACtor::PreReplicate)。unreal的服务器会按照一定频率的去执行同步对象属性的数据发送和接收,同时处理回调函数。属性同步的产生是为了维持对象的状态,是一个从概念上非常贴近“同步”二字的功能,一旦服务器上的同步属性发生了变化,就一定会发送给客户端(注意:属性同步只是服务器向客户端的同步,不存在客户端向服务器流通),也许中间会丢包会延迟(actor首次同步时是reliable的),但是其内置的机制会保证属性的值最终送达到客户端。借用一句经典的话来说就是,同步数据也许会迟到,但是永远不会缺席

    无论是RPC,还是属性同步,你会发现他都是基于UObject的,或者更确切的讲都是基于Actor的(以及其附属组件)。因为这两种功能一个是利用类中的函数,另一个是利用类对象的属性,他们都需要与某一个具体的对象作为媒介,而在UE的架构中,设计都是面向对象的,每个Actor都可以理解为游戏世界的对象。

    既然是基于Actor的,那么整个同步就与GamePlay框架紧密相连。由于我们在发送同步数据的时候需要知道这个数据应该发向哪个客户端,而客户端与服务器的链接信息(IP等)又在Playercontroller里面,所以同步的逻辑与playercontroller密切相关。很多刚接触unreal的朋友经常会遇到RPC数据发不出去或者收不到的问题,就是没有认识到playercontroller其实是包含客户端与服务器的连接信息的。最典型的,假如你有服务器上连着10个玩家客户端,服务器上有一辆车,让他执行Client RPC,他怎么知道发给哪个客户端?当然是通过这个车找到控制他的playercontroller,然后找到对应客户端的IP,如果这个车不被任何客户端控制,那他就不知道要发给谁。

    当然,RPC与属性同步的实现原理不同也决定了他们有很多差异。由于属性同步是跟着每一个实例对象走的,所以不存在“随用随发”。也就是说,属性同步需要在每帧特定的时机通过统一的引擎接口写到发送缓存(sendbuffer)里面。这样带来的问题就是,你在同一帧里面修改的属性只有最后的那个值会传到客户端那里,进而导致你的回调函数也只会执行一次。而RPC不同,每次执行时都会立刻将数据塞到发送缓存里面,从而保证不会丢失任何一次RPC的调用(假如RPC是可靠的)。

    另外,这里面还有一个深坑,就是关于Actor以及Component的同步顺序问题。一个对象的同步首先要给客户端上的对象与服务器上的对象建立关联,这样服务器的A变化了才会告诉客户端上的A也去变化。但是A是一个对象,对象也是需要同步的,一个场景里面有那么多的对象,同步肯定是按顺序的来的。这样就会经常出现A的对象里面有很多指向B对象的同步指针属性,但是A对象出现的时候B还没同步过来,所以在A的Beginplay里面访问B是不行的。那么如何解决这个问题?答案是用属性回调,一旦执行了属性回调,就可以确保A的B指针是存在的。不过,属性回调并不能解决所有问题。假如B对象还有C对象的指针,回调的时候C还没同步过来,你想用B去访问C发现又是空指针。这问题目前在现在的虚幻引擎里面还没有完美的解决方案,所以我们要尽可能的避免这种情况(我本人正在尝试实现一些可行的方法)。类似引发的更细节的问题还有很多,后面我会列举一些。

    移动同步理解

    两种同步手段已经介绍完毕,我们现在把视角锁定在网络同步的解决方案上。游戏中同步本质上是同步客户端之间的表现,而RPC与属性同步都只是数据上的同步,我们需要将其与画面表现结合起来。画面表现说白了就是物体的显示与隐藏、动画、位置等,其中位置同步就是最复杂的一项,因为游戏中的角色可能是每帧都在移动的,移动组件(movementcomponent)就是为了解决这个问题而诞生的。

    移动组件很复杂,他需要考虑到各种情况的延迟、抖动,需要解决不同客户端不同角色的流畅性问题,需要实现各种插值手段。在网络同步中,始终存在三种形式的角色,分别是本地玩家控制的、服务器控制的以及其他玩家控制的,在unreal中分别对应着Autonomous、Authority与Simulate。这三种类型的存在本质上代表着角色的控制者是谁(哪个端可以直接通过命令操作他),而从另一个角度讲这种分类其实是代表着玩家的操作是否有网络延迟以及延迟的大小。对于本地控制的Autonomous角色,他可以在本地直接响应你的操作,如果想把操作发给服务器,则需要经历一个client——server的延迟,而服务器想把这个操作同步给其他客户端又需要一个server——client的延迟。

    同步中最难的其实就是如何有效的对抗这种延迟。所以,会诞生诸如延迟补偿这种同步策略,即本地客户端收到其他客户端消息的时候将本地的所有角色回滚到【当前时间 - 网络延迟时间】时的位置再进行消息的处理和计算。

    (UE4默认引擎里面没有这种操作,虚幻竞技场里面有。如下图,红色是当前端的具体位置,黄色是回滚预测的位置)。

    移动组件本地客户端到服务器采用的是不可靠的RPC,而服务器到其他客户端采用的是属性同步。为什么使用RPC?因为客户端向服务器发送消息只能通过RPC,属性同步只是用来服务器同步给客户端用的。unreal在同步位置时记录了各个客户端以及服务器的时间戳,通过位置buffer缓存、每帧不停的发送位置、判断时间戳调整位置与回滚等操作实现比较理想的效果,本质上守望先锋的帧同步+状态同步是相同的(见:守望先锋架构与网络同步)。不过虚幻并没有采用ECS,并不能在架构上很好的支持所有逻辑的回滚。

    网络同步发展至今,其实基本已经成型。从早期的Lockstep到指令流水线化再到预测回滚TimeWarp,大体的同步优化手段都是这些,现在的趋势就是状态同步与帧同步里的各种机制互相借鉴互相促进。除了移动同步,其他的诸如动作同步和隐藏显示我们一般要求不那么苛刻,因为他们不需要每帧都做处理,一般采用RPC做一次性的通知修改就可以了。

    关于同步,还有一个大家平时不是很在意的细节,那就是同步频率。前面提到了UE4会按照尽可能快的速度去发送同步数据,如果客户端的性能非常好帧数非常高,那么一帧就会产生非常多的移动RPC。理论上来说,如果没有丢包的话,即使服务器帧率很低,服务器也会按照客户端发来数据逐个模拟,最后两端结果相同,仍然是流畅的。但是,如果中间丢失了部分移动的RPC(引擎内部就会对发送进行限流),就可能造成服务器计算结果与客户端不同进而不断拉回客户端,造成卡顿。

    总的来说,RPC与属性同步有些场景是可以相互替代的。对于简单且实时性要求不高的使用RPC就可以,而对于需要服务器实时保有主控权且持续性同步的状态我们就可以使用属性同步。属性同步本身已经做了优化消耗没有那么大,你可以通过各种条件来设置他的同步规则。但是注意,量变产生质变,如果不加节制的全部使用属性同步,那么actor(以及属性)遍历的开销与会相当可观,所以还是合理的使用还是非常重要的。这块理论上有很多可以优化的地方,比如Actor可以设置同步的范围(类似AOI),距离玩家很远的对象不需要同步;Actor可以根据一些规则关闭对某些客户端的属性复制功能(Dormancy),同时关闭ActorChannel并从NetConnection里移除;采用replicationgraph对空间进行划分,剔除相关性不强的对象从而减少带宽的占用(但是这个方案只适合大世界类型的游戏)。理论上,我们还可以添加更多的优化方式以及更细的粒度来进行调整,不过具体方案就要根据游戏类型来灵活处理了。

    Replicationgraph示意,每个宝箱被放置到他所影响的所有格子里面。玩家只有进入这些格子里面才会收到宝箱的同步信息

    回放系统

    回放看起来是个很高大上的功能,但其实早在上世纪90年代就随着Lockstep算法一起诞生了。UE4内置了一套Demonetdriver系统来处理回放和录制,但由于采用的是状态同步而不是帧同步,所以实现起来比较复杂。基本思路就是在本地创建一个虚拟的服务器,录制的时候本地当成一个服务器,回放的时候本地又当做一个客户端。在游戏进行的时候,本地开始录制并把回放相关的数据序列化到数据流里面(可以是内存、磁盘或者是网络包),播放的时候再去对应的数据流里面读出来。虽然框架是有的,但还处于一个未完成的阶段,用起来坑也是相当的多(比如过期的多播事件在回放中不会被执行到)。对于死亡回放以及精彩镜头这种实时切换的需求,涉及到的逻辑要更复杂一些(比如真实世界和回放世界的切换与隐藏),这块有时间我会再写文章来仔细讲讲。

    (官方射击游戏Demo——ShooterGame中就含有一个简单的回放演示功能)

    底层框架

    说完了上层的网络同步,再简单谈谈底层。虚幻引擎诞生于90年代,也肯定参考了很多其他游戏的设计,比如“雷神之锤(Quake)”,“星际围攻:部落(Tribe)”等。当时Quake是最早一批采用基于“CS架构状态同步”的游戏,而Tribe将模块进行拆分和封装,是第一个构建了比较完善的网络同步架构的游戏。UE4的架构与Tribe很像,通过NetDriver + NetConnection + Channel + Actor/Uobject抽象分层实现了目前的同步方式。很多人总是抱怨虚幻引擎把底层搞得太复杂,但这其实有很多历史原因以及技术上的权衡,官方团队在过去的20年里肯定也无数次地思考过这种问题,这里也不过多赘述。总之,从网络层面上说,UE4高度耦合的网络框架不适合帧同步(这里指lockstep),同时也很难改造成ECS架构。不过,我个人也同样觉得很多游戏没必要非要追求帧同步,两种同步开发各有各的坑,真做起来游戏其实都没那么简单(也许踩UE官方的坑可能会让你更不爽一点,毕竟不是自己写的)。

    关于网络协议,游戏界经过大量的测试很早就公认——对于高频同步的游戏,使用UDP同步的效果要好于TCP。因此,Unreal使用的就是UDP协议,但是为了保证数据的可靠性,需要在上层封装一个可靠的UDP,也就是NetDriver + NetConnection + Channel那一套。里面的逻辑很复杂而且涉及到很多模块,确实有一些冗余。此外,虽说是可靠的,但是在属性同步和RPC的处理方式上并不相同,属性同步只保证最后的数据是可靠的,中间的结果可能会丢失,而RPC则可以保证消息一定按序送达。针对其内置的RUDP的重发机制,UE其实已经做过很多次的优化和调整了,之前任何的丢包和乱序都会立刻触发重发,4.24里面已经添加了循环队列来收包矫正收包的次序,一定程度上减少了不必要的重传。消息的接收和发送默认还是在主线程处理的(我们可以决定是否启用多线程),由于UDP不需要监听多个Socket而且针对收包采用多线程意义也不大,所以也没有采用iocp或者其他异步IO的方式。在虚幻引擎中,网络包的更新顺序是“收数据——逻辑更新——发数据”,但并不是所有的同步更新逻辑都在收包的时候做,UObject类型同步属性的更新可能就是在发包前更新的(这块是一个坑,要注意),具体可以参考我的知乎文章“《Exploring in UE4》网络同步原理深入(下)” 中的第五部分第8小节。

    到此,我已经比较全面的把虚幻引擎的网络模块重新梳理一遍,更多的细节请参考文章 “ 游戏角色移动原理 ” 以及我的知乎专栏《Exploring in UE4》相关文章。

    https://zhuanlan.zhihu.com/c_164452593 

    《Exploring in UE4》网络同步原理深入(上)

    《Exploring in UE4》网络同步原理深入(下)

    《Exploring in UE4》网络同步的理解与思考

    《Exploring in UE4》移动组件详解

    《Exploring in UE4》回放系统分析(待更)

    最后,我们再总结一些在同步中经常会遇到的问题,这些都是我踩了无数坑才总结出来的,拿大家的 “在看” 或 “转发” 换一下不过分吧。

    • 1.Client的RPC并不能保证一定在客户端执行。在服务器上,如果有一个没有connection信息的actor(比如不是同步的,完全由AI控制的。或者说他的remote role等于none),那么他的clientRPC只会在自己的客户端上面执行。最后可能造成的后果就是函数调用栈的无限循环进而崩溃。

    • 2.beginplay在客户端服务器都会执行,如果在beginplay执行另外一个actor的生成。可能会触发客户端和服务器都生成一遍自己的actor,结果客户端存在了两个Actor(一个自己生成的,一个服务器生产的)。之后在调用RPC的时候很可能会出现RPC执行失败,因为本地生成的Actor没有任何connection信息。

    • 3.客户端上对象的Beginplay是可能执行多次。在unreal中,如果一个actor是服务器创建并同步给客户端,那么服务器可以随时关闭这个对象的同步。一旦这个对象距离玩家角色非常远或者服务器主动关闭同步,客户端上的对象就会被删除掉。后期如果玩家又靠近了这个对象,那么就会重新同步到客户端,再执行一次Beginplay。这样某些数据进行两次初始化,可能不是我们想要的。

    • 4.我们经常会遇到“游戏状态恢复”的场景,比如网络游戏中的断线重连。然后你就可能会遇到一些对象在重连后状态不对,因为很多对象的变化是通过RPC去做的,RPC是一次性的。当你重连后,RPC不会再执行一次,所以客户端重连的状态与服务器其实是不同的。这时候需要使用属性同步来解决问题,但是属性回调在断线重连的时候你也并不一定想执行,所以要重新审视一下回调函数里面的内容。

    • 5.不要把随时可能被destroyed的对象传进RPC的参数里面,RPC参数里面没有判断对象是否是合法的。如果传递的过程中对象被destroy掉,后续可能触发序列化找不到NETGUID的相关崩溃。

    • 6.一般情况下,同步顺序在一个character内是严格按照属性的声明顺序的,不同actor无法保证。

    • 7.一般回调会调到的函数,要注意里面有没有判空return的情况,这个时候其他actor的指针是有可能为空的。

    • 8.一个UObject指针类型的数组属性,可能会触发多次回调,最后一次可以确保所有指针都有值。

    • 9.属性回调执行的前提是客户端与服务器的值不同,如果你本地先修改一个值,然后服务器修改的与客户端相同,那么是不会触发回调的。

    • 10.一般来说当Actor与PC解绑后,Actor就无法保证RPC的执行了。这种情况往往发生在角色死亡后执行unpossess时,所以在这时应该注意RPC的执行情况。

    • 11.如果属性没有同步到客户端或者不执行回调,注意一下是否使用了自定义的条件属性

    • 12.所有设置定时器来判断同步属性是否收到的逻辑都是不规范的,一旦服务器或者客户端变卡(一开始没有表现,但是随着游戏内容的增加可能出现各种诡异的bug)就可能导致信息丢失。

    【End】

    CSDN 618程序员购物日:显示器、键盘、蓝牙耳机、扫地机器人、任天堂游戏机、AirPods Pro等超多IT人的心仪好物,全场超低价出售,让1亿程序员买到爽!

    更多精彩推荐

    ☞“原本我不会在拼多多购物,但它补贴实在太多了”
    ☞算法鼻祖高德纳,82 岁仍在写《计算机程序设计的艺术》
    ☞程序员晒端午福利,网友:看了你的福利我想摔手机
    ☞如何成为一名黑客?
    ☞发送0.55 ETH花费近260万美元!这笔神秘交易引发大猜想
    ☞京东姚霆:推理能力,正是多模态技术未来亟需突破的瓶颈!
    
    你点的每个“在看”,我都认真当成了喜欢
    
    展开全文
  • Unreal Engine 4 Gameplay框架介绍

    千次阅读 2016-08-30 16:44:50
    可以说,一个框架是一个可复用的设计构件,它规定了应用的体系结构,阐明了整个设计、协作构件之间的依赖关系、责任分配和控制流程,表现为一组抽象类以及其实例之间协作的方法,它为构件复用提供了上下文(Context)...
  • Unreal4 入门

    万次阅读 2015-07-16 10:16:39
    使用Unreal4引擎前需要安装 或者编译引擎。 下面就为同学们介绍下 Unreal4 安装配置。 一. 在Win7下安装Unreal4 在Win7下安装Unreal4需要以下几个步骤: 1. 打开“控制面板”的“程序和功能” 检查...
  • 从Unity引擎过度到Unreal4引擎(最终版)

    千次阅读 2017-12-28 16:59:41
    ... 寒假回家到现在已经有十多天了,这些天回家不是睡就是吃....哎╮(╯▽╰)╭,今天早上一觉醒来,突然得知,UE4免费了,这绝对是个好消息,前不久我还在纠结怎么申请校园账号呢o(╯□╰)o。迫
  • 探索开发一个引擎需要用到多少知识和技术
  • Unreal4引擎开发学习笔记 虚幻4引擎介绍 虚幻4是一套为游戏开发者设计和构建游戏、模拟和可视化的集成工具。 关于C++开发和蓝图开发:【蓝图建立在C++代码之上;蓝图与C++可以很好地协作。】C++由游戏逻辑程序员使用...
  • 本节书摘来异步社区《精通Unreal游戏引擎》一书中的第2步,作者: 【英】Ryan...精通Unreal游戏引擎只要项目已经保存,您就可以在Unreal Engine启动程序中找到并打开它。这一次,我们不是新建项目,而是打开已经创建...
  • unreal 源码目录结构

    2019-01-18 09:34:27
    unreal 源码目录结构 https://api.unrealengine.com/CHN/Engine/Basics/DirectoryStructure/index.html 目录结构 本页面的内容: 根目录 通用目录 特定于引擎的目录 游戏项目目录 解决方案目录 最顶层是...
  • Unreal4 学习资源合集

    2020-06-14 07:40:19
    Unreal4 学习资源合集 https://zhuanlan.zhihu.com/p/23712250?refer=VR2AR Unreal4 在国内资料还不是很多。列举一些资料供大家查阅学习。 Epic 官方有一个完全中文的文档: Unreal4 官方中文文档 基本大多数都...
  • Unreal4制作插件

    千次阅读 2015-08-03 11:51:04
    UnrealEngine4开放了插件功能,估计用不了多久UE4的生态就会想Unity3D一样健全.成熟。目前看来Unreal Engine4的工程创建最好都是通过-工程创建向导生成,那么源码版的同学会创建一个带有Engine和Programes的文件夹。...
  • Unity3D和Unreal Engine 4对比(转自知乎)

    万次阅读 2018-03-02 14:06:18
    作者:谭草头乙链接:...什么是游戏引擎,什么是虚幻引擎Unreal Engin 4)?游戏引擎是由多个子系统共同构成的复杂系统,它几乎涵盖了游戏开发过程中的所有重要环节,是游戏开发的流程核心。 UE(Unreal...
  • 2 游戏蛮牛相关资料的整理 :游戏蛮牛 - 手册 - 虚幻引擎4 | UnrealEngine4 二 深入了解虚幻底层原理 1 因为c++自身是没有垃圾回收的,所有虚幻4自己实现了一套垃圾回收机制 :虚幻4垃圾回收剖析 2 同样c++自身...
  • Unreal4 C++使用注意事项

    千次阅读 2015-08-03 11:49:02
    从UE3的UnrealScript和各种classes到UE4现在的发开方式,昨天大概拟定了一个UE4引擎探究的计划,下周会围绕计划展开研究。  这几天再看UnrealEngine4的代码,在写代码的过程中有些注意事项跟大家分享一下,也算给...
  • C++程序员如何入门Unreal Engine 4

    千次阅读 2018-06-02 21:51:36
    其次,本文只是偏重工程方面的介绍,没多少理论知识的介绍,目的纯粹就是让一个完全没有接触过UE4的C++程序员能够很快着手开发UE程序。再次,本文只是个人的经验之谈,甚至有可能有理解错误的地方...
  • Unreal Engine 4 C++使用注意事项

    千次阅读 2016-10-21 10:31:14
    从UE3的UnrealScript和各种classes到UE4现在的发开方式,昨天大概拟定了一个UE4引擎探究的计划,下周会围绕计划展开研究。  这几天再看UnrealEngine4的代码,在写代码的过程中有些注意事项跟大家分享一下,也算给...
  • 作者:唐小姐的茶 ...来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 游戏引擎是由多个子系统共同构成的复杂系统,它... UE(Unreal Engin)是目前世界最知名授权最广的顶尖游戏...
  • UE4文件夹里面包含了许多有用的资源,我们首先去了解这些文件结构,以便日后需要用到的时候随时地查找我们所需要的东西。 打开UE4所安装的目录,你会看到我们下载好的4.10版本的文件夹。 1) DirectXRedist是...
1 2 3 4 5 ... 20
收藏数 1,835
精华内容 734
热门标签
关键字:

unreal4引擎程序架构