诚如之前所说,虚幻4主要的一些特性都是由UObject穿针引线在一起的,想把虚幻玩到比较深的程度,UObject是迟早要面对、回避不得的问题,所以,准备在其它主题之前,先把UObject好好弄一下。UObject主要完成了哪些工作呢?私以为:

 

反射系统

 

UObject体系构建了整个虚幻反射系统的核心,每个UObject都来自于一个UClass,这个Class可以是Unreal Header Tool(以后统一遵循官网命名:UHT)生成的,也可以是来自于Blueprint生成的(UBlueprintGeneratedClass)。反射可以说是现在主流引擎的构建基础,对国内多数人而言,可能更熟悉的是Unity透过Mono构建出来的反射,它的重要性不言而喻。

反射很大一坨的东西,具体就不说了,它最大的作用,相当于在运行时动态生成代码,可以省掉很多手写代码的工作量。否则像UE这样复杂的界面,全部Hardcode,100人是绝对不够的,改一次所需的时间也是无法接受的。有了反射之后,剩下的很多就是很好理解的一条路就顺下来了:属性编辑器自动生成、自动消息包收发、自动序列化、自动生成BP节点、BP和C++的自动接口交互、自动浅拷贝深拷贝、甚至按照设定规则来进行拷贝……不胜枚举。

共通性都是一样:Get Class,Get Property,或者Get Function,分析Property和Function的属性,然后,设值、获取值、Invoke函数……

 

垃圾回收和生命期管理

 

UObject构建了虚幻的垃圾回收(GC)系统。GC这东西众说纷纭,但博主本人持乐观态度。最近的公司业务就遇到这么个事儿:脚本里需要发动态包,于是就需要在脚本里手动生成一个动态包,并挂接在包上面。为了完成这个目的,我就必须在脚本中制作一个生成动态包的节点,然后问题来了,我们必须还得要一个回收动态包的节点,否则这个动态包就无法回收……于是最后放弃了,回到了包里加各种Reserved的老路上去……有了GC,多数情况下都不用管这事儿……你让一个策划去理解回收这种事情,就是在给他们添麻烦,那本不是他们业务内的范畴。

GC的存在价值,并非是让事情变得简单这么简单,更多时候它能让你节省下很多编程心力,把精力花在真正该关注的地方。真GC出问题时去查错所带来的成本,未必比忘写delete带来的成本要大,说不定反而更小。

虚幻的垃圾回收系统,基本上就是从Root开始,不断遍历所有的Property,标记其为使用中。最后再遍历一遍,确认哪些Object没有标使用中就给它删掉。基本上,你不需要管这个过程,因为反射的作用,所以相关的信息都是UE自动就帮我们处理好的。有几个要注意的:

"Singleton",需要一直存在的,直接AddToRoot。

F类本身是不走垃圾回收的,但是F 类内部又有U类,这种情况下你需要注意AddReferencedObjects。把F类内部的U类给加入到GC树上。

Classes里的类,标UPROPERTY的UObject属性会被自动加入到当前类的GC列表里,但不标的会不会,没有具体跟,反正习惯随手写个就行了。

TArray和TMap里面的UObject会被自动加入GC列表,但是如果写的是std::vector和std::map,则应该是不会的,需要手动用AddReferencedObjects加进去。

 

资源管理

 

UObject最后一个作用是构建了虚幻的资源管理体系。包括资源的搜索、资源之间的引用管理,下面详细展开。

首先要先说一下虚幻的Object命名,由于资源也都是UObject,所以其命名与UObject是同一个标准。按照现在的要求,是[类名']路径名/路径名/Asset名.[包内路径.]Object本名:[属性名]['](一般是Object所在类名+一个数字后缀)。比如:

Brush'/Script/Engine.Default__Brush'

BillboardComponent'/Script/Engine.Default__TextRenderActor:Sprite'

/Engine/TemplateResources/MI_Template_BaseGray_03_Metal.MI_Template_BaseGray_03_Metal

这个名称解析跟UE3和UDK略有不同,UE3由于基于upk来对包进行管理,而又限制Content文件夹下的Upk包不能重名,所以不需要前面的路径名。UE4基于Asset,Content不同子目录下可以有同名UAsset,所以路径名就是不可或缺的了。除此之外,Asset跟UPK没有太多不同,我们后面说包,也是指的UAsset,虽然看起来这个不像包。

理论上,所有的UObject都可以交由StaticLoadObject来加载(事实上也确实是这么做的),但是很多类是有基于LoadObject的特殊实现的,比如UClass(Blueprint Class),就必须用StaticLoadClass,而地图必须使用LoadMap,LevelStreaming这样地图相关的加载流程。这些变种的主要区别是会针对相应的情况做一些特殊的处理和操作。但是核心都绕不开StaticLoadObject。所以搞明白这个StaticLoadObject,实际上就搞明白了虚幻主要的资源组织结构。

主要的流程如下:

解析路径,找到对应包(UAsset或者UPK),如果还没加载则加载包。

判断Object是否已经加载,如果已经加载则直接返回。

对资源包的加载,会把整个资源包的所有Object全部预加载的(创建并调用PreLoad,对于资源等需要PostLoad的调用PostLoad)。同时,加载包时创建ULinkerLoad,这个LinkerLoad会自动分析每个包与其他包之间的关联,通过Imports来记录本包对其它包的引用,通过Exports记录本包内的Object。

加载后,看看目标对象是不是个Redirector,如果是Redirector,则说明"曾经有个包在这里,但是被移动到新地方去了",就重定位到必要的地方。

说了这么多,其实你明白原理就很简单:虚幻所有对象都是按照一个包含了路径、包内路径、对象类名的唯一名称来命名的。而虚幻所有的包都是会记录对其它包的引用的。

所以一旦一个包的路径、对象的包内路径、以及对象类名本身发生变化,都可能会导致旧有资源的丢失和重定向。

当然,相关也都有一些机制可以帮助你事后修正(比如Redirector,Engine.ini里的Redirector config),但是,那都是补救措施,不能100%保证成功补救。好的情况,重新定位一下资源引用什么的就可以解决,但是最糟糕的情况下,有可能会导致数据丢失(其中最容易发生的就是因为BP类名修改,导致子BP类无法找到父BP类而导致子类无法正常使用只能删了重来……)

所以,在做UE4的包路径转移、资源名修改之前,一定要做好备份工作。最好是把所有原型迭代完毕后,统一进行类似操作,并经常存档或发SVN、GIT。

 

相关的注意事项:

 

Redirector需要提醒一点,虚幻里进行资源文件从一个文件夹到另一个文件夹的移动,一定要在编辑器中进行。因为虚幻资源之间的引用关系是通过前面说的Object命名来保证的,而路径名又是Object的一部分,名称不对等很容易发生问题。而编辑器移动资源后,有时候会发现移动前所在的路径下多了一个1KB字节左右小尾巴,这个小尾巴就是Redirector,同样不要手动删除,而是要在资源查看器里,通过对Redirector(需要Filter开启)的Fix up命令来进行删除。

先把Redirector打开

然后Fixup,或者

右键直接文件夹,Fix up Redirectors in Folder。

 

由于包是"Link Load"的,加载过程中会分析引用,所以如果包比较碎,这里就会由于做了更多的文件访问而导致速度变慢。单如果单个包内数据量较大,则也会导致加载单个包时速度变慢的情况,归根到底,就是权衡啊权衡。(一般说来,10个1k > 1个10k)

 

Transient对象不会被存盘,Transient包(GetTransientPackage)是一个特殊的包,所有临时对象都应该创建在这个包里面。

 

异步实际并非真正异步。LoadObject加载过程中有一系列的全局变量,而且这些全局变量维护时没有任何锁,所以也无法真正做到异步加载。所以虽然您看到接口上有LoadPackageAsync,但那个的实现是在主线程每帧区分时间片来实现的。不过话说回来,多线程读包真的有必要吗?机械硬盘的访问速度本身是最大的限制因素,读包过程中的多线程,CPU其实帮不上任何忙。

 

真正大量磁盘或网络数据的异步加载可以参考Texture Streaming(为何只有Texture做了Streaming就是因为这玩意儿现在是游戏最吃资源的了,十个模型的资源量不见得比得上一张贴图啊),先把Object和少量基本信息当作占位符加载进来,Object的实际数据则放到其他线程里慢慢加载。如果您有类似需求,可以考虑这个方案。不过感觉是没必要,比如我们游戏常用的角色异步加载什么的,其实走主线程时间片完全够了。

 

编辑器中的资源会被标记为Standalone,在无引用时仍然存在,其它还有一系列不会被GC的情况,需要注意。

虚幻4随笔6 Object和序列化的更多相关文章

  1. 虚幻4随笔 三 从UE3到UE4

    笔者有幸参与过两个UE3项目,完全不同的使用方法,总共用了5.6年.引擎学习最好还是能参与项目,自己看的话往往容易纠结到一些细节上去,而引擎之所以是引擎,重要的恰恰是在容易被人忽视的工作流上.单从细节 ...

  2. java中流转化为Object可序列化

    一.PO实体类 public class buisPO{ /** * 业务数据 */ private Bolb buisData; public Blob getBusiData() { return ...

  3. 16 IO流(十三)——Object流 序列化与反序列化

    Object流.序列化与反序列化 Object流是将 可序列化的对象 进行序列化与反序列化的流. 可序列化的对象:使用关键字Serializable修饰,表示这个对象可以进行序列化与反序列化. 序列化 ...

  4. Error serializing object:序列化对象时出错

    序列化对象时出错 :Error serializing object. Error serializing object. Cause: java.io.NotSerializableExceptio ...

  5. 虚幻4随笔4 从project開始

     前文说到UE3開始.虚幻就使用了UnrealBuildTool(下面简称UBT)来编译和生成代码. 为什么这么做而不是使用VS是非常好理解的:由于VS跨平台会比較麻烦.像虚幻这样体量的proje ...

  6. Ignite cahce 存储object类型数据和object类型数据序列化后string存储区别

    Ignite cache在存储时 object类型的数据和 序列化该object成string类型 两者存储时间差不多. 但是这两者在读取出来的时候,string类型比object类型快很多. 以下为 ...

  7. C# 序列化与反序列化几种格式的转换

    这里介绍了几种方式之间的序列化与反序列化之间的转换 首先介绍的如何序列化,将object对象序列化常见的两种方式即string和xml对象; 第一种将object转换为string对象,这种比较简单没 ...

  8. c# Json 自定义类作为字典键时,序列化和反序列化的处理方法

    一般情况下,Newtonsoft.Json.dll 对 Dictionary<int,object>.Dictionary<string,object>等序列化与反序列化都是成 ...

  9. YbSoftwareFactory 代码生成插件【二十】:DynamicObject的序列化

    DynamicObject 是 .NET 4.0以来才支持的一个类,但该类在.NET 4.0下未被标记为[Serializable] Attribute,而在.NET 4.5下则被标记了[Serial ...

随机推荐

  1. day4:vcp考试

    Q61. Which two statements are true regarding Virtual SAN Fault Domains? (Choose two.)A. They enable ...

  2. 爬楼梯 · Climbing Stairs

    [抄题]: 假设你正在爬楼梯,需要n步你才能到达顶部.但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部? [思维问题]: 不知道一步.两步怎么加.还是用iteration迭代.此题公式可被 ...

  3. C#中获取串口与并口列表

    //获取系统中的串口并加入到下拉框中 cbCashBoxPort.Items.Clear(); string[] ports = System.IO.Ports.SerialPort.GetPortN ...

  4. js strict 关键字

    strict strict模式,JavaScript在设计之初,为了方便初学者学习,并不强制要求用var申明变量.这个设计错误带来了严重的后果:如果一个变量没有通过var申明就被使用,那么该变量就自动 ...

  5. 在控制台远程连接mysql数据库时,出现ERROR 2049 (HY000)错误

    问题的原因是,你本地的数据库版本过高,而远程的数据库版本低. 解决方法:在连接时加上  --skip-secure-auth 参数就可以了. mysql -h主机 -u用户名 -p密码 --skip- ...

  6. Golang之(for)用法

    地鼠每次选好了一块地,打洞,坚持半个月发现地下有块石头,然后他就想绕路了...殊不知绕路只会让它离成果越来越远 package main import ( "fmt" " ...

  7. EXPAT(XML解析库)

    一.简介 expat是一个由C语言编写的XML解析库.James Clark创建了这个库,现在是制定XML标准的W3组织的技术leader.现在的版本是2.0.2.0开始就由Clark Cooper领 ...

  8. web札记

    url中不能是#号,struts不读取#之后的字符串.

  9. Devexpress VCL Build v2014 vol 14.2.1 beta发布

    已经快到2015 年了. 14.2.1 beta 才出来了. 还好,有一些新东西. 官网地址 VCL Gauge Control Designed to clearly convey informat ...

  10. 2018.07.08 NOIP模拟 第K小数(二分)

    第K小数 题目背景 SOURCE:NOIP2016-AHSDFZ T1 题目描述 有两个正整数数列,元素个数分别为 N 和 M .从两个数列中分别任取一个数相乘,这样一共可以得到 N*M 个数,询问这 ...