UE4 Modules 和 PCH


本文参考:

https://zhuanlan.zhihu.com/p/107270501

https://docs.google.com/presentation/d/1rSFFQk7RxNAHevROfVvUNviUfIntLkO_HpdvzHLkNEs/view#slide=id.g6e0e4b3bcf_2_326

UE4模块的加载方式

待补充

Modules

模块是什么?一堆dll 类的集合,UE4被分为了1000个模块

有什么作用?组织代码,可移植性复用性,可以获得更快的编译和链接性能,也可以确认模块何时加载

如何做一个Module? B U I L D

Build

Build,建立一个固定的目录

[YourModuleName].Build.cs文件的作用用

描述如何编译模块、模块的依赖等等

模块的编译只取决于.Target.cs和.Build.cs而不取决于sln

一个最少代码的模块包括:

和模块名相同的C#类

构造函数中处理模块的依赖性,必须依赖Core,里面包括了很多模块相关代码

Use

use 使用模块

模块的代码并不是默认对其他的模块暴露的

你需要队每个函数或者类进行显式的指定他是否导出

如果你不打算暴露任何代码到模块外,你可以不用分private和public的文件夹

这些代码在蓝图中可以访问,但是在其他模块中不能使用

通过给UCLASS添加MinimalAPI(最小)标识来表示将其显式导出到可以被其他模块使用

要将某个函数暴露给其他模块使用必须在函数前添加[YourModuleName]_API,全部大写字母

要将整个类暴露就在类前加[YourModuleName]_API,全部大写字母,这样整个类的public部分都会暴露

由于这个类依赖了Actor类,所以我们在模块的编译时候会报错

要用的话,就需要添加头文件、添加模块依赖关系

这里在publicDependency进行一个添加,因为其他的模块要是引用这个NickNameActor他自然也会需要对Actor有依赖

关于private 和 public dependency的问题:

当这个关系只有一层时没有任何区别

当涉及到三个模块时,这里的依赖的public private就会起作用

头文件内容传递,表示这个模块可以调用其他模块全部定义在在头文件中的内容,如果有内容是 .cpp中定义,则会有无法解析的外部符号错误(就是将头文件一起编译,但是不会做链接,对应IncludePathModuleNames,现在基本已经弃用了)

把定义写在头文件中意味着他可能会被多次编译,将拖慢编译速度

DependencyModuleNames,则是将两个模块链接,则表示这个模块可以调用到.cpp中定义的内容

private会切断头文件路径链接的传递,你的模块被其他的模块依赖时这些引入的头的链接都不会传递

非常重要的依赖传递图

img

当你的模块被其他的模块引用的时候,情况很多,上图基本包括了所有情况

你private 引用了别的module,引用了你module的module尝试找你的子模块的代码的时候就会出现文件找不到的错误

总结选择:建议使用private链接,他们会减少编译时间!

Implement

Implement,实现模块

#include "Modules/ModuleManager.h" // which is in core module


IMPLEMENT_MODULE(FPlatformCommonModule, PlatformCommon)

这句话可以在你的模块的任何地方写,这句调用就是将你的模块的主类给暴露给引擎

实现这个模块的类必须继承这个IModuleInterface的接口,一般来说做个空类就好了可以使用UE自带FDefaultModuleImpl类

有关module的接口有很多,最基本的就加载和卸载module的生命周期

有关很少会用到的GameplayModule,游戏工程本身也是一个模块,一般所有的模块都是用给这个游戏模块的,很少会反过来依赖游戏模块

Load

LoadModule

需要选择自己的module是在什么时候被加载的和面向什么target,通过选择Type,最常用的是Runtime和Editor

Depend

depend

只有在依赖链上的模块才会被编译

通过添加DependencyModuleNames来配置依赖,如果没有模块依赖你的模块,也可以添加Target.cs

PCH

Prcompiled Header

一般的头文件不会自己编译,而是在cpp引入的时候编译

这样的话就有一个很大的头文件重复编译量

让头文件只编译一次

PCH定义了一个头文件,包含了所有最常用的头文件,然后他们会先于所有的文件编译,他们不会重编,除非他们引入的头文件发生了改变

必须注意的是,如果PCH重编了,所有的这个模块的CPP文件都会重编,最好是用于引擎的头文件或者非常少改动的头文件

有几种PCH,

private PCH

构建你模块自己的PCH,把他定义在Build.cs文件里

不应该在任何头文件或者CPP文件中引入PCH文件,他们会由UBT工具自动注入到你的模块里

PCH是一种优化层面的东西

你的代码写法应该是就算PCH关掉了也能正常跑

shared PCH

除了自己定义PCH之外也可使用别人定义的PCH,模块自己定义的SharePCH不能给自己用,只能给依赖了这个模块的模块使用

只有引擎模块才能创建Share PCH

Private PCH 和 Shared PCH又是什么?有啥用?

答:它俩是特殊一点的PCH,只需记住几(×)1(√)点,

Private PCH是给本模块用的PCH,Shared PCH是给依赖本模块的模块用的PCH
一个模块可以使用其他模块定义的Shared PCH
一个模块不能使用自己定义的Shared PCH
模块对PCH的选择只有三个:
-使用本模块定义的Private PCH
-使用引擎自动从**本模块依赖的模块**定义的Shared PCH中,选出一个最佳PCH
-不使用PCH

两者需要在模块的build.cs文件中手动指定

UE会自动选择最佳的SharePCH以便尽可能多的覆盖本模块用到的头文件

Shared PCH的选择范围是本模块依赖的模块中定义的Shared PCH

假如模块A依赖Slate,Core以及CoreUOject模块,且没定义Private PCH,
且只有Slate及Core模块中定义了Shared PCH,
那么,A的Shared PCH就只能从Slate, Core两个模块中选
选哪一个呢?

基于得分来选择——以它俩依赖的<定义了Shared PCH的>模块数量来作为它们的分数

假设Core模块依赖了TraceLog, Json,假如只有TraceLog模块中定义了Shared PCH,
那么它得1分

假设Slate模块依赖了Core, SourceControl, Json模块,假如Json模块中定义了Shared PCH,
那么它得2分(Core 1 + Json 1)

而且很容易看出,由于Slate的依赖项里包含了Core,那么它Shared PCH很可能包含了大部分Core模块里的头文件,以及其它额外的头文件,这样,我们就会理所当然的选择使用Slate的Shared PCH,因为我们的模块使用到的头文件,也在Slate的Shared PCH里的概率更大。

因此我们将会选择Slate模块的Shared PCH作为本模块的PCH。

PCH使用模式只选择UseExplicitOrSharePCHs,这个模式下,你没设置PCH就会使用引擎PCH,你设置了PrivatePCH就可以使用

posted @ 2021-07-15 20:45  飞翔的子明  阅读(1733)  评论(2编辑  收藏  举报