[UE4C++程序]GameModule与Plugin

当专案建立的时候,引擎会自动产生一个同名的Game Module在Source资料夹底下。我们当然可以将所有撰写的C++类别全部放在这个Module中,可是当专案越来越大,若还是将所有的功能都放在同个Module下,不仅仅会造成管理上的混乱,而且编译时间也会增加。

试着想像当我们随便改动一个h档或cpp档的参数就要编译几10分钟的情况?由于UE4在一个Module中的cpp数量到达32个的时候就会自动启动编译档案合并的机制(Unity Build),虽然UE4中有参数可以控制这个机制是否启用,但却是舍弃掉了整体的编译时间。因此,如何将功能适当的切到各别Module中,就变成了一件非常重要的程式架构设计问题。

那么实际上UE4的Module系统是怎么运作的呢?

每一个独立的Module基本上是由${MODULE_NAME}.build.cs与其下的C++程式码所组成,而在UE4中,游戏中的Module主要被分成以下几种实作Macro定义:

  • IMPLEMENT_PRIMARY_GAME_MODULE
  • IMPLEMENT_GAME_MODULE
  • IMPLEMENT_MODULE

到底这些实作彼此之间有什么不同?

其实,这些Macro定义在最后都会被展开成IMPLEMENT_MODULE中的实作,它们之间在本质上并没有不同,主要是用提供Module初始化Function的实作给UE4中的ModuleManager呼叫,参照Code 2.4.1:

  1. #define IMPLEMENT_PRIMARY_GAME_MODULE ( ModuleImplClass, ModuleName , GameName ) \
  2. IMPLEMENT_GAME_MODULE ( ModuleImplClass, ModuleName )
  3.  
  4.  
  5.  
  6. #define IMPLEMENT_GAME_MODULE ( ModuleImplClass, ModuleName ) \
  7.  
  8. IMPLEMENT_MODULE ( ModuleImplClass, ModuleName )
  9.  
  10.  
  11.  
  12. #define IMPLEMENT_MODULE ( ModuleImplClass, ModuleName ) \
  13.  
  14. extern “C" DLLEXPORT IModuleInterface * InitializeModule () \
  15.  
  16. { \
  17.  
  18. return new ModuleImplClass(); \
  19.  
  20. } \
  21.  
  22. PER_MODULE_BOILERPLATE \
  23.  
  24. PER_MODULE_BOILERPLATE_ANYLINK (ModuleImplClass, ModuleName )

 

Code 2.4.1 Module的定义Macro,最后都会被展开成IMPLEMENT_MODULE中的实作。其中PER_MODULE_BOILERPLATE让引擎有机会提供一些额外的功能给这个module,例如把原本C++中的New跟delete做Overriding,并将功能导向UE4中的记忆体管理机制。最后的PER_MODULE_BOILERPLATE_ANYLINK其实并没有其他特别的功能,就只是用来做标示,用来说明这个Module相关的定义已经完成。

从上面的程式码中我们可以看出,从Primary Game Module、Game Module到Module总共有三个层级的定义。只是,既然这些Module的定义做的事情都差不多,那为何引擎还要个别提供?

这边虽然没办法完全的掌握设计者的意图,但至少我们可以推敲出区分出这些Module定义名称的好处:若以后想要在Primary Game Module或Game Module层级增加功能的时候,就不会影响到最下层的Module层级的实作。

到这里,或许有人会开始思考这几个层级到底应该用在哪些地方。其实UE4在架构上把程式码拆分成以下几个部份:

  • Engine:位于${UE4_ENGINE_ROOT}/Engine/Source/下面的Developer、Editor、Runtime以及ThirdParty这几个资料夹。
  • Engine Plugin:位于${UE4_ENGINE_ROOT}/Engine/Plugin/。
  • Game:位于${PROJECT_NAME}/Source/。
  • Game Plugin:位于${PROJECT_NAME}/Plugin/。
  • Programs:位于${UE4_ENGINE_ROOT}/Engine/Source/Programs,属于独立运行的工具类程式,里面使用C#或C++分别进行不同的实作。

其中只有位于Game中的Module会使用IMPLEMENT_PRIMARY_GAME_MODULE跟IMPLEMENT_GAME_MODULE这2个实作,从名称中我们可以推测出,作为主要游戏的入口的Primary Game Module只能有一个,其他则都必需设成Game Module。

其他在Engine、Engine Plugin跟Game Plugin底下的Module则都是直接使用IMPLEMENT_MODULE这个定义。

当然,就目前的引擎版本我们是可以全部都用IMPLEMENT_MODULE来实作出上面Module所有效果,但是为了往后更新引擎版本时的兼容性,建议还是照着UE4所定义出来的流程实作。

最后的Programs跟其他的Module不同,里面的C++ Module会使用IMPLEMENT_APPLICATION这个定义,参见Code 2.4.2。

  1. #if IS_MONOLITHIC
  2.  
  3.  
  4. #define IMPLEMENT_APPLICATION ( ModuleName , GameName ) \
  5.  
  6. /* For monolithic builds, we must statically define the game's name string (See Core.h) */ \
  7.  
  8. TCHAR GInternalGameName [64] = TEXT ( GameName ); \
  9.  
  10. IMPLEMENT_DEBUGGAME () \
  11.  
  12. IMPLEMENT_FOREIGN_ENGINE_DIR () \
  13.  
  14. IMPLEMENT_GAME_MODULE ( FDefaultGameModuleImpl , ModuleName ) \
  15.  
  16. PER_MODULE_BOILERPLATE \
  17.  
  18. FEngineLoop GEngineLoop ;
  19.  
  20.  
  21.  
  22. #else
  23.  
  24.  
  25.  
  26. #define IMPLEMENT_APPLICATION ( ModuleName , GameName ) \
  27.  
  28. /* For non-monolithic programs, we must set the game's name string before main starts (See Core.h) */ \
  29.  
  30. struct FAutoSet##ModuleName \
  31.  
  32. { \
  33.  
  34. FAutoSet##ModuleName() \
  35.  
  36. { \
  37.  
  38. FCString :: Strncpy ( GInternalGameName , TEXT ( GameName ), ARRAY_COUNT ( GInternalGameName )); \
  39.  
  40. } \
  41.  
  42. } AutoSet##ModuleName; \
  43.  
  44. PER_MODULE_BOILERPLATE \
  45.  
  46. PER_MODULE_BOILERPLATE_ANYLINK ( FDefaultGameModuleImpl , ModuleName ) \
  47.  
  48. FEngineLoop GEngineLoop ;
  49.  
  50.  
  51.  
  52. #endif

 

Code 2.4.2 在UE4中,Module的Link分成Modular跟Monolithic二种方式:Modular指的是将Module编译成各别的dynamic library再做连结;Monolithic则是指将所有的Module全部编译到同一份Library中。其中Editor预设是Modular,而Game预设是Monolithic。

而要不要把该Module放到Plugin或者是当成Game Module,则要看这个Module需不需要用在不同的专案上使用。若是这个Module中的功能需要让其他专案使用的话,则将该Module制作成Plugin会比单纯的当成Game Module会更有弹性。

 

 

  • 在UE项目添加一个新的Module

本文只简单介绍下创建自定义模块的方法

1. 新建项目并在Source文件夹下建立我们要添加的新模块 
这里写图片描述 
2. 并在新建的NewModule文件夹下创建.h .cpp .Build.cs 后缀的3个文件 
这里写图片描述 
3进入VS建立NewModule(筛选器)并把刚才文件夹里新创建的3个文件拖入NewModule下 
这里写图片描述 
4.为NewModule.Build.cs添加内容(直接从我们项目里的TestModule.Build.cs拷贝即可) 

using UnrealBuildTool;

public class NewModule: ModuleRules
{
    public NewModule(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

        PrivateDependencyModuleNames.AddRange(new string[] { });

        // Uncomment if you are using Slate UI
        // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

        // Uncomment if you are using online features
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");

        // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
    }
}

5. 修改NewModule.h为

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Engine.h"
#include "ModuleManager.h"

class FNewModuleModule : public IModuleInterface
{
public:

  /** IModuleInterface implementation */
  virtual void StartupModule() override;
  virtual void ShutdownModule() override;
};

 

6.修改NewModule.cpp为

// Copyright Sigurdur Gunnarsson. All Rights Reserved. 
// Licensed under the MIT License. See LICENSE file in the project root for full license information. 

#include "NewModule.h"

void FNewModuleModule::StartupModule()
{
  // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}

void FNewModuleModule::ShutdownModule()
{
  // This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
  // we call this function before unloading the module.
}

  
IMPLEMENT_MODULE(FNewModuleModule, NewModule)

6. 进入项目的uproject内添加我们的新模块 
这里写图片描述 
7. 最后在项目里的.Target.cs里添加新的模块名 
这里写图片描述
2个.Target.cs后缀的都需要添加模块名 


8. 使用Editor在NewModule下创建Actor,重新编译成功后,在C++ Class下就能看到新建的NewModule
这里写图片描述 
 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值