|
接上篇
- 反射-UHT
① 理解generated.h和gen.cpp
② 理解MODULENAME_API含义(用来做dll导出的,容易出错)
③ 掌握常用宏的含义和用法(反射的重要标记):UCLASS,USTRUCT,UENUM,UPROPERTY,GENERATED_BODY等
④ 清晰理解类型和对象的关系,ClassReference VS ObjectReference
(类型引用 和 对象引用)
⑤ 掌握通过反射遍历对象属性、读取写入
⑥ 掌握通过反射遍历对象函数并调用的方式
⑦ 通过对象找类型,通过类型找对象
⑧ 理解“对象用类型描述,类型也是对象”
UE4反射机制概要
① 首先了解反射:反射是指在运行状态下,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。(包括在编译时未确定的类或者对象)这种动态获取程序信息以及动态调用对象的功能称为反射。
② 支持反射的语言有Java、C#等等,但C++本身是不支持反射的,所以UE4在C++基础上搭建了一套自己的反射系统。通过反射,UE4可以实现序列化,GC(垃圾回收),网络复制,C++蓝图通信等重要功能。
③ 当一个头文件中include了“xxx.generated.h“,意味着这个头文件加入了反射系统。那些UPROPERTY,UFUNCTION之类的宏,也标记着这些方法属性等等加入了UE4的反射系统,加入了反射系统,UE4才能帮你做GC(垃圾回收),你才能实现蓝图C++通信等等很多功能。
④ UnealBuildTool和UnrealHeadTool可以看作是UE4实现反射系统的工具。
UBT通过扫描头文件,记录所有包含反射类型的modules(模块),当其中有头文件改变时,就会用UHT更新反射数据。UHT解析头文件,扫描标记,生成用于支持反射的C++代码。
(个人总结,参考某大佬的文章 https://zhuanlan.zhihu.com/p/60622181)
- CoreUObject
① GC(垃圾回收):
UE4采用了标记清扫的垃圾回收方式。从一个Root集合出发,遍历所有存在引用关系的对象。遍历完成后就可以标记出存在引用的和没有引用的了,然后清理掉不存在引用的对象。
GC方式可以类比C#,有UPROPERTY标记才能被引擎GC。
UE4重载了new、delete关键字,不要使用。
FGCObject : :AddReferencedObjects可以手动添加不希望被垃圾回收的UObject的硬引用。
② ClassDefaultObject(类默认对象):理解类型和对象实例化,模版
理解ClassDefaults作为模版的作用
理解ClassDefaultObject在序列化中的意义作用
通过UClass : : GetDefaultObject可以获得ClassDefaultObject的信息
③Package:理解对象的相互组织方式
对象可以包含子对象
序列化时,把一系列对象用一个对象包起来,这一个对象叫Package
Package也可以互相引用,根据对象相对路径
Which——虚幻C++中有哪些套路需要掌握?
- 模块链接:几个常用属性.AddRange
比如最近我要在C++中引用到NiagaraComponent,但却找不到头文件
此时需要在项目的build.cs中添加模块链接
- 创建Actor方法:ConstructorHelpers,CreateDefaultSubobject,SetupAttachment
- GamePlay框架继承
① 尽量别在关卡蓝图写逻辑:个人认为那些不复用的,关卡自身的特殊逻辑,触发一些事件之类的还是写在关卡蓝图比较方便,比如我们在做的解谜游戏,每关都会有一些特殊的关卡设计。但如果做一个吃鸡类游戏,关卡蓝图中应该是很干净的。
② 遵循引擎结构,善用GamePlay框架比如GameInstanse,GameMode,GameState,PlayerController等,继承使用,后期扩展和联机会十分友好。
- C++与蓝图交互
① UPROPERTY UFUNCTION宏的使用,各种函数属性说明符BlueprintCallable,EditAnywhere等等。
官方文档-函数说明符 官方文档-属性说明符
② C++基类写逻辑,蓝图继承然后可视化配置数据。这种方式易扩展高性能,十分舒服。
③ 善用函数库。
- 事件绑定
① DELEGATE(委托),MULTICASTDELEGATE(多播),DYNAMIC(动态)
② 输入事件绑定:BindAxis,BindAction
③ 碰撞事件绑定:Hit,Overlap(AddDynamic)
④ Slate&UMG Event:SLATE_EVENT(FOnClicked,OnClicked)
⑤ 计时器:FTimerManager::SetTimer,ClearTimer
- 引擎常用方法
① Engine/Class/Kismet中的各种库十分有用。
② Kismet/GamePlayStatics很常用,可以访问GamePlay的很多对象。
③ UKismetSystemLibrary,系统目录等功能。
④ UKismetMathLibrary,数学库。
Which——有哪些编码注意事项?
- 用插件在项目间共享代码,动手编码前多思考架构
- 变量类型尽量偏向抽象基类,比如声明一个粒子的引用时,使用UFXSystemComponent而不是ParticleSystemComponent或者NiagaraSystemComponent,可以适应多变的情况。
- Include头文件时,请Include What You Use(只包含使用到的头文件),加快编译速度
- 善用C++前置声明,加快编译
- 善用蓝图函数库
- C++代码里不要做数据配置。 尝试数据驱动或者在蓝图原型中配置数据。
- 没有C++基类的蓝图类,可以重新设置基类,注意先备份。
- 重构了C++基类名字,导致蓝图找不到父类?可以用CoreRedirects修复。
- 尽量不修改引擎代码,如果修改,做好注释或记录
Which——虚幻C++有哪些学习难点?
- 数据结构与算法,操作系统,多线程
- 数学物理相关知识例如线性代数等
- 渲染,动画,AI等游戏开发知识
- 足够的项目开发经验才能设计出强壮的代码框架
- 理解不了别人的代码
6.How——如何学习虚幻C++?
- 学会模仿代码比如模版项目,引擎源码,社区项目等
- 模仿的基础上尝试修改
- 学会总结与记录
- 积累有用的代码功能块,对于概念,尝试用思维导图加深理解
- 必须要项目实战
C++与蓝图的合适比例
- 根据80/20原则,20%核心用C++,80%表层用蓝图(根据项目类型和个人水平而定)
- 大概规则:
偏向引擎底层,偏向性能热点,偏向稳定的,采用C++
偏向表现层,偏向经常操作的,偏向多变的,采用蓝图
三、虚幻引擎源码剖析经验分享
1.源码剖析方法论
- 时空观
① 时:注重时间上的先后,观察函数的调用流程,事件的触发时机
② 空:注重数据的吞吐转换,观察信息的采集,数据的加工消化利用
③ 时空的交互,组成了有机的功能架构支撑,调度起资源,启动内循环,响应外部刺激
- 信息论
① 最小信息原则=依赖最少=越稳定
② 信息本质也是能量,只有掌握足够信息,才足以实现某些功能
③ 数据是信息,代码结构是信息,项目协作方式也是信息
- 保持谦逊但不迷信
① 大部分认为引擎代码不够好的时候,其实都是自己的问题。
② 引擎开发人员也会犯错,有错很正常。
③ 引擎存在某些历史遗留问题,开发人员也没办法,不要太苛责。
- 耐心比好奇心更重要
2.源码剖析工具
- 工具越简单越好,专注核心内容,别浪费时间调整格式。
① 只用VisualStudio阅读源码,额外的都是负担。
② 放弃UML图系列工具,只取其思想,放弃其形式。
③ 完全理解的内容,才需要画图加深理解。
⑤ 思维导图是个不错的选择。
⑥ 写文章可以尝试用Markdown码字,Processon画流程图
- 调试过程
① 开Debug配置编译源码(此篇文章中有配置选项说明 InsideUE4基础概念)
② 边调试边记录
③ 在引擎源码内添加Debug输出,可以确定数据内容先后顺序
④ 以主线为主,支线可以先放放
- 用于寻找的工具
① WidgetReflector定位代码块
② VisualAssistX查找所有引用调用(正版很贵很贵很贵)
③ VisualStudio字符串查找
④ Everything工具定位文件
⑤ 开Debug断点查事件调用
3.注意事项
- 能找到办法调用就不要拷贝代码
- 事先理解各模块对象的组织关系,有助于找到调用的代码
- 别在Runtime下调用Editor代码,不合法且费劲
- 找到目录代码块之后,注意输入输出,小心内部依赖
有任何问题请评论或私信,感谢关注评论点赞收藏! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|