虚幻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的这套游戏上层逻辑框架,是多么地稳固和强大。
虚幻4随笔 三 从UE3到UE4的更多相关文章
- 虚幻4随笔4 从project開始
前文说到UE3開始.虚幻就使用了UnrealBuildTool(下面简称UBT)来编译和生成代码. 为什么这么做而不是使用VS是非常好理解的:由于VS跨平台会比較麻烦.像虚幻这样体量的proje ...
- 虚幻4随笔6 Object和序列化
诚如之前所说,虚幻4主要的一些特性都是由UObject穿针引线在一起的,想把虚幻玩到比较深的程度,UObject是迟早要面对.回避不得的问题,所以,准备在其它主题之前,先把UObject好好弄一下.U ...
- Alpha冲刺随笔三:第三天
课程名称:软件工程1916|W(福州大学) 作业要求:项目Alpha冲刺(十天冲刺) 团队名称:葫芦娃队 作业目标:在十天冲刺里对每天的任务进行总结. 随笔汇总:https://www.cnblogs ...
- 框架计划随笔 三.EntityFramework在传统事务脚本模式下的使用
某个朋友问为什么不推首页或者允许评论,我说一直没怎么写博客,也习惯了先随便乱画再开始写文档,担心公开后一些不经意的"呓语“中得出的错误的结论会给别人错误的观点,所以这个系列只是当做熟悉写博客 ...
- 随笔三 安装Linux操作系统
一.虚拟机安装Ubuntu图文教程]在自己笔记本上安装Linux操作系统 我参考了VirtualBox虚拟机安装Ubuntu的图文教程,根据图片和所附内容一步步的将虚拟机安装到位,没看安装教程之前完全 ...
- JAVA 多线程随笔 (三) 多线程用到的并发容器 (ConcurrentHashMap,CopyOnWriteArrayList, CopyOnWriteArraySet)
1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...
- Java随笔三
1.接口: 1)接口不是类,不能使用new运算符实例化一个接口,而是对类的一组需求描述,这些类要遵循接口描述的统一格式进行定义.个人感觉像C语言中的函数在头文件中的预先声明,但是包含了一些类的特点,比 ...
- matlab随笔(三)
把矩阵变成行向量(矩阵元素的排列是从上到下,从左到右): 1.先转置,转成行向量 A = >> A=A' A = >> a=A(:)' a = 2.reshape函数 A = ...
- android 应用架构随笔三(ListView)
import java.util.ArrayList; import java.util.List; import com.heima.googleplay.holder.BaseHolder; im ...
随机推荐
- python之用户登陆作业
实现一个用户登陆的小程序,如果密码错误,可以重试三次,如果三次均错误,则锁定 数据库如下格式,分别用户名|密码|锁定,其中锁定字段0为正常,1为被锁定 root|admin123|0 admin|ad ...
- 165. Compare Version Numbers比较版本号的大小
[抄题]: Compare two version numbers version1 and version2.If version1 > version2 return 1; if versi ...
- PythonQt进阶
本文介绍PythonQt和qt之间是如何进行交互操作的 例子是以Qt的TreeView为实例进行介绍 在该例子中,TreeItem不是从Qt中进行的继承,这样的类如果要和Python进行交互,首先需要 ...
- 转:从框架看PHP的五种境界及各自的薪资待遇(仅限于二三线城市,一线除外)
在撰写此文前首先必须申明的是本人不鄙视任何一种框架,也无意于挑起PHP框架间的战争,更没有贬低某个框架使用者的用意,本文纯粹个人的看法.你可以认为我无知也好,或者装逼也好,请不要试着在任何情况下,随便 ...
- Netty 零拷贝(三)Netty 对零拷贝的改进
Netty 零拷贝(三)Netty 对零拷贝的改进 Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html) Netty 的&quo ...
- 优秀UX设计师的八条黄金法则
与用户保持亲密 成为成功的UX设计师最重要的先决条件之一就是与用户保持紧密的联系,以发现和了解他们的需求和爱好.理想情况下你应该让自己完全地成为产品用户,因为只有这样你才能理解背后的动机.“这样的 ...
- maven web 项目 打入 jar 包 , 和编译入 java 文件到 web-inf 下
<outputDirectory>src\main\webapp\WEB-INF\classes</outputDirectory> 可以把 类文件编译到 web-inf 下 ...
- 给自己名字abel.这个好,怎么字母排序都第一
给自己名字abel.这个好,怎么字母排序都第一
- 05 数据库入门学习-正则表达式、用户管理、pymysql模块
一.正则表达式 正则表达式用于模糊查询,模糊查询已经讲过了 like 仅支持 % 和 _ 远没有正则表达式灵活当然绝大多数情况下 like足够使用 #语法 select *from table whe ...
- KBMMW 4.6 正式版发布
喜大普奔迎新年! Merry Christmas! We are happy to announce the release of kbmMW v. 4.60.00 Professional and ...