ue4的代码是模块的形式来组织

在源码层面,一个包含*.build.cs的目录就是一个模块

这个目录里的文件在编译后都会被链接在一起,比如一个静态库lib,或者一个动态库dll。

不管是哪种形式,都需要提供一个给外部操作的接口,也就是一个IModuleInterface指针。

*注意这里并不是说调用模块内任何函数(或类)都要通过该指针来进行,实际上外部代码只要include了相应的头文件,就能直接调用对应的功能了(比如new一个类,调一个全局函数等),因为实现代码要么做为lib被链接进exe,或是做为dll被动态加载了。

这个IModuleInterface指针是用来操作做为整体的模块本身的,比如说模块的加载、初始化和卸载,以及访问模块内的一些全局变量(向下转成具体的模块类型后)

外部获取这个指针,只有一个办法,就是通过FModuleManager上的LoadModule/GetModule。

而FModuleManager去哪里找需要的模块呢?

1、在动态链接情况下,根据名字直接找对应的dll就行了,做为合法ue4模块的dll,必定要导出一些约定的函数,来返回自身IModuleInterface指针。

2、在静态链接时,去一个叫StaticallyLinkedModuleInitializers的Map里找,这就要求所有模块已把自己注册到这个Map里。

以上2点基本就是FModuleManager::LoadModule的内容。

而每个模块为满足以上约定,也需要插入一些例程代码,这就是IMPLEMENT_MODULE干的事。

在静态链接时:

    // If we're linking monolithically we assume all modules are linked in with the main binary.
#define IMPLEMENT_MODULE( ModuleImplClass, ModuleName ) \
/** Global registrant object for this module when linked statically */ \
static FStaticallyLinkedModuleRegistrant< ModuleImplClass > ModuleRegistrant##ModuleName( #ModuleName ); \
/** Implement an empty function so that if this module is built as a statically linked lib, */ \
/** static initialization for this lib can be forced by referencing this symbol */ \
void EmptyLinkFunctionForStaticInitialization##ModuleName(){} \
PER_MODULE_BOILERPLATE_ANYLINK(ModuleImplClass, ModuleName)
template< class ModuleClass >
class FStaticallyLinkedModuleRegistrant
{
public: FStaticallyLinkedModuleRegistrant( const ANSICHAR* InModuleName )
{
// Create a delegate to our InitializeModule method
FModuleManager::FInitializeStaticallyLinkedModule InitializerDelegate = FModuleManager::FInitializeStaticallyLinkedModule::CreateRaw(
this, &FStaticallyLinkedModuleRegistrant<ModuleClass>::InitializeModule ); // Register this module
FModuleManager::Get().RegisterStaticallyLinkedModule(
FName( InModuleName ), // Module name
InitializerDelegate ); // Initializer delegate
} IModuleInterface* InitializeModule( )
{
return new ModuleClass();
}
};

FStaticallyLinkedModuleRegistrant是一个注册辅助类,它利用全局变量的构造函数被crt自动调用的特性,实现自动注册逻辑。

它在构造函数里把一个“注册器”添到前面说的StaticallyLinkedModuleInitializers里,而这个注册器的内容就是创建并返回相应模块。

在动态链接时:

    #define IMPLEMENT_MODULE( ModuleImplClass, ModuleName ) \
\
/**/ \
/* InitializeModule function, called by module manager after this module's DLL has been loaded */ \
/**/ \
/* @return Returns an instance of this module */ \
/**/ \
extern "C" DLLEXPORT IModuleInterface* InitializeModule() \
{ \
return new ModuleImplClass(); \
} \
PER_MODULE_BOILERPLATE \
PER_MODULE_BOILERPLATE_ANYLINK(ModuleImplClass, ModuleName)

声明了一个dllexport函数,功能就是创建并返回相应模块。

实际的应用:

1、如果模块本身实在没什么特别的,那么就:

IMPLEMENT_MODULE(FDefaultModuleImpl, ModuleName)

FDefaultModuleImpl是一个IModuleInterface的空实现,什么都没干,ModuleName则是模块名字,很重要,其它地方要加载模块,就靠这个名字了。

2、如果模块有自己的初始化逻辑,那么应该实现自己的IModuleInterface子类,比如说MyUtilModule,然后:

IMPLEMENT_MODULE(MyUtilModule,MyUtil)

这里类名带Module后缀,而模块名不带,是ue4的惯例。

3、如果模块是一个包含游戏逻辑的模块(相对于通用功能型模块),那么可以用一个特殊的宏IMPLEMENT_GAME_MODULE,不过目前看来它和前者没啥差别,可能未来有所扩展

4、更特别的是,如果模块是表示当前游戏项目的“主模块”,那么应该用一个更特殊的宏IMPLEMENT_PRIMARY_GAME_MODULE,而且在构建一个UEBuildGame类型的Target时,必须有一个这样的模块。

当IS_MONOLITHIC,构建成单个exe时:

#define IMPLEMENT_PRIMARY_GAME_MODULE( ModuleImplClass, ModuleName, DEPRECATED_GameName ) \
/* For monolithic builds, we must statically define the game's name string (See Core.h) */ \
TCHAR GInternalGameName[] = TEXT( PREPROCESSOR_TO_STRING(UE_PROJECT_NAME) ); \
/* Implement the GIsGameAgnosticExe variable (See Core.h). */ \
bool GIsGameAgnosticExe = false; \
IMPLEMENT_DEBUGGAME() \
IMPLEMENT_FOREIGN_ENGINE_DIR() \
IMPLEMENT_GAME_MODULE( ModuleImplClass, ModuleName ) \
PER_MODULE_BOILERPLATE \
void UELinkerFixupCheat() \
{ \
extern void UELinkerFixups(); \
UELinkerFixups(); \
}

非单体构建时:

#define IMPLEMENT_PRIMARY_GAME_MODULE( ModuleImplClass, ModuleName, GameName ) \
/* Nothing special to do for modular builds. The game name will be set via the command-line */ \
IMPLEMENT_GAME_MODULE( ModuleImplClass, ModuleName )

统一来看,其实也就比普通模块多做了三件事,一是设置游戏名字GInternalGameName,二是设置了GIsGameAgnosticExe=false,三是多了个UELinkerFixupCheat暂且不究。

GIsGameAgnosticExe是一个有趣的变量,它表示当前这个exe是特定于某游戏的?还是一个通用的加载器?

如果整体编成一个exe文件,即IS_MONOLITHIC,那肯定是特定于某游戏,这时游戏名GInternalGameName必定是已知固定的,所以以宏参数的形式直接硬编码在exe里了。

与之相反,当使用模块化构建时(通过给ubt传入-modular参数),将会生成一个exe和一堆dll,并且能加载任何其它以dll形式存的游戏模块,游戏名是未知的,是通过命令行参数来决定要加载哪一个游戏,所以也就用不着GInternalGameName变量了。

5、当构建一个工具程序(TargetType.Program)时,可以使用专门定制的IMPLEMENT_APPLICATION:

最特别的是它竟然提供了一个FEngineLoop GEngineLoop,而这个变量存在于一般的Game/Editor构建时都会链接的【Launch模块】中。

不过这也正常,因为在工具程序中一般是要自己写main函数的,所以就用不着重量级的Launch模块了。

ue4 模块的构建和加载的更多相关文章

  1. linux 编译,链接和加载

    1.   序 最近在折腾各种.so,碰到了一些问题,一开始对于很多错误也没有头绪,茫然不知所措.索性化了一天多时间将<<程序员的自我修养—链接.装载与库>>中部分内容略读了一遍 ...

  2. 前端性能和加载体验优化实践(附:PWA、离线包、内存优化、预渲染)

    一.背景:页面为何会卡? 1.1 等待时间长(性能) 项目本身包/第三方脚本比较大. JavaScript 执行阻塞页面加载. 图片体积大且多. 特别是对于首屏资源加载中的白屏时间,用户等待的时间就越 ...

  3. [转]QT4.8.5+qt-vs-addin-1.1.11+VS2010安装配置和QT工程的新建和加载

    1.下载windows下的QT库 QT4.8.5 for vs2010: http://download.qt-project.org/official_releases/qt/4.8/4.8.5/q ...

  4. keras中的模型保存和加载

    tensorflow中的模型常常是protobuf格式,这种格式既可以是二进制也可以是文本.keras模型保存和加载与tensorflow不同,keras中的模型保存和加载往往是保存成hdf5格式. ...

  5. 【sklearn】from sklearn.extermals import joblib(保存模型和加载模型)

    原创博文,转载请注明出处! sklearn中保存和加载模型的方法 1.载入模块 from sklearn.externals joblib. model = joblib. # -*- coding: ...

  6. 从头学pytorch(十二):模型保存和加载

    模型读取和存储 总结下来,就是几个函数 torch.load()/torch.save() 通过python的pickle完成序列化与反序列化.完成内存<-->磁盘转换. Module.s ...

  7. 精简CSS代码,提高代码的可读性和加载速度

    前言 提高网站整体加载速度的一个重要手段就是提高代码文件的网络传输速度.之前提到过,所有的代码文件都应该是经过压缩了的,这可提高网络传输速度,提高性能.除了压缩代码之外,精简代码也是一种减小代码文件大 ...

  8. Unity 打AssetBundle和加载方案

    一.如何组织assetBundle: unity5以前,打包需要自己去找依赖,然后需要按照拓扑图顺序压入AB栈,这样在最后打AB时才能有效利用依赖(栈内已有的AB才能作为依赖). unity5.x后, ...

  9. Demo示例——Bundle打包和加载

    Unity游戏里面的场景.模型.图片等资源,是如何管理和加载的? 这就是本文要讲的资源管理方式--bundle打包和加载. 图片 Unity游戏资源管理有很多方式: (1)简单游戏比如demo,可以直 ...

随机推荐

  1. jfinal相关

    1:jfinal工具类 1):密码加密工具 HashKit.md5(String password);2):数据缓存工具类 chacheKit3):获取项目路径工具类: PathKit 4):读取文件 ...

  2. mysql 忘记root 密码的解决方法

    LINUX 1.切换root 用户 2.停止mysqld 服务 /etc/inid.d mysqld stop 3.跳过验证登录 mysqld_safe --skip-grant-tables &am ...

  3. 浅述python中argsort()函数的用法

    由于想使用python用训练好的caffemodel来对很多图片进行批处理分类,学习过程中,碰到了argsort函数,因此去查了相关文献,也自己在python环境下进行了测试,大概了解了其相关的用处, ...

  4. Windows转到linux中,文件乱码,文件编码转换 & 解决sqlplus连接oracle乱码

    转载:http://www.cnblogs.com/wanyao/p/3399269.html 最近,学习又重新开始Linux学习,所以一直在Centos中,昨天一朋友把他在Windows下写的C程序 ...

  5. ubuntu 14.4 中文语言包安装问题

    1.安装前,请选择更新源,在系统设定 system setting 里,选择software and updates 里,选择中国的源,用于快速更新语言包 2.在language support里选择 ...

  6. htnl5中设置文本单行显示,超出部分打省略号,鼠标移到文本时alt出全部文本内容

    Html代码: 1.<span class="my-span" title="无数无数无数无数无数">机构</span> Css样式: ...

  7. 简单的VC++ ADO帮助类

    首先看头文件 #pragma once #import "c:\Program Files\Common Files\System\ado\msado15.dll" no_name ...

  8. 基于log4net的支持动态文件名、按日期和大小自动分割文件的日志组件

    最近处理一个日志功能,用log4net的配置不能完全满足要求,所以在其基础上简单封装了一下,支持以下功能: 1 零配置 内置默认配置,引用dll后不需要添加或修改任何配置文件也可以使用 2 动态指定文 ...

  9. Net分布式系统之二:CentOS系统搭建Nginx负载均衡

    一.关于CentOS系统介绍 CentOS(Community Enterprise Operating System,中文意思是:社区企业操作系统)是Linux发行版之一,它是来自于Red Hat ...

  10. VFP自定义函数StringFormat (仿.NET String.Format 方法)

    VFP仿.NET String.Format 方法 将指定字符串中的每个{x}替换为相应值,并返回文本 *-- 调用格式 StringFormat("日期{2},字符{1}",&q ...