# Unreal Engine: Actors, Components, and Blueprints

*2025-07-02*

> Notes taken from the third week of Game Programming Foundations


Following on from [controllers and game modes]({{< relref "/posts/2025/unreal-engine-02-controllers-and-game-modes/" >}}), this week was about actors, components, and blueprints – the object model that underpins basically everything in Unreal.

As ever, I've been committing changes to the repository as I go – notes below.

[sawnoff-studios/first-project](https://github.com/sawnoff-studios/first-project)

## Game Objects: Renderable and Non-Renderable

Not everything in a level is something you can see. Useful mental split:

- **Renderable**: enemies, players, pick-ups, props, projectiles, scenery
- **Non-renderable**: waypoints, volumes, triggers, sounds

## Inheritance vs Composition

**Inheritance ("is a"):**

- Hierarchies can run deep, and a change at the top cascades all the way down
- Multiple inheritance (one class, several parents) causes the "Deadly Diamond" problem: multiple copies of base class members can end up in one object
- This is why most studios outright prohibit multiple inheritance; some permit it only if there's a single grandparent at worst

**Composition ("has a"):**

- The entity becomes a container for components rather than a chain of specialisations
- This is the dominant paradigm in modern game engines – and it's the model Unreal itself uses
- Downsides: many components can slow things down, and tasks that need synchronised data often end up holding duplicate copies of it

The course's example was building a Halftrack both ways – an illustration of how the same object falls out of inheritance versus composition.

## Blueprints: Events, Functions, Pure Functions, Macros

The colour-coding of blueprint nodes tells you what kind of thing you're looking at:

### Events (red)

- Fired by engine triggers (e.g. BeginPlay, Tick) – you don't call them yourself
- You can create **custom events** from the context menu
- Can be replicated from the server for multiplayer
- Blueprint **functions and macros** can be double-clicked to inspect their bodies

### Functions (blue)

- Declared through the Functions panel; called like any built-in function
- **Performance note**: functions have a slight edge over events, because a function compiles into one batch of bytecode – whereas every node in an event graph is processed individually. In practice the bigger difference is structural (local scope, no latent actions), but it's nice to know
- Variables inside are **local** to the function
- **Access specifiers** (public/protected/private) work just like in C++

### Pure functions (green)

- Like `const`-typed functions in C++: they take inputs, produce outputs, and have no side effects
- Commonly seen feeding **Branch** conditions
- Variables are local to the function as above

### Macros (grey)

- `ForLoop`, `Sequence`, and Branch are actually pre-defined macros, not functions
- Sequence runs one section after another in order
- You can define your own macros in the Macros panel, with defined inputs and outputs like functions
- Macros can contain **Delays** (and other latent nodes), which functions cannot
- Don't edit the StandardMacros library; instead build a **Blueprint Macro Library** for custom reusable macros

## Components

The composition half of this week's theory, in practice:

- Add components via the Components panel, or in C++:
    - Declare a `TObjectPtr<>` member (with a forward declaration in the header)
    - Initialise with `CreateDefaultSubobject<>()` in the constructor
    - Attach to a parent with `SetupAttachment()` – e.g. a mesh onto a capsule
    - Component classes and their headers are easy to find via the documentation search
- **CapsuleComponent** – untick Hidden In Game to actually see it while playing
- **SpringArm** - `TargetArmLength` sets the camera's distance behind the pawn; Camera Lag Speed controls how fluidly the camera catches up
- **Vertical** camera rotation again required **Use Pawn Control Rotation** on the camera/spring arm – the same gotcha from last week, biting me a second time

---

## Notes on things that tripped me up

Two repeat offenders this week: the Use Pawn Control Rotation setting (which seems to bite everyone twice) and discovering that ForLoop and friends aren't functions at all.

The functions-vs-events distinction also took a while to settle – my working summary: **events react, functions compute, pure functions don't touch state, macros are the escape hatch for Delays**.

