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. SequoiaDB 笔记

    SequoiaDB 笔记 这几天翻了翻SequoiaDB的代码,记了点笔记.不保证下面内容的正确性(肯定有错的地方) 个人观感 优点 代码还不错,设计也算简洁. EDU和CB的使用让整个系统变得简单很 ...

  2. pdo 整套类的封装,保存修改查询

    <?php /** * */ class Db{ private $host = ''; private $port = ''; private $user = ''; private $pas ...

  3. 转载-V.I.Arnold, beyond a mathematician

    转自-http://blog.renren.com/blog/248100754/471276636 在AMS的首页上找到了三条讣告,依次是V.I.Arnold(1937-2010),W.Rudin( ...

  4. modelsim(3) - summary ,issue,tips

    1) the OEM of modelsim is 10 times slower than offical questa 2)how to the file full path in the mod ...

  5. Python发送邮件(支持中文)

    # -*- coding: utf-8 -*- from email.header import Header from email.mime.text import MIMEText import ...

  6. 使用php技术实现无刷新的上传文件

  7. viewgroup用addview添加的view不显示问题

    先看代码: public class MyviewGroup extends ViewGroup { private final Context context; private View view; ...

  8. 如何在SqlServer中获取前端连接的IP地址,计算机名等信息

    在一些需求中,可能我们需要知道连接到SqlServer的前端程序的一些系统信息,比如前端连接的计算机名称,IP地址,什么时候开始请求连接,什么时候结束连接等信息. 如果你对SqlServer的系统函数 ...

  9. Shell 的变量功能

    搜寻路径PATH(系统预设变量) 执行命令时,系统透过PATH得路径顺序搜寻指令,如果再搜寻完后还找不到该指令,就会打印错误讯息[command not fount].   环境变量 进入shell之 ...

  10. Servlet学习三——传输文件

    最先在考虑传输文件时,想通过java写一个文件上传案例,传给Servlet,Servlet再保存至数据库中,但苦于一直没找到实例,听说Flex有实际的例子,就直接用Flex例子来测试了.本文的顺序为: ...