- What is an Unreal Module
- Module Structure Analysis
- Creating Modules Manually
- Creating Modules with IDE
- Migrating Modules
- Module Dependencies
- Best Practices
- Common Issues and Solutions
- References
- Contributing
An Unreal Engine Module is the fundamental unit of code organization, similar to Libraries or Packages in other programming languages. Each module is an independent compilation unit with the following characteristics:
- Encapsulation: Groups related functionality together
- Reusability: Can be shared across different projects
- Dependency Management: Clearly defines dependencies between modules
- Load Control: Controls when and how modules are loaded
Source/
└── [ModuleName]/
├── [ModuleName].Build.cs # Build configuration file
├── Public/ # Public header files
│ ├── [ModuleName]Module.h # Main module header
│ └── [PublicClass].h # Other public classes
└── Private/ # Private implementation files
├── [ModuleName]Module.cpp # Module implementation
└── [PrivateClass].cpp # Other implementation files
📝 Important Note: The module names Auto and Manual in this project are chosen purely to demonstrate two different methods of module creation:
- Auto: Created using IDE (JetBrains Rider) with automatic generation
- Manual: Created manually with full control over the process
These names have no special meaning regarding module dependencies or functionality. The same principles apply to any module names (e.g., ModuleA/ModuleB, GameplayCore/UISystem, etc.). The dependency relationship (Manual depends on Auto) is simply for demonstration purposes and could easily be reversed or applied to any other module combination.
FirstModule/Source/
├── Auto/ # IDE-created module
│ ├── Auto.Build.cs
│ ├── Public/
│ │ ├── AutoModule.h
│ │ ├── AutoCppOnly.h
│ │ └── AutoExposed.h
│ └── Private/
│ ├── AutoModule.cpp
│ ├── AutoCppOnly.cpp
│ ├── AutoExposed.cpp
│ ├── AutoInternal.cpp
│ └── AutoInternal.h
├── Manual/ # Manually created module
│ ├── Manual.Build.cs
│ ├── Public/
│ │ ├── ManualModule.h
│ │ ├── ManualCppOnly.h # C++ only classes
│ │ └── ManualExposed.h # Blueprint compatible
│ └── Private/
│ ├── ManualModule.cpp
│ ├── ManualCppOnly.cpp
│ ├── ManualExposed.cpp
│ ├── ManualInternal.cpp
│ └── ManualInternal.h # Internal implementation
├── Migrate/ # Migrated module (example)
│ ├── Migrate.Build.cs
│ ├── Public/
│ │ ├── Migrate.h
│ │ └── MigrateActor.h
│ └── Private/
│ ├── Migrate.cpp
│ └── MigrateActor.cpp
└── FirstModule/ # Main game module
├── FirstModule.Build.cs
├── FirstModule.cpp
└── FirstModule.h
- Build.cs File: Defines compilation settings and dependencies
- Module.h File: Public interface definition of the module
- Module.cpp File: Actual implementation of the module
- Public Folder: Header files for use by other modules
- Private Folder: Internal implementation files for the module
Let's examine how the Manual module was created in this project:
Create a new module folder under the Source/
directory:
Source/
└── Manual/
├── Public/
└── Private/
Create Source/Manual/Manual.Build.cs
:
//----------------------------------------------------------------------------------------------------
// Manual.Build.cs
//----------------------------------------------------------------------------------------------------
using UnrealBuildTool;
public class Manual : ModuleRules
{
public Manual(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange([
"Core",
"Auto" // This module depends on Auto module
]);
PrivateDependencyModuleNames.AddRange([
"CoreUObject",
"Engine"
]);
}
}
Create Source/Manual/Public/ManualModule.h
:
//----------------------------------------------------------------------------------------------------
// ManualModule.h
//----------------------------------------------------------------------------------------------------
#pragma once
#include <CoreMinimal.h>
#include <Modules/ModuleManager.h>
DECLARE_LOG_CATEGORY_EXTERN(LogManual, Log, All);
class MANUAL_API FManualModule final : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
Create Source/Manual/Private/ManualModule.cpp
:
//----------------------------------------------------------------------------------------------------
// ManualModule.cpp
//----------------------------------------------------------------------------------------------------
#include "ManualModule.h"
#include <Modules/ModuleManager.h>
DEFINE_LOG_CATEGORY(LogManual);
#define LOCTEXT_NAMESPACE "FManualModule"
void FManualModule::StartupModule()
{
UE_LOG(LogManual, Warning, TEXT("FManualModule::StartupModule()"));
}
void FManualModule::ShutdownModule()
{
UE_LOG(LogManual, Warning, TEXT("FManualModule::ShutdownModule()"));
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FManualModule, Manual)
FirstModule.uproject:
{
"FileVersion": 3,
"EngineAssociation": "5.6",
"Modules": [
{
"Name": "FirstModule",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "Auto",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "Manual",
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
}
FirstModule.Target.cs:
//----------------------------------------------------------------------------------------------------
// FirstModule.Target.cs
//----------------------------------------------------------------------------------------------------
using UnrealBuildTool;
public class FirstModuleTarget : TargetRules
{
public FirstModuleTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_6;
ExtraModuleNames.Add("FirstModule");
RegisterModulesCreatedByRider();
}
private void RegisterModulesCreatedByRider()
{
ExtraModuleNames.AddRange(["Auto", "Manual"]);
}
}
For creating your own modules, use this template:
// YourModuleName.Build.cs
using UnrealBuildTool;
public class YourModuleName : ModuleRules
{
public YourModuleName(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange([
"Core"
]);
PrivateDependencyModuleNames.AddRange([
"CoreUObject",
"Engine"
]);
}
}
The Auto module in this project was created using Rider:
- Right-click on the project root directory → Select
Add
→Unreal Module…
- Enter module name:
YourModuleName
- Select module type:
Runtime
- Select loading phase:
Default
- Click OK, Rider automatically created:
//----------------------------------------------------------------------------------------------------
// Auto.Build.cs (Generated by Rider)
//----------------------------------------------------------------------------------------------------
using UnrealBuildTool;
public class Auto : ModuleRules
{
public Auto(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange([
"Core"
]);
PrivateDependencyModuleNames.AddRange([
"CoreUObject",
"Engine"
]);
}
}
//----------------------------------------------------------------------------------------------------
// AutoModule.h (Generated by Rider)
//----------------------------------------------------------------------------------------------------
#pragma once
#include <CoreMinimal.h>
#include <Modules/ModuleManager.h>
DECLARE_LOG_CATEGORY_EXTERN(LogAuto, Log, All);
class FAutoModule final : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
//----------------------------------------------------------------------------------------------------
// AutoModule.cpp (Generated by Rider)
//----------------------------------------------------------------------------------------------------
#include "AutoModule.h"
#include <Modules/ModuleManager.h>
DEFINE_LOG_CATEGORY(LogAuto);
#define LOCTEXT_NAMESPACE "FAutoModule"
void FAutoModule::StartupModule()
{
UE_LOG(LogAuto, Warning, TEXT("FAutoModule::StartupModule()"));
}
void FAutoModule::ShutdownModule()
{
UE_LOG(LogAuto, Warning, TEXT("FAutoModule::ShutdownModule()"));
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FAutoModule, Auto)
- Right-click on the project root directory → Select
Add
→Unreal Module…
- Select Empty Unreal Engine Module
- Click Add
- Enter module name:
YourModuleName
- Select module type:
Runtime
- Select loading phase:
Default
- Click OK
Sometimes you need to move or migrate an existing module from one project to another. This process involves copying the module files and updating the target project's configuration. The Migrate module in this project demonstrates how an external module can be integrated.
📝 Migration Note: Module migration is useful when you want to:
- Share modules between different projects
- Move modules from an experimental project to production
- Reuse existing functionality in new projects
- Backup and restore specific modules
The first step is to copy the entire module folder from the source project:
- Open File Explorer and navigate to the source project's
Source/
directory - Locate the module you want to migrate (in this case, it's the
Migrate
module) - Copy the entire module folder including all subdirectories and files
SourceProject/Source/
└── Migrate/ # Copy this entire folder
├── Migrate.Build.cs
├── Public/
│ ├── Migrate.h
│ └── MigrateActor.h
└── Private/
├── Migrate.cpp
└── MigrateActor.cpp
Paste the copied module folder to the target project:
- Navigate to the target project's
Source/
directory - Paste the module folder at the same hierarchy level as existing modules like
Auto
andManual
TargetProject/Source/
├── Auto/ # Existing module
├── Manual/ # Existing module
├── Migrate/ # ✅ Newly migrated module
│ ├── Migrate.Build.cs
│ ├── Public/
│ │ ├── Migrate.h
│ │ └── MigrateActor.h
│ └── Private/
│ ├── Migrate.cpp
│ └── MigrateActor.cpp
└── FirstModule/ # Main game module
After copying the files, you need to register the migrated module in the target project:
Add the new module to FirstModule.uproject
:
{
"FileVersion": 3,
"EngineAssociation": "5.6",
"Modules": [
{
"Name": "FirstModule",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "Auto",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "Manual",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "Migrate", // ✅ Add this entry
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
}
Add the module name to both FirstModule.Target.cs
and FirstModuleEditor.Target.cs
:
FirstModule.Target.cs:
private void RegisterModulesCreatedByRider()
{
ExtraModuleNames.AddRange(["Auto", "Manual", "Migrate"]); // ✅ Add "Migrate"
}
FirstModuleEditor.Target.cs:
private void RegisterModulesCreatedByRider()
{
ExtraModuleNames.AddRange(["Auto", "Manual", "Migrate"]); // ✅ Add "Migrate"
}
After updating the configuration files:
- Close your IDE (Rider/Visual Studio)
- Right-click on
FirstModule.uproject
→ Select "Generate Visual Studio Project Files" - Wait for generation to complete
- Reopen the project in your IDE
To confirm the migration was successful:
- Check that the module compiles without errors
- Verify module loading in the UE Editor logs:
LogTemp: Warning: MigrateActor::AMigrateActor
- Test module functionality by using its classes in other modules or Blueprints
- Keep module self-contained: Ensure the module doesn't have hard-coded dependencies on the source project
- Update dependencies carefully: Check if the target project has all required dependencies
- Test thoroughly: Verify all functionality works in the new environment
- Maintain API consistency: Keep public interfaces stable for easier migration
- Copy system-specific files: Don't migrate
Intermediate/
orBinaries/
folders - Ignore dependencies: Make sure all required modules exist in the target project
- Skip regeneration: Always regenerate project files after migration
- Assume compatibility: Different UE versions might require code adjustments
Symptoms: Compilation errors about missing includes or undefined classes
Solution: Ensure all dependency modules exist in the target project or update Build.cs
Symptoms: Linker errors or undefined symbols
Solution: Verify the MIGRATE_API
macro is correctly defined and used
Symptoms: Module doesn't appear in the modules list
Solution: Double-check .uproject
and Target.cs
entries, then regenerate project files
When migrating between different UE versions:
- Check API changes in the UE documentation
- Update deprecated functions to their modern equivalents
- Verify Build.cs compatibility with the target UE version
When migrating a module that depends on other custom modules:
- Migrate dependencies first in the correct order
- Update Build.cs dependencies to match the target project structure
- Test the entire dependency chain after migration
Module migration is a powerful feature that enables code reuse across projects and helps maintain a modular architecture. The Migrate module in this project serves as a practical example of how external modules can be seamlessly integrated into existing projects.
📝 Naming Convention Note: In this project, "Manual depends on Auto" is purely for demonstration. The dependency direction has nothing to do with how the modules were created (manually vs. IDE). In real projects, you might have:
GameplayCore
depends onUtilityLibrary
UISystem
depends onGameplayCore
AudioManager
depends onConfigurationModule
PlayerController
depends onInputManager
The dependency direction and module names should reflect your actual project architecture, not the creation method. You could equally have:
- Auto depends on Manual
- ModuleA depends on ModuleB
- Any meaningful combination based on your project needs
Manual.Build.cs shows the dependency:
PublicDependencyModuleNames.AddRange([
"Core",
"Auto" // Manual depends on Auto
]);
ManualExposed.h includes AutoCppOnly:
//----------------------------------------------------------------------------------------------------
// ManualExposed.h
//----------------------------------------------------------------------------------------------------
#pragma once
#include <CoreMinimal.h>
#include "AutoCppOnly.h" // Include from Auto module
#include <ManualExposed.generated.h>
UCLASS(Blueprintable)
class MANUAL_API AManualExposed : public AActor
{
GENERATED_BODY()
public:
AManualExposed();
UFUNCTION(BlueprintCallable, Category = "Manual")
void DoManualExposed() const;
FAutoCppOnly AutoCppOnly; // Using class from Auto module
};
ManualExposed.cpp uses the Auto module class:
//----------------------------------------------------------------------------------------------------
// ManualExposed.cpp
//----------------------------------------------------------------------------------------------------
#include "ManualExposed.h"
#include "ManualModule.h"
AManualExposed::AManualExposed()
{
UE_LOG(LogManual, Warning, TEXT("AManualExposed::AManualExposed()"));
}
void AManualExposed::DoManualExposed() const
{
UE_LOG(LogManual, Warning, TEXT("AManualExposed::DoManualExposed()"));
AutoCppOnly.DoAutoCppOnly(); // Call method from Auto module
}
- PublicDependencyModuleNames: Public dependencies, modules that depend on this module can also use them
- PrivateDependencyModuleNames: Private dependencies, used only internally within this module
Based on this project's structure, here's the three-tier classification:
Purpose: High-performance C++ API for other modules Features:
- ✅ Other C++ modules can use
- ❌ Blueprint cannot inherit/call
- ⚡ High performance, no reflection system overhead
//----------------------------------------------------------------------------------------------------
// ManualCppOnly.h
//----------------------------------------------------------------------------------------------------
#pragma once
#include <CoreMinimal.h>
class MANUAL_API FManualCppOnly
{
public:
FManualCppOnly();
void DoManualCppOnly();
};
//----------------------------------------------------------------------------------------------------
// ManualCppOnly.cpp
//----------------------------------------------------------------------------------------------------
#include "ManualCppOnly.h"
#include "ManualModule.h"
FManualCppOnly::FManualCppOnly()
{
UE_LOG(LogManual, Warning, TEXT("FManualCppOnly::FManualCppOnly()"));
}
void FManualCppOnly::DoManualCppOnly()
{
UE_LOG(LogManual, Warning, TEXT("FManualCppOnly::DoManualCppOnly()"));
}
Purpose: Blueprint-compatible game logic Features:
- ✅ Other C++ modules can use
- ✅ Blueprint can inherit/call
- 🔄 Can internally call other modules' classes (like AutoCppOnly)
//----------------------------------------------------------------------------------------------------
// ManualExposed.h
//----------------------------------------------------------------------------------------------------
#pragma once
#include <CoreMinimal.h>
#include "AutoCppOnly.h"
#include <ManualExposed.generated.h>
UCLASS(Blueprintable)
class MANUAL_API AManualExposed : public AActor
{
GENERATED_BODY()
public:
AManualExposed();
UFUNCTION(BlueprintCallable, Category = "Manual")
void DoManualExposed() const;
FAutoCppOnly AutoCppOnly;
};
Purpose: Internal module implementation details Features:
- ❌ Other modules cannot use (located in Private folder)
- ❌ Blueprint cannot access
- 🔒 Encapsulates implementation details
//----------------------------------------------------------------------------------------------------
// ManualInternal.h
//----------------------------------------------------------------------------------------------------
#pragma once
#include <CoreMinimal.h>
class FManualInternal
{
FManualInternal();
void DoManualInternal() const;
};
- Module Names: Use PascalCase, e.g.,
Auto
,Manual
- Class Names: Use the appropriate prefix:
F
for regular classes:FManualCppOnly
A
for Actors:AManualExposed
U
for UObject-derived:UManualComponent
- API Macros: Use uppercase module name:
MANUAL_API
,AUTO_API
Source/Manual/
├── Public/ # External interface
│ ├── ManualModule.h # Module definition
│ ├── ManualCppOnly.h # C++ only classes
│ └── ManualExposed.h # Blueprint compatible
└── Private/ # Internal implementation
├── ManualModule.cpp # Module implementation
├── ManualCppOnly.cpp # C++ implementation
├── ManualExposed.cpp # Blueprint class implementation
├── ManualInternal.h # Internal definitions
└── ManualInternal.cpp # Internal implementation
// Public class - can be used by other modules
class MANUAL_API FManualCppOnly // Has MANUAL_API macro
{
// Accessible from other modules
};
// Private class - internal use only
class FManualInternal // No API macro, in Private folder
{
// Internal use only
};
// In ManualModule.h
DECLARE_LOG_CATEGORY_EXTERN(LogManual, Log, All);
// In ManualModule.cpp
DEFINE_LOG_CATEGORY(LogManual);
// Usage throughout the module
UE_LOG(LogManual, Warning, TEXT("FManualCppOnly::DoManualCppOnly()"));
Symptoms: Game startup shows module not found
Solutions:
- Check if .uproject file correctly registers the module
- Check if Target.cs files include the module name
- Confirm Build.cs file syntax is correct
- Regenerate project files
Symptoms: Cannot find header files or linking errors
Solutions:
- Check dependencies in Build.cs (like Manual depending on Auto)
- Confirm API macros are used correctly (
MANUAL_API
,AUTO_API
) - Check #include paths
- Clean and rebuild
Symptoms: Module A depends on B, B also depends on A
Solutions:
- Redesign module architecture
- Extract common dependencies to a third module
- Use forward declarations to reduce dependencies
- Consider using interfaces for decoupling
Symptoms: C++ classes not visible in Blueprint
Solutions:
- Ensure class inherits from UObject/AActor/UComponent
- Add UCLASS macro with BlueprintType, Blueprintable
- Use UFUNCTION for callable functions
- Use UPROPERTY for accessible properties
- Regenerate project files after changes
Example from ManualExposed (Working Blueprint Class):
UCLASS(Blueprintable) // Makes class available in Blueprint
class MANUAL_API AManualExposed : public AActor
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Manual") // Callable from Blueprint
void DoManualExposed() const;
};
Symptoms: Constructor logs appear multiple times before the game starts
LogManual: Warning: AManualExposed::AManualExposed()
LogManual: Warning: AManualExposed::AManualExposed()
LogManual: Warning: AManualExposed::AManualExposed()
This is normal behavior! UE calls constructors for:
- CDO (Class Default Object) creation
- Blueprint asset validation
- Editor preview systems
- Actor Factory systems
Best Practices:
- Keep constructors lightweight (only set default values)
- Put game logic in
BeginPlay()
instead - Use
IsTemplate()
to check if it's a CDO
AManualExposed::AManualExposed()
{
// ✅ Good: Just log and set defaults
UE_LOG(LogManual, Warning, TEXT("AManualExposed::AManualExposed()"));
// ❌ Bad: Don't do game logic here
// StartGameplay();
// LoadAssets();
}
void AManualExposed::BeginPlay()
{
Super::BeginPlay();
// ✅ Good: Game logic goes here
StartGameplay();
}
// In ManualModule.cpp
DEFINE_LOG_CATEGORY(LogManual);
- Purpose: Creates a dedicated log category for the module
- Usage:
UE_LOG(LogManual, Warning, TEXT("Message"));
- Benefits: Filter module-specific logs in editor
// In ManualModule.cpp
#define LOCTEXT_NAMESPACE "FManualModule"
// ... module code ...
#undef LOCTEXT_NAMESPACE
- Purpose: Sets namespace for localization text
- Usage:
LOCTEXT("Key", "Default Text")
- Benefits: Supports game internationalization
// In ManualModule.cpp
IMPLEMENT_MODULE(FManualModule, Manual)
- Purpose: Registers module with UE system
- Format:
IMPLEMENT_MODULE(ClassName, ModuleName)
- Critical: Without this, module won't load!
This handbook is based on the real FirstModule project, demonstrating:
- Auto Module: Created using IDE (Rider) with automatic generation
- Manual Module: Created manually with full control over structure
- Dependency System: Manual depends on Auto, showing inter-module communication
- Classification System: CppOnly, Exposed, and Internal classes for different access levels
- Blueprint Integration: Real working examples of C++ to Blueprint exposure
Through proper module design, you can:
- Improve code maintainability
- Facilitate team collaboration
- Enable feature reuse
- Optimize compilation time
- Create clear separation between C++ and Blueprint functionality
Remember, good module design is the foundation of successful UE projects. Start with small modules and gradually build your modular architecture.
- Unreal Engine Modules—Official Documentation
- Improving Code Structure with Unreal Engine's C++ Modules
- Getting into C++ with Unreal Engine—Part 7 - Modules & Plugins
Feel free to contribute to this handbook by:
- 📝 Adding more examples
- 🐛 Reporting issues or improvements
- 💡 Suggesting new sections or topics
- 🌟 Sharing your own module development experiences
This handbook is provided under the Apache 2.0 License. Feel free to use, modify, and distribute it for educational purposes.