虚幻4随笔6 Object和序列化
诚如之前所说,虚幻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和序列化的更多相关文章
- 虚幻4随笔 三 从UE3到UE4
笔者有幸参与过两个UE3项目,完全不同的使用方法,总共用了5.6年.引擎学习最好还是能参与项目,自己看的话往往容易纠结到一些细节上去,而引擎之所以是引擎,重要的恰恰是在容易被人忽视的工作流上.单从细节 ...
- java中流转化为Object可序列化
一.PO实体类 public class buisPO{ /** * 业务数据 */ private Bolb buisData; public Blob getBusiData() { return ...
- 16 IO流(十三)——Object流 序列化与反序列化
Object流.序列化与反序列化 Object流是将 可序列化的对象 进行序列化与反序列化的流. 可序列化的对象:使用关键字Serializable修饰,表示这个对象可以进行序列化与反序列化. 序列化 ...
- Error serializing object:序列化对象时出错
序列化对象时出错 :Error serializing object. Error serializing object. Cause: java.io.NotSerializableExceptio ...
- 虚幻4随笔4 从project開始
前文说到UE3開始.虚幻就使用了UnrealBuildTool(下面简称UBT)来编译和生成代码. 为什么这么做而不是使用VS是非常好理解的:由于VS跨平台会比較麻烦.像虚幻这样体量的proje ...
- Ignite cahce 存储object类型数据和object类型数据序列化后string存储区别
Ignite cache在存储时 object类型的数据和 序列化该object成string类型 两者存储时间差不多. 但是这两者在读取出来的时候,string类型比object类型快很多. 以下为 ...
- C# 序列化与反序列化几种格式的转换
这里介绍了几种方式之间的序列化与反序列化之间的转换 首先介绍的如何序列化,将object对象序列化常见的两种方式即string和xml对象; 第一种将object转换为string对象,这种比较简单没 ...
- c# Json 自定义类作为字典键时,序列化和反序列化的处理方法
一般情况下,Newtonsoft.Json.dll 对 Dictionary<int,object>.Dictionary<string,object>等序列化与反序列化都是成 ...
- YbSoftwareFactory 代码生成插件【二十】:DynamicObject的序列化
DynamicObject 是 .NET 4.0以来才支持的一个类,但该类在.NET 4.0下未被标记为[Serializable] Attribute,而在.NET 4.5下则被标记了[Serial ...
随机推荐
- 使用Spring框架来管理模板类
1. 刚才编写的代码使用的是new的方式,应该把这些类交给Spring框架来管理. 2. 修改的步骤如下 applicationContext.xml中<beans>标签的开头配置为: * ...
- 如何在C#中自定义自己的异常
在C#中所有的异常类型都继承自System.Exception,也就是说,System.Exception是所有异常类的基类. 总起来说,其派生类分为两种:1. SystemException类: 所 ...
- 主成分_CPA
基本原理:方差最大原理 通过正交变换将原相关性变量转化为不相关的变量 第一主成分:线性组合 方差最大 第二主成分:线性组合,COV(F1,F2)=0 步骤: 原始数据标准化:DataAdjust(m ...
- 记unit of work与事务提交
https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using- ...
- Mybatis Blob和String互转,实现文件上传等。
这样的代码网上有很多,但是本人亲测有bug, 下面是我写的代码.望参考 @MappedJdbcTypes(JdbcType.BLOB) public class BlobAndStringTypeHa ...
- dbutils封装对象,单列,一行一列(用)
基本用法:查找并封装对象与对象集合 public User findUserByNamePassword(String name,String password){ QueryRunner runne ...
- mac安全与隐私只有两个选项,少了一个任何来源
很多软件安装后就会出现,程序已损坏,请移到废纸篓的提示. 解决方法:在终端里输入:sudo spctl --master-disable 然后回车,然后输入密码,即可在安全选项中看到重新出现的允许任何 ...
- 2018.08.31 bzoj3566: [SHOI2014]概率充电器(概率dp+容斥原理)
传送门 概率dp好题啊. 用f[i]" role="presentation" style="position: relative;">f[i] ...
- hdu-1179(匈牙利算法)
题目链接: 思路:找n个巫师和m个魔棒匹配的问题,匈牙利算法模板 匈牙利算法:https://blog.csdn.net/sunny_hun/article/details/80627351 #inc ...
- Deployment is out of date due to changes in the underlying project contents. Deployment is out of date due to changes in the underlying project contents. You'll need to manually 'Redeploy' the projec
原因1:导入的jar包路径不对,造成第一个错误, 原因2:设置右键工程->属性->myeclipse->web->deployment选use workbenk defaul ...