虚幻4随笔 三 从UE3到UE4

笔者有幸参与过两个UE3项目,完全不同的使用方法,总共用了5、6年。引擎学习最好还是能参与项目,自己看的话往往容易纠结到一些细节上去,而引擎之所以是引擎,重要的恰恰是在容易被人忽视的工作流上。单从细节上看,UE3的代码很多地方并不完美,甚至有些奇怪,但是一旦做到工作流上,就会发现整个UE3工作流的强大之处。

先回顾一下UE3系统的一些结构要点,权当做个记录,看看UE4在这些方面有什么不同,作为我们接下来读码的突破口。

如果真心想要学习这个引擎,最好还是能用它来做做项目,项目不分大小,只论完整程度,笔者短时间内看来是没机会了。

1、BuildTools

UE4除了工具和插件外,本体不分工程了,UE3的核心部分最后分了Core、Engine、Editor、WinDrv、Network、Renderer等十数个工程,UE4就一个UE4工程。但工程归工程,编译时还是分得很开的。

而且这个工程猜测是靠BuildTools来生成出来的(一开始那个bat)。看起来,散落在各个文件夹下的.cs文件就像CMakeList.txt那样,它们才是整个工程的组织核心。

事实上从UE3时代,就可以完全脱离Visual Studio IDE来工作了,UE3的工程本身都已经不再是典型的VC工程,代码编完后,实际上最后执行的是Build.bat、调用UnrealBuildTools.exe来编译,所以要改工程设置,也是需要去修改UnrealBuildTools工程的。

** 估计,如果要改工程组织结构,增加文件什么的,也需要维护这个.cs文件吧。这个可能得等做做才知道了。

不过应该可以看出来,UE4这里是为了支持项目和引擎分离而进行的。

2、Core

个人观点,UE3的Core重点是下面几个部分:

作为整个虚幻构架基础的UObject和由一大堆宏和各种Classes.h这套组织结构反射系统——围绕它的包括GC、UObject与UnrealScript的互操作性、与编辑器的互操作性、自动序列化、对象克隆等。这个非常重要,整个虚幻体系的核心就是这一套东西,如果前面不注意的话,后面迟早会在这里栽点跟头。

序列化和统一具名访问:ULinkLoader,起这个名字可能主要是因为加载的时候,它会自动分析Object的引用链,并且根据需要继续往下加载。另外,所有虚幻的Object都会有自己独一无二的具名路径,例如xxx.umap:persistentLevel.Pawn_0,xx.Material.Material_0,任何时候,只要使用LoadObject、FindObject并传入这些名字,就可以访问到对应的对象。这个在编辑器的维护中是相当方便的一个底层特性。甚至,这个具名路径还可以访问到脚本中的类、内置模板资源等等。

MakeCommandlet和UC脚本核心:Core中间编码了整个Unreal Script的编译和运行时环境。Unreal Script编译过程中会生成相应工程的Unreal Script/C++互操作文件:xClasses.h以及一堆自动生成的方法和调用。编译后的结果是.u文件,其实同时就是跟.upk资源文件一样的格式——虚幻2不清楚,但至少从虚幻3时代开始,资源和脚本就被当作是同一个东西,脚本是可执行的资源,资源是不可执行的脚本。另外与此相关的就是一套调试器——很多人用了半天虚幻3却不知道虚幻脚本是可以调试的……AutoDebug命令行或者ToggleDebugger指令搜一下,印象中调试器的核心接口是基于UDebuggerCore还是UDebuggerInterface这个类。VS装nFringe插件后、或者自带的UDE都可以对脚本进行调试。

状态机:脚本的特殊语法,状态机是在脚本类内部的概念,每个状态可以重载脚本类某些函数的实现,这样当状态切换到这个状态的时候,就只是执行状态内的函数而非脚本类的函数本身。Actor和Controller里大量用到。

Latent:脚本的特殊语法,基本类似于不通过连线的Kismet,latent类似于Erlang这样的Coroutine语言,每个语句都是步骤而非过程,步骤可能会花很多帧去执行,执行完毕后接着进行下个步骤,传统语言的过程只能当前帧执行完毕。

其它就是一系列的数学库、内存管理、辅助函数。内存管理比较有意思,一开始看总觉得问题较大,当时组里的内存专家Aman Jiang老师实际打出来报告后发现这块儿管的还是很不错的,碎片率远低于我们的预期。

3、Engine

相当庞大的集合,个人观点,重点在于:

Actor-Component体系:组件化结构的虚幻版,组件化现在应该是大多数引擎的标配了吧?这块儿可以说中规中矩,主要组件还是得花心思去看看,否则极易在接口调用顺序乱掉的情况下发生问题。渲染器与游戏上层逻辑通过Component来接口,提供新的渲染技术后,只需要做一个对应的Component就可以了——SpeedTree什么的就是这么集成进来的。

Game-Player-Controller体系:Game Mode决定了当前关卡的游戏玩法,一个关卡可以有不同的游戏玩法——对于FPS你可以想象虚幻竞技场中的很多地图都同时支持Free for all、夺旗、Team计分。对于网络游戏,你可以想象一个关卡资源可以用来做战场、也可以用来做副本。Player是所有IO的总入口,一般一个游戏只有一个Player,就是Local Player。主机游戏可以设计同时存在两个Player的场合,可以用分屏显示来分离各自的IO。进入地图后,会针对当前地图生成Controller,来实际从Player截获输入和部分输出操作,并真正影响到游戏中。相应的概念还包括View(实际上的摄像机)、ClientViewport(游戏和编辑器窗口)。

Controller-Pawn体系:Pawn是可以被Controller控制的东西,Controller把IO和UI消息转化为对Pawn的操作,通知Pawn完成其功能,并把这些功能执行过程反馈给IO和UI。在游戏中可以切换Controller内部的不同状态,例如根据Pawn是在走还是在爬墙,把输入消息转化为对Pawn不同的指令。还可以切换Controller,比如进载具了,Controller一换就Ok。甚至技能中也可以切换Controller,比较经典的例子就是虚幻竞技场3里的榴弹炮:普通架起来的状态下打出的是一般炮弹,炮弹飞行过程中鼠标可以一直控制其方向,右键可以把这个炮弹展开使其定位在空中,然后你的视角一直停留在这个炮弹上,鼠标变成在地面选择一个区域,火炮变成一门发射榴散弹的大杀器,把致命散弹砸向这个区域。笔者接触过不少游戏的游戏系统了——不幸的是这个流程很少有系统能够不加大改地实现。AI也是一种Controller,操纵的是Bot这个特殊的Pawn,这块儿有兴趣也可以研究一下。

World-Level-Actor体系:World里存一堆Level,Level里存一堆Actor, Build后的光照跟Level走,Level是关卡部分的资源单位。但场景图根是World里的Hash,虚幻3里是个八叉树实现。World里实现了基本的场景功能,角色的走跑、悬崖边缘的检测、走到碰撞体前被挡住、碰撞体位置变化时导致自己上面放置的其它碰撞体变化……你如果有自己的场景需求,可以修改World里面的这个部分。注意,在虚幻3里场景和物理虽然有关,但是本质上还是分离的,引擎提供了默认的整合方式,但你可以在Actor里重新控制这种整合。

资源体系:没什么好说的,Material、Texture、各种Mesh、Particle。Material连线球很赞,但是跟渲染Stage有较深的关联,笔者试图做过一个自以为比他更好的,跟Stage可以一定程度脱耦的材质连线球系统——但是最终发现材质这东西根本上还是离不开渲染Stage,什么都想控制的结局一定是什么都控制不过来。

渲染体系:就不说什么了吧,Deferred Lighting的Stage体系,网上的文章海了去了,做图形的这个早就抛脑后了吧。最近一两年的版本支持了Deferred Shading。这套Stage的低端化替换还是很方便的——毕竟现在需要考虑到游戏可能更多是会在intel HD 3000/4000这种显卡上跑的可能性了。

4、UnrealEd

编辑器……怎么说呢,这个没法按体系来了,太多了,有些精品也有些糟粕,反正编辑器这东西,没法说,需要扩展的时候自己去改吧。

注意属性编辑器是如何发挥反射的强大效力的。

有些细节问题,实际做了可能会遇到:拷贝对象时,有些属性是引用的(一般的Object属性不加任何描述),有些是复制的(editinlinenew、instance和duplicate),有些是舍弃的(transient)。还有就是虚幻比较喜欢用Prototype + Clone的方式来实现Template-Instance这种需求,典型的例子是Animation Tree和Prefab,实例都是直接从资源拷贝出来的。主要是因为UE3的反射外围有一个比较强大的Clone系统,但是前提是您得对刚刚列举的那些关键字比较熟悉,否则为这些系统扩展时就比较容易遇到问题。

多说一句:编辑器选物体用的是把物体的ID渲到一张Render Target上再去这个Render Target上查找鼠标点Id的做法,叫HitProxy,如果你的游戏想支持像素点选,可以参考这个东西,很容易就能把这个Stage集成到游戏Stage里。

5、其他工程

就没什么好说的了:

渲染器:Renderer,DX9、DX10、DX11、OpenGL的实现都有,DX10只有一个版本昙花一现。

平台库:以XXXDrv为名,例如WinDrv。主要提供平台方法,没什么好说的。

网络库:IpDrv、Channel什么的,也没什么好说的。

网游的开发者很喜欢自己构造上层,我参与的第一个UE项目就是这么做的,实际上最后做出来的上层后来看来比虚幻的整个上层体系差的太远。第二个项目笔者就一直推动着在虚幻框架上的小修小改。最后能实现什么呢?能实现在编辑器里根服务器通信,编辑器调整完Kismet连线图、资源、布怪点后,不用退出,直接就可以启动开发服务器,然后在编辑器内测试刚刚自己做的东西对不对,可惜因为资金原因没做下去,两年前这个级别的工具集成,不知道有多少人做到了?至少我现在在这个项目里还没能推广到,也不可能推广到那个程度(笔者鼻子翘起来了~*^_^*)。不过未来不需要再弄了——看看虚幻4的Blueprint,可以考虑这个级别的集成了。

笔者一直认为,虚幻3强大的地方不在于他的图形,而是在于这套强大而稳固的结构和迅速的工作流,而当你握这套结构和工作流后,笔者发现所有引擎在自己的面前索然无味,图形虽然还在追求,但已经退居次席了,而自己重新去做引擎的冲动则不断降低,直至完全消失。

用虚幻一定要首先明白一个原则,就是这是个解决方案式引擎,不是OGRE那样的图形工具库,所以你的所有改动一定要符合虚幻的基本结构假设,否则你只是在给自己找麻烦。但是,相信我,符合这些基本的结构假设一点不会让你的自由度降低,你的控制能力把控能力还是相当强的——不信你看,笔者上面列举的框架部分,有哪个是会影响你的发挥的?M——World-Actor、V——Player、C——Controller,哪一环是可以省略的?当然,如果您的体系结构设计的比这个还好,那真的很恭喜您了,一山更比一山高,笔者只能感叹于自己的时运不济了……

这两天还是在看例子,看几个Blueprint的例子,顺便回想一下UE3的那些事儿。最近的紧张局面可能会延续到4月中旬,届时才有可能有更多时间来看UE4。目前感觉变化还是挺大的,不过,喜在框架方面的改动似乎很有限。

从另一个方面,也证明了UE3的这套游戏上层逻辑框架,是多么地稳固和强大。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注