🧱 Lesson 2.1: Project & Module Anatomy and the Coding Standard
Unreal doesn't compile "a project" — it compiles modules. Understanding modules, their build rules, and Epic's naming conventions turns cryptic build errors into obvious ones and makes your code look like it belongs in the engine.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain what an Unreal module is and how it differs from a project
- Read a
.Build.csfile and add a module dependency - Describe the role of
.Target.csfiles and the primary game module - Apply Epic's naming prefixes (
A,U,F,E,T,I,b) correctly - Follow the include conventions (IWYU,
CoreMinimal.h) that keep builds fast
Estimated Time: 45 minutes
Engine Version: Unreal Engine 5.8
In This Lesson
What Is a Module?
A module is Unreal's unit of C++ compilation and linking — it compiles to one binary (a DLL in editor builds). The engine itself is hundreds of modules (Core, Engine, UMG, Niagara…). Your game has at least one module of its own, and you can add more.
.Build.cs.📖 Why modules exist
Modules give faster incremental builds (only changed modules recompile), clear dependencies (a module states what it needs), and encapsulation (public vs private headers). For most of this course you'll work in a single game module; Module 8 covers making your own.
Reading a .Build.cs
Each module has a <ModuleName>.Build.cs file — written in C#, not C++. It's build configuration: which other modules this one links against. Here's a typical game module's build file:
// MyProject.Build.cs
using UnrealBuildTool;
public class MyProject : ModuleRules
{
public MyProject(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
// Modules whose PUBLIC headers you use in YOUR public headers.
PublicDependencyModuleNames.AddRange(new string[]
{
"Core", "CoreUObject", "Engine", "InputCore"
});
// Modules you use only in .cpp files (implementation detail).
PrivateDependencyModuleNames.AddRange(new string[]
{
"EnhancedInput"
});
}
}
⚠️ The most common build error you'll hit
"Unresolved external symbol" or "cannot open include file" for an engine type almost always means a missing module in .Build.cs. Using UEnhancedInputComponent? Add "EnhancedInput". Using UMG widgets? Add "UMG". The fix is a one-line edit here, followed by regenerating project files.
✅ Public vs Private dependency — the rule
If the dependency's types appear in your public headers, it's a Public dependency (consumers of your module need it too). If you only use it inside your .cpp files, it's Private. When unsure, start Private and promote to Public only if a header needs it.
Targets & the Primary Game Module
Where .Build.cs configures a module, a .Target.cs configures a whole build target — the thing you actually produce. A C++ project has two by default:
| File | Produces |
|---|---|
MyProject.Target.cs | The game — the standalone, packaged runtime |
MyProjectEditor.Target.cs | The editor — the game plus editor-only code |
Every project needs exactly one module declared as its primary game module, via a macro in that module's .cpp:
// MyProject.cpp — the module implementation file
#include "MyProject.h"
#include "Modules/ModuleManager.h"
// Registers this module as the primary game module. Every game needs exactly one.
IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, MyProject, "MyProject");
💡 You rarely edit these by hand. The project wizard writes them. But when a build target behaves oddly — say, editor-only code leaking into a packaged game — the .Target.cs is where the answer lives. We return to targets in Module 8.
Naming Prefixes
Unreal enforces a single-letter prefix on type names. This isn't decoration — the header tool requires the right prefix for the base class, and reading them tells you instantly what a type is.
| Prefix | Meaning | Example |
|---|---|---|
A | Actor — a placeable object with a transform | ACharacter, ASpinningCube |
U | UObject that is not an Actor (components, subsystems, most reflected classes) | UStaticMeshComponent, UGameInstance |
F | Plain struct / non-UObject class | FVector, FHitResult |
E | Enum | EWeaponType |
T | Template type | TArray, TMap, TObjectPtr |
I | Interface class | IInteractable |
b | Boolean variable (lowercase, camelCase after) | bIsDead, bCanEverTick |
⚠️ Prefix must match the base — or it won't compile
If you derive from AActor, your class must start with A. Derive from UObject, start with U. The Unreal Header Tool checks this and errors if you get it wrong. So class UMyActor : public AActor is a build error — it must be AMyActor.
Beyond prefixes: types and functions are PascalCase, booleans get the b prefix, and Unreal uses sized integer types — int32, int64, uint8 — rather than bare int, for portability across platforms.
Include Conventions
Unreal projects follow IWYU — "Include What You Use." The idea: each file includes exactly the headers it needs, no more, so changing one header doesn't recompile half the project.
The two anchors
#include "CoreMinimal.h"— a curated, lightweight set of the most common core types (FString,FVector,TArray, basic macros). Start most headers with it.#include "<ClassName>.generated.h"— the header tool's generated code. Always the last include in any header declaring a reflected type (you met this rule in Lesson 1.4).
// The canonical header include order for a reflected class:
#pragma once
#include "CoreMinimal.h" // 1. core types
#include "GameFramework/Actor.h" // 2. the base class you derive from
// ... any other real includes you truly need in the header ...
#include "MyActor.generated.h" // 3. ALWAYS LAST
class UStaticMeshComponent; // forward declares can sit above or below CoreMinimal
✅ Pro Tip — forward declare in headers, include in .cpp
You met this in Lesson 1.4 and it's worth cementing: if a header only uses a type as a pointer or reference, forward-declare it (class UFoo;) and include Foo.h in the .cpp. This is the single biggest lever on your project's compile times.
Hands-on Exercise & Quiz
🏋️ Exercise: Diagnose & name
Objective: Apply modules and prefixes to realistic situations.
- You add code that uses
UEnhancedInputComponentand get "unresolved external symbol." What one-line change fixes it, and in which file? - Give the correct prefixed name for: an actor named "Turret"; a non-actor UObject named "InventoryComponent"; a plain struct named "DamageInfo"; an enum named "AmmoType"; a bool member "is reloading".
- True or false: a header should
#includethe full definition of every type it mentions. Explain.
✅ Answers
- 1. Add
"EnhancedInput"to a dependency list inMyProject.Build.cs(Private if only used in .cpp), then regenerate project files. - 2.
ATurret,UInventoryComponent,FDamageInfo,EAmmoType,bIsReloading. - 3. False — prefer forward declarations for pointer/reference-only usage (IWYU); include the full header only where you actually use the type's members (usually the
.cpp).
🎯 Quick Quiz
Question 1: What language is a .Build.cs file written in?
Question 2: You derive a class from AActor. Which name is legal?
Question 3: A dependency's types appear in your module's public header. It should be listed as:
Summary
🎉 Key Takeaways
- Unreal compiles modules, not projects; your game has at least one, and each compiles to its own binary.
.Build.cs(C#) declares a module's dependencies — missing ones cause "unresolved symbol" errors;.Target.csconfigures whole build targets (game vs editor).- Exactly one module is the primary game module via
IMPLEMENT_PRIMARY_GAME_MODULE. - Prefixes are mandatory and meaningful:
A(actor),U(UObject),F(struct),E(enum),T(template),I(interface),b(bool) — and must match the base class. - Follow IWYU: start with
CoreMinimal.h, forward-declare in headers, include in.cpp, and keep.generated.hlast.
📚 Additional Resources
🚀 What's Next?
You know how the project is organized. Now the centerpiece: the reflection system — what UCLASS, USTRUCT, and UENUM actually do, and why they're the reason C++ and the editor can talk at all.
🎉 Lesson complete!
The skeleton makes sense. Let's meet the nervous system: reflection.