UE引擎研究笔记
对UE的反射名(可选 or 全部)进行HASH,且不影响游戏原始的运行效果。
对结构体进行随机化。
实现思路
① 源码级
(宏实现)编译期处理所有gen.cpp字符串
资源预处理
处理代码中对反射名的引用
② bin层
直接对bin数据的字符串处理
混淆反射的思路:
1、只有正式&编译打包时,才会hash;避免直接对gen.cpp哈希后,影响编辑器。
2、资源预处理。这个东西可以理解为数据初始化的存档,里边记录着对象反射的序列化数据,用于加载时反序列化初始到对应对象中;因此如果只单纯对gen.cpp进行hash,那么游戏初始化对象时,遇到带有资源的属性直接报错。
- 目前只修改UE4下边gen.cpp中非对象类型的反射名,游戏能跑通,打包编译也正常,下一步研究一下资源预处理。
记录
开发&调试环境相关
确保安装UE引擎是勾选输入调试用符号,不然没法对UE引擎部分进行调试。
vs中四种编译模式:DebugGame、DebugGame Editor、Development、Development Editor、Shipping。
带
Editor:对UE4Editor.exe进行调试,这个模式下也是可以调试游戏的,只需要在编辑器中点击启动。不带
Editor:直接对游戏进行调试。
其中Debug、Development、Shipping的区别简单说就是编译优化程度不同(低→高)。
当以Editor模式调试时,项目工程目录下的Binaries会生成一个dll文件,该文件实际上为游戏本体,用于加载到编辑器中。(Editor选项的编译产物为DLL,反之为EXE)。
使用VS调试时,发现被编译的gen.h和gen.cpp的来源会根据选择的调试目标进行重定向。例如当前选择的调试目标为DebugGame Editor,并且断点打在UE4中,但是拉起调试后,断点却断在了UE4Editor中;以调试目标为DebugGame时,也会发生相同的情况。(断点在UE4Editor,却断在UE4中)
验证编译产物的反射名。
带Editor编译时:
不带Editor编译:
知识相关
术词
COD:Class Default Object”,即“类默认对象”。在UE中,每一个类(UClass)都有其对应的CDO。复制或者实例化一个类型对象的时候,CDO不会被重新创建,尽管后续该实例可能会有一些数据被修改,但它们的CDO其实还是一直不变的,相同类型的所有实例都是共享的同一个CDO,所以CDO很适合用于做一些所有对象共享的操作与数据存储。
由 UClass::GetDefaultObject() 获取到的为类的 CDO 对象。它由引擎自动创建,编程时需要注意避免在 CDO 中处理非必要的逻辑 。CDO 对象的主要作用就是用于为类提供默认值。例如我们在编辑器中编辑的蓝图类的属性就是保存在该蓝图类的 CDO 中,且在构造此蓝图类的实例时会以其 CDO 作为默认的模板对象来初始化蓝图类实例的属性。此外 CDO 对象是具有稳定的网络路径的,可由网络同步其引用( 参考此链接),这使得我们在同步一个 UObject 对象的属性时,仅需同步和 CDO 中不同的属性即可。同样的道理在序列化时也只需保存和 CDO 中不同的差异属性即可。
资源
https://www.cnblogs.com/sin998/p/15501520.html
资源路径,右键资源,点击复制引用。
会得到一串字符串:Blueprint’/Game/FirstPersonCPP/Blueprints/FirstPersonCharacter.FirstPersonCharacter’
- 红色部分
是资源的类型,平时加载资源的时候也可以不要,加载函数内部会截掉这部分只留单引号里面的路径,所以其实前面这个StaticMesh或Blueprint可以不写,在编辑器Content Browser里右键点资源拷路径是有这部分的,我猜引擎留着这部分可能是为了用户清楚他的类型是什么,更方便识别一些,这个只是给人看的,程序不看这个。
- 绿色部分
是资源的分区。大部分资源的路径都是以/Game开头,这个其实表示这个资源是在游戏的Content目录下面,也可以是/Engine就表示引擎下面的,比如下图,或者对应插件目录。
- 蓝色部分
实际资源路径,表示资源在哪个文件夹下面。
- 黄色部分
资源的包(Package)名,也就是这个资源所在的真实物理资源文件(uasset/umap)的名字,包其实就是UE4将对象按照自己的规则序列化到磁盘上的文件,在Content Browser里看到的每一个文件都是一个包。
- 紫色部分
资源的对象名,因为物理的资源文件里面可能有多个对象,这个名字可以唯一标识包的内部每个对象的唯一名字。比如蓝图资源里有多个UObject,一个关卡文件里有多个Actor,一个UI蓝图里有多个控件。如果不写,UE4的某些接口会默认以包名补充到后面,也就是说默认使用和包名相同的对象名,但有的接口又可能不做处理,所以还是建议写。如果用的不是默认对象,而是资源对象的类,就要在后面加一个_C,如果是CDO对象,就要在前面加Default__。
- 冒号部分
子对象名,有些资源路径后面会带冒号:接一个文件名,这种其实是对象的子对象名,有的资源对象内部有子对象,比如C++类里的子类,这个不常用,知道即可。
若对象名带有“_C”表示这是一个蓝图产生类(GeneratedClass),没加的代表是蓝图类。
ParentClass是蓝图要编辑的对象的父类。
蓝图和GeneratedClass就好像修改器和存档的关系,如果我们把UE4运行起来的世界看做是一个没有实时存档功能的上古游戏,那么,我们需要游戏存档(GeneratedClass)我们才能游玩,而我们要修改存档只能使用特定的修改器(特定类型的Blueprint)。而游戏运行起来后完全不需要修改器,只需要游戏存档。每次当我们点击蓝图的Compile编译按钮时,相当于我们使用修改器编辑存档后点击保存,只有点击保存了我们的修改才有效。
资源是一种 “既可以被序列化成文件(.uasset、.umap) 保存在硬盘” , 又可以 “从硬盘加载然后反序列化到内存中(UObject)” 的对象 ,主要用来存贮游戏资源(比如:蓝图,贴图,骨骼,声音,特效等).从硬盘加载资产文件以后,在内存中就可以把它当作UObject对象使用了;比如:UBlueprint、UTexture2D、USkeletalMesh、 USoundWave 等
勾选
点击
会在对应资源下生成utxt文件。
该文件以json格式保存资源的存储内容(简化过),可以大概进行浏览。
Cook
什么是Cook(烘焙)?
Cook简单而言就是对“没用”资产的提出过程,比如在日常开发过程中,我们需要调试,以及有些功能只有在编辑器下才可用,资产中也会携带与正式发布版无关的功能或者相关内容,Cook的过程就是对这一类我们不需要的东西做剔除,然后只保留游戏相关的部分,这个过程就是的Cook。
理解了Cook的概念再对上面做参数介绍的时候有关Cook的部分这里做一下说明:
Unknown:相当于自动Cook,如果有对此资源的引用就Cook,如果没有就不Cook
Never Cook:永不Cook,如果存在对它的引用会报错
Development Cook:在Development打包条件下有引用就会Cook
Development Always Cook:在Development打包条件下永远会Cook
Always Cook:在Development或者Ship打包条件下永远会Cook
总结:只要有对资产的引用,就应该做Cook操作。
Cook前后资产对比:
图片引擎中的资产大小
Cook之后的大小
从对比可知,Cook前后资产大小的变化,一般会减小,因为去掉了编辑器相关的部分。
反射相关
反射中间文件(gen.h、gen.cpp),由UHT生成,存放下列两个位置
| 路径 | 作用 |
|---|---|
| X:/ProjectSource/Intermediate/Build/Win64/UE4 | 实际生成到EXE中 |
| X:/ProjectSource/Intermediate/Build/Win64/UE4Editor | 在UE4Editor面板中显示 |
如果想重新生成反射文件有两种方法:
1、vs点击重新生成解决方案。
2、删除反射文件,编译 or 运行编辑器时会自动生成。
3、打包时,好像会判断时间戳,太久的话会重新生成
当同步修改gen.cpp和cpp中的内容
会出现一个情况,进入游戏后枪不见了!
这是因为uasset资源绑定的反射名(gen.cpp)没有修改。进入编辑器中右键对象-浏览至资产。
点击在文件夹视图中显示
此时定位到对应文件,直接二进制编辑器大概,搜索原反射名
可看到资源中保存的数据名未改变,要改变有两种方式:
1、重新在编辑器中设置。
2、修改二进制bin,但是需要注意字符串长度与原反射名一致。
代码层探究CreateDefaultSubobject与反射的作用,以UE4Editor为例。
| 序号 | .cpp | gen.cpp | 结果 |
|---|---|---|---|
| 1 | FP_Gun→FP_Gun |
FP_Gun→FP_Gun111 |
编译后重新打开调试器,可看到面板的反射名变了,且加载的资源、数据项、数据依旧正常。 |
| 2 | FP_Gun→FP_Gun111 |
FP_Gun→FP_Gun |
编译后重新打开调试器,面板可搜索到FP_Gun,但是不可编辑,也缺少了很多项数据,编辑器中运行时崩溃。 |
| 3 | FP_Gun→FP_Gun111 |
FP_Gun→FP_Gun111 |
编译后重新打开调试器,可看到面板的反射名变了,且数据项都正常,只是缺少数据;与1情况相同,只是数据都清空。 |
| 4 | FP_Gun→FP_Gun111 |
FP_Gun→FP_Gun11 |
与3相同 |
所以为什么 2 的结果会崩溃,我有点迷茫。。。。。。崩溃点:
明明他妈的构造都创建出对象了,到这里就空了。
这个暂时不用管。
对象相关
其他
带【*】为重点
https://blog.csdn.net/ZFSR05255134/article/details/116763773
https://www.cnblogs.com/wellbye/p/5808894.html?utm_source=itdadao&utm_medium=referral
https://blog.csdn.net/qq_29523119/article/details/119420238
https://zhuanlan.zhihu.com/p/61042237