Unreal 模块设计(Modules)

Unreal对C++代码有一套构建系统UBT(Unreal Build Tools),从引擎自身到用户的代码,都由UBT以Module为单位进行构建。一个Module相当于一个库,被UBT构建后,在运行时被加载和使用。UBT相当于UE的一套CMake+编译系统,一来隔离平台差异,二来用统一的方式来管理依赖比较优雅。下面罗列一些Unreal Module的特点(后面称Module为模块)。

结构和构建

模块的根目录有一个配置文件,名为YourModuleName.Build.cs,基本内容如下,声明了

// ...

// 声明预编译头的用法
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;    
// 声明公开依赖的其它模块,这里依赖了一些引擎的基本模块
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
// 声明私有依赖的其它模块
PrivateDependencyModuleNames.AddRange(new string[] {  });

// ...

构建时,UBT基于这些依赖关系去引用其它模块的头文件,并链接它们的代码,进行编译。Unreal也提供了工具(右键uproject文件)来生成对应IDE的工程文件(比如.sln或.xcodeproj),方便用IDE打开。

预编译头(PCHs)

预编译头是为了编译性能而设计的机制,将常用的、通用的、不经常变的头预先编译一次,后续只要不改变就不重新编译,从而提升编译性能。

用户定义模块时,可以选择创建自己的PCH,使用依赖模块的PCH,或不使用PCH。

上面的Build.cs文件中有定义PCHUsage,创建一个模块时的默认选项是UseExplicitOrSharedPCHs,绝大部分模块都使用UseExplicitOrSharedPCHsNoPCHs即可。

所谓使用SharedPCHs,就是由Engine在依赖的诸多模块中,选择一个最合适的模块所定义的预编译头。而Explicit的,就是使用本模块显式定义的预编译头

用户可以在一个模块的Build.cs中定义PrivatePCHHeaderFile = "xxx.h"SharedPCHHeaderFile = xxx.h

  • PrivatePCHHeaderFile是为当前模块指定的预编译头
  • SharedPCHHeaderFile则是为其它依赖本模块的其它模块提供的可选预编译头,不是给本模块用的

暴露性(给其它模块调用)

模块的根目录通常有Private和Public两个路径,想要暴露给其它模块使用的代码,要放在Public中,但还不够,需要对代码做进一步定义。

在UCLASS声明中添加MinimalAPI,可以允许该类在其它模块代码中被用于类型转换,或调用inline的函数

UCLASS(Blueprintable, MinimalAPI)

在成员变量/函数前声明本模块API字样,可以允许其它模块代码使用此成员变量/函数

HEHE_API void DoSomething();

在类名前声明本模块API字样,可以允许整个类被其它模块的代码使用

class HEHE_API AHeheActor : public AActor

可见性(public/private)

一个模块要使用另一个模块,需要:

  • 引用其头文件
  • 在Build.cs文件中的依赖中添加该模块(PublicDependencyModuleNamesPrivateDependencyModuleNames

除了C++本身的可见性,UBT在处理依赖时,也有一些可见性规则。

  • 单层依赖,一个模块A依赖另一个模块B,UBT在构建A模块时会去包含B模块的头文件,并链接B模块的代码
  • 多层依赖,模块A以依赖模块B,模块B以private依赖模块C,A并不会直接包含C的头文件或链接C的代码
  • 多层依赖,模块A以依赖模块B,模块B以public依赖模块C,A会直接包含C的头文件,但不会链接C的代码

所以在实际设计模块代码时,将只有本模块才使用的依赖项,作为PrivateDependencyModuleNames依赖,如果依赖项可能被本模块的用户使用,就应该加入到PublicDependencyModuleNames

实现

要让UE引擎识别模块,需要在源码的任意处声明IMPLEMENT_MODULEIMPLEMENT_GAME_MODULEIMPLEMENT_PRIMARY_GAME_MODULE,例如默认的C++游戏工程会自动生成一个模块名.cpp,里面定义了IMPLEMENT_PRIMARY_GAME_MODULE。一个游戏工程里通常只有一个一个IMPLEMENT_PRIMARY_GAME_MODULE

模块可以定义一个实现类,通过IMPLEMENT_MODULE方法注册给引擎,这个类中可以实现一些有用的回调,比如StartupModuleStartupModule如下

// Hehe.h
class FHeheModule : public IModuleInterface
{
    virtual void StartupModule() override;
    virtual void StartupModule() override;
}

// Hehe.cpp
IMPLEMENT_MODULE(FHeheModule, ModuleTest);

其它模块也可以调用实现类的成员函数

FModuleManager::Get().LoadModuleChecked<FHeheModule>(TEXT("Hehe")).DoSomething();

一个模块如果依赖其它GameModule,它就应该被声明为GameModule,GameModule的好处是热更新。如果希望做一个功能相对独立的,可以发布给其它人使用的模块,则可以定义为普通Module。定义为GameModule的模块如果重载了FDefaultModuleImpl实现类,还需要定义IsGameModule()方法来返回true

重要属性

模块要么在.uproject中加载,要么在.uplugin中加载。在这两个文件中,可以定义一些模块的属性:

  • Type:默认是Runtime,除了独立运行都可以,可以进一步限制模块的使用场景,仅编辑器或者仅服务器/客户端等,详见EHostType::Type
  • LoadingPhase:模块的加载阶段,默认是Default是在引擎初始化期间,GamePlay模块加载完后加载,详见ELoadingPhase::Type
  • IncludelistPlatforms / ExcludelistPlatforms: Win32, Win64, Mac, Linux, Android, IOS等
  • IncludelistTargets / ExcludelistTargets:Game, Server, Client, Editor, Program等
  • IncludelistTargetConfigurations / ExcludelistTargetConfigurations:ebug, DebugGame, Development, Shipping, Test等

详细属性列表可见Unreal Engine Modules。上述的Include/Exclude是UE5的属性,在UE4中叫做Whitelist/Blacklist。

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容