[Summary]
Modules and Gameplay Modules in Unreal Engine are powerful features; they allow programmers to independently develop and inherit functionality as well as doing so in a self-encapsulated manner. In this brief post, we’ll go over how to add and register a module to your Project.
[Warnings / Architecture]
There are a few gotchas’ within Modules.
- Modules are one-way relationship only. Unlike having Class A reference B and have B reference A be “okay,” Modules are treated as a self-governing box. If we had Module B referencing A, upon loading, B would not be able to load until A has been fully loaded. If A was referencing B, that would mean that B would then have to load beforehand and so on… You can see where I’m going with this.
- Modules are rather Rigid by design. Say you have two systems, C and D, and each has four “layers” of headers: 1 to 4. D1 includes C1, C2 -> D1, and so forth. Within a single module, this is possible. you can even, half-way through design, throw system E, F, G, etc into the mix while taking care of circular dependencies. Nevertheless, Modules cannot “talk” to each other. You can create Module “0” to have interfaces for both A and B to use, and then establish a mediator-based communication. From an inheritance PoV, however, this means that both A and B “inherit” from “0.”
- Within Unreal Engine 4’s Module system, only what you manually export gets actually exported. Some exceptions apply.
[Rundown ]
To create a module, you need at least 3 files: a .h + .cpp pair for the Module class, and a .Build.cs configuration file. Say that you wish to create the module called “Elephant”:
Elephant.h
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FElephant: public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
Elephant.cpp
#include "Elephant.h"
#define LOCTEXT_NAMESPACE "FElephant"
void FElephant::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin/.uproject file per-module
}
void FElephant::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.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FElephant, Elephant)
Elephant.Build.cs
using UnrealBuildTool;
public class Elephant : ModuleRules
{
public Elephant(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
//Unity controls how Actions are built.
bUseUnity = false;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
}
}
Now, we must make sure that we’re adding it to what we need: potential modules and main Project Module
//inside the module that will be using the "Elephant" Module in Public/Private dependencies, depending on your needs.
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Elephant"
// ... add other public dependencies that you statically link with here ...
}
);
And more importantly, we MUST register it for our Plugin/Project to actually use it!
In the Modules part, add the entry
"Modules": [
{
"Name": "Elephant",
"Type": "Runtime",
"LoadingPhase": "Default"
},
//Other modules if necessary
]
For Plugins, you must input this in YourPluginName.uplugin. For GameModules, it will go in your YourProjectName.uproject. You must add them to this list in order to get it loaded!
[Exporting]
What can be exported:
- Global Functions/Variables
- UStruct structs
- UClass classes
What is exported automatically:
- UEnum enumerators
- Delegates
Examples of Exported classes:
UENUM(BlueprintType)
enum class EMyClass : uint8 { MyType = 0, MyPC, MyCar };
DECLARE_DELEGATE_OneParam(FMyDelegate, bool);
USTRUCT()
struct ELEPHANT_API FMyStruct { GENERATED_BODY() };
UCLASS()
class ELEPHANT_API AMyActor : public AActor { GENERATED_BODY() };