Skip to main content

🧱 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.cs file and add a module dependency
  • Describe the role of .Target.cs files 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.

graph TD Proj["MyProject (.uproject)"] --> GM["Game module: MyProject"] Proj --> Plug["Plugin modules (optional)"] GM --> Dep1["depends on: Core"] GM --> Dep2["depends on: Engine"] GM --> Dep3["depends on: InputCore, EnhancedInput..."] Dep1 --> Bin["Each module compiles to its own binary"] Dep2 --> Bin Dep3 --> Bin
Figure 1: A project is a container of modules. Your game module depends on engine modules; those dependencies are declared in .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:

FileProduces
MyProject.Target.csThe game — the standalone, packaged runtime
MyProjectEditor.Target.csThe 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.

PrefixMeaningExample
AActor — a placeable object with a transformACharacter, ASpinningCube
UUObject that is not an Actor (components, subsystems, most reflected classes)UStaticMeshComponent, UGameInstance
FPlain struct / non-UObject classFVector, FHitResult
EEnumEWeaponType
TTemplate typeTArray, TMap, TObjectPtr
IInterface classIInteractable
bBoolean 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.

  1. You add code that uses UEnhancedInputComponent and get "unresolved external symbol." What one-line change fixes it, and in which file?
  2. 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".
  3. True or false: a header should #include the full definition of every type it mentions. Explain.
✅ Answers
  • 1. Add "EnhancedInput" to a dependency list in MyProject.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.cs configures 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.h last.

📚 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.