5.2 Blueprints

Phase 5 · Unreal Engine (alternative) · Study time: 25–45 h

Unreal's visual scripting — fast prototyping and gameplay logic without writing C++, and widely used even at AAA studios.

Earlier in this chapter you saw how Unreal objects work in C++: a class inheriting from AActor, variables and functions exposed with macros like UPROPERTY and UFUNCTION. This section covers the tool Unreal developers reach for just as often, sometimes more: Blueprints, Unreal's visual scripting system. By the end of this section you will be able to read a Blueprint graph the same way you read a page of C++, know when to reach for one over the other, and understand how the two talk to each other in a real project.

Every section below follows the same shape as earlier chapters: a small example, a trace of what actually happens, then a plain explanation. Blueprints do not print console output the way C++ does, so instead of "real output" you will see a worked trace of the graph — which node runs, in what order, with what values.

1. What Is a Blueprint?

A Blueprint is Unreal Engine's visual scripting system. Instead of typing lines of C++ text, you place small boxes called nodes on a canvas and connect them with lines called wires. Each node does one small thing — "when the game starts," "add two numbers," "play a sound." The wires say what order things happen in, and what data flows where.

Under the hood, a Blueprint still becomes real bytecode (compiled instructions the engine's virtual machine runs) when you click Compile. You are not "faking" code — you are writing code with boxes and lines instead of text. Almost anything you can build in a Blueprint graph could, in theory, be rewritten as C++. The value of Blueprints is not that they do something C++ cannot do; it is that they are faster to read, faster to change, and do not require a C++ compiler and a project rebuild to test.

TEXT CODE (C++) VISUAL CODE (Blueprint) Score += 10; [Add Score: 10] PlaySound(CoinSound); | v [Play Sound: CoinSound]

Both sides above do the same job. The left side is text you compile with a C++ compiler. The right side is a graph you compile by clicking a button in the editor — no separate build tool, no waiting for a whole C++ module to relink.

A Quick Tour of the Blueprint Editor

When you open a Blueprint (for example, double-click BP_Coin in the Content Browser), you see several panels. A beginner does not need to memorize all of them, but should recognize these:

THE BLUEPRINT EDITOR (simplified layout) My Blueprint panel Event Graph (the canvas) ------------------- -------------------------- Variables [Event BeginPlay] Score (int) | exec bCollected (bool) v Functions [Print String] AddScore Graphs EventGraph ConstructionScript Details panel (bottom): shows properties of whatever is currently selected above.
Tip Right-click anywhere on the Event Graph canvas and type to search for a node by name — you do not need to memorize which menu a node lives in. After wiring anything, click Compile in the toolbar. A node that turns red with a warning icon has a problem; hovering over it tells you exactly what is missing.

2. Why Even AAA Studios Use Blueprints

Beginners sometimes assume Blueprints are a "toy" version of scripting for hobbyists, and that professional studios only use C++. That is wrong. Fortnite, Gears of War, and most AAA Unreal titles ship with thousands of Blueprint graphs in the final game. There are concrete reasons for this:

The real-world pattern is not "C++ or Blueprint." It is "C++ for the parts that need to be fast or reused everywhere, Blueprint for the parts that need to change often." Sections 8 and 9 cover this in detail.

3. Event Nodes: Where a Graph Starts

Every chain of nodes in a Blueprint has to start somewhere. It starts at an Event node — a node with no execution input, only an execution output, that fires automatically when something happens. Event nodes are drawn in red in the editor.

The most common Event nodes a beginner will use:

[Event BeginPlay] [Event Tick] | Delta Seconds (float) o v | runs once, v right after spawn runs every single frame; Delta Seconds = seconds since the last frame
Common mistake New Blueprint users put expensive logic (searching for actors, looping over large arrays, spawning objects) directly in Event Tick. Tick runs 30 to 240+ times per second depending on frame rate. Logic that does not need to run every single frame should not live in Tick — see Section 11.

4. Execution Wires vs. Data Wires

A node has up to four kinds of connection points, called pins:

The white triangle pins connect with execution wires (also called exec wires). These are the "and then" of the graph — they define order, exactly like semicolons and line breaks define order in C++. The colored circle pins connect with data wires — these are the "using this value" of the graph, exactly like a variable or expression passed as an argument in C++.

Pin color tells you the data type, the same way a C++ type tells you what a variable holds:

EXECUTION WIRE (white, thick) — defines ORDER [Node A] ====> [Node B] ====> [Node C] DATA WIRE (colored, thin) — supplies a VALUE [Get Score] ----(int)----> [Print String]

A data input pin with no wire attached still works — it uses a default value typed directly into the pin (shown as a small text box on the node itself). Once you attach a wire, that default box disappears; the wire now supplies the value instead.

5. Variables in a Blueprint

A Blueprint variable works like a member variable in a C++ class — it stores a value that belongs to this object, can be read or changed anywhere in the graph, and keeps its value between frames.

You create one in the My Blueprint panel by clicking the + next to Variables, naming it, and picking a type (Boolean, Integer, Float, Vector, Actor Reference, and so on — the same idea as choosing a type in C++, just from a dropdown instead of typing int32 or bool).

To use a variable in the graph, drag it from the My Blueprint panel onto the canvas. The editor asks whether you want a Get node (reads the value) or a Set node (writes the value).

GET NODE (pure — reads a value, no exec pins) +----------------+ | Get Score | | Score o-----|---> (hand back the value, instantly) +----------------+ SET NODE (impure — writes a value, has exec pins) exec in | v +--------------------+ | Set Score | | New Value o<---------|--- (value to store) | Score o--------|--> (also outputs the new value) +--------------------+ | v exec out

Notice the Get node has no white triangle pins at all. Reading a variable does not "do" anything in terms of order — it just hands back a value instantly. This kind of node is called pure. The Set node changes something (it writes memory), so it needs an exec pin to say when in the sequence that write happens. This is called impure.

Two checkboxes matter for a variable, visible in the Details panel after you select it:

6. Functions, Custom Events, Branches, and Loops

These four node types are the actual "logic" building blocks of a graph — the visual equivalents of function definitions, callbacks, if, and loops in C++.

Functions

A Blueprint Function is the same idea as a C++ function: a named, reusable block of nodes that takes inputs, optionally returns an output, and can be called from multiple places. You create one in the My Blueprint panel under Functions, then build its graph the same way you build the Event Graph, except a Function starts with an Entry node (its parameter list) instead of an Event node.

// The C++ equivalent of a Blueprint function "AddScore"
void AGameCharacter::AddScore(int32 Amount)
{
    Score += Amount;
    if (Score >= 100 && !bHasWon)
    {
        bHasWon = true;
        OnPlayerWon();
    }
}
FUNCTION GRAPH: "AddScore" [Entry] Amount (int) o | exec v [Set Score = Score + Amount] | exec v [Branch] Condition o<--- (Score >= 100 AND NOT bHasWon) | True | False | v v [Set bHasWon = true] (nothing else happens) | exec v [Call OnPlayerWon]

A Function can also be marked Pure (no exec pins, like the Get node in Section 5) if it only computes and returns a value without changing anything — for example, a function that returns true if the player has enough gold to buy an item, without spending the gold.

Custom Events

A Custom Event node looks similar to a Function but is placed directly on the Event Graph, and can be called from other Blueprints or triggered by an Event Dispatcher (Section 10). Rule of thumb: use a Function when you need a return value or you are only calling it from inside this same Blueprint; use a Custom Event when something else needs to trigger it, or when it fits naturally as "a thing that happens" rather than "a value I compute."

Branches and Loops as Nodes

C++ control-flow keywords like if, for, and while become specific nodes in Blueprint:

BRANCH — the Blueprint "if" [Branch] Condition o | True | False | v v [Do Thing A] [Do Thing B] FORLOOP — runs body once per index, 0 to 4 [ForLoop] First Index: 0 Last Index: 4 | Loop Body | Completed | v v [Print Index] [Print "Done"]
Common mistake Forgetting to connect anything to a loop's Completed pin is harmless (it just means nothing happens after). What trips people up is forgetting that Loop Body fires once per element, before moving to the next one — including any exec chain hanging off it. If that chain does something slow, like spawning an actor, doing it 10,000 times in one frame inside a ForEachLoop will freeze the game for that frame.

Sequence: More Than One Path at Once

One more control-flow node worth knowing: Sequence. It has one exec input and several numbered exec outputs (Then 0, Then 1, Then 2...), all of which fire in order, one after another, on the same frame. It is not a loop — it is a way to say "do all of these, in this order," useful when a node has only one exec output pin but you need to kick off several different chains from it.

7. Walkthrough: A Coin Pickup Graph

Now put Sections 3 through 6 together into one real example: a coin the player can walk over to collect. This graph lives inside a Blueprint called BP_Coin, an Actor with a sphere collision component and a static mesh (the coin's visual model).

The goal: when the player overlaps the coin, add 10 to the player's score, play a pickup sound, and remove the coin — but only once, even if the overlap event somehow fires twice.

EVENT GRAPH — Blueprint "BP_Coin" [Event ActorBeginOverlap] Other Actor o (not wired below; this example does not check who) | exec v [Branch] Condition o<--- [NOT] <--- [Get bCollected] | True | False | v v [Set bCollected = true] (exec chain stops here, | exec nothing else happens) v [Call AddScore] Target o<--- [Get PlayerCharacterRef] Amount: 10 | exec v [Play Sound at Location] Sound o<--- CoinPickupSound (asset) Location o<--- [Get Actor Location] (self) | exec v [Destroy Actor] Target o<--- (self, default)

Reading this the same way you would read C++ line by line:

Every node here matches something you already know from C++: an if statement, a boolean flag used as a one-shot guard, a function call, and cleanup at the end of a scope. The visual form just draws the order and the data flow instead of you inferring it from indentation and semicolons.

Common mistake Leaving a function call's Target pin unwired quietly assumes "Self" — the object that owns this graph. In [Call AddScore] above, forgetting to wire Target to Get PlayerCharacterRef would make the node try to call AddScore on the coin itself. Since BP_Coin has no such function, the graph fails to compile, and the error points at that pin.

8. Blueprint vs. C++: Choosing the Right Tool

This is the most common question a beginner asks after seeing both. There is no single rule, but there are strong signals for each side.

Lean toward C++ when:

Lean toward Blueprint when:

Ask: how OFTEN does this change, and how much PERFORMANCE / SCALE does it need? Changes often, low performance need --> BLUEPRINT "this specific puzzle trigger in level 3" "boss enters phase 2 when HP < 30%" Changes rarely, high performance need --> C++ "pathfinding for 200 AI agents" "inventory system core logic" "network replication of player position"

9. The Hybrid Workflow in Practice

In real Unreal projects, you almost never pick "all Blueprint" or "all C++" for an entire game. The standard pattern: write a C++ base class that does the heavy lifting, then create a Blueprint that inherits from it and fills in the specific, per-content details.

// AGameCharacter.h -- the C++ base class
UCLASS()
class MYGAME_API AGameCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Score")
    int32 Score = 0;

    UFUNCTION(BlueprintCallable, Category = "Score")
    void AddScore(int32 Amount);

    // BlueprintImplementableEvent: C++ declares this, but the BODY
    // is drawn as a graph inside a Blueprint subclass.
    UFUNCTION(BlueprintImplementableEvent, Category = "Score")
    void OnScoreChanged(int32 NewScore);
};

Three macros make C++ and Blueprint talk to each other, and every beginner should recognize them on sight:

C++ BASE CLASS: AGameCharacter BLUEPRINT SUBCLASS: BP_Player (inherits from AGameCharacter) Score (int) ---exposed--> can Get / Set Score AddScore(Amount) ---exposed--> can place [Add Score] node OnScoreChanged(NewScore) --declared only, no body--> [Event OnScoreChanged] NewScore (int) o | exec v [Flash UI Widget] | exec v [Play "ding" Sound]

This split means the programmer owns Score, AddScore, and anything that must be fast or correct. The designer owns what the screen does when the score changes, which is tuned constantly during playtesting and does not need a programmer in the loop for every change.

Tip A very common variant is BlueprintNativeEvent instead of BlueprintImplementableEvent. It lets C++ provide a default body and lets a Blueprint override it if it wants to. Look this up once you are comfortable with the pattern above.

10. Blueprints Talking to Each Other

The coin example in Section 7 called AddScore on "the player." How does BP_Coin get a hold of the player Blueprint to call a function on it? There are three common ways, from most rigid to most flexible.

1. Direct reference

If a Blueprint has a variable of type Actor Reference (or a more specific type like BP_PlayerCharacter) pointing at another specific object, it can call that object's functions directly — the same way a C++ pointer lets you call a method on another object. Simple, but tightly coupled: BP_Coin would need to know the exact class BP_PlayerCharacter exists, which breaks if you ever reuse BP_Coin in a project with a different player class.

2. Blueprint Interfaces

A Blueprint Interface is a named list of functions with no implementation, that any Blueprint can "implement." This should look familiar — it is the same idea as a C++ abstract base class with pure virtual functions, or a C# interface. BP_Coin can call "Give Score" on whatever overlapped it, as long as that actor implements the BPI_ScoreReceiver interface, without ever knowing or caring what specific class it is.

// The C++ equivalent of a Blueprint Interface
UINTERFACE(BlueprintType)
class UScoreReceiver : public UInterface
{
    GENERATED_BODY()
};

class IScoreReceiver
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable, Category = "Score")
    void GiveScore(int32 Amount);
};
BP_Coin some actor implementing BPI_ScoreReceiver [Event ActorBeginOverlap] Other Actor o | exec v [Does Implement Interface?] Interface: BPI_ScoreReceiver Target o<--- Other Actor | Yes | No | v v [Call Interface Function: (do nothing -- this actor "Give Score", Amount: 10] does not receive score) Target: Other Actor]

3. Event Dispatchers

An Event Dispatcher is a Blueprint's way of saying "something happened, and I don't care who is listening." It is the same idea as a C++ multicast delegate or a C# event. The Blueprint that owns the dispatcher calls (fires) it; any number of other Blueprints can bind to it beforehand to be notified when it fires. The object firing the dispatcher does not need a reference to the listeners at all — the listeners subscribed to it, not the other way around.

// The C++ equivalent of a Blueprint Event Dispatcher
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCoinCollected, int32, Amount);

UCLASS()
class MYGAME_API ABP_Coin : public AActor
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintAssignable, Category = "Score")
    FOnCoinCollected OnCoinCollected;
};
BP_Coin (the "publisher") [event: OnCoinCollected fires] | +---------------------------+ | | v v BP_HUD (bound listener) BP_AudioManager (bound listener) [Update Score Text] [Play Fanfare Sound] Neither listener needed a reference to BP_Coin ahead of time, except to call "Bind Event to OnCoinCollected" once, usually during BeginPlay.

Use a direct reference for a tight, one-off relationship (a door that only ever opens for one specific switch). Use an Interface when many different classes need to respond to the same call in their own way. Use an Event Dispatcher when the object that detects something should not need to know who cares about it — the coin should not need to know the HUD exists.

11. Performance: What Actually Costs You

Blueprint nodes run slower than equivalent hand-written C++, because each node carries extra bookkeeping (the engine's virtual machine interprets the graph rather than running raw machine instructions). For most gameplay glue — reacting to an overlap once, calling a function when a button is pressed — this cost is far too small to matter. It becomes a real problem in a few specific situations:

// Getting overlapping actors returns an array -- cheap once,
// expensive if you call this every frame for many objects.
TArray<AActor*> OverlappingActors;
GetOverlappingActors(OverlappingActors, ABP_PlayerCharacter::StaticClass());

if (OverlappingActors.Num() > 0 && !bCollected)
{
    bCollected = true;
}
Tip The engine has a built-in profiler (stat unit, stat game, and the Unreal Insights tool) that shows exactly where frame time goes. Do not guess about performance — measure first, then optimize the actual bottleneck. This is the same "measure, don't guess" discipline from earlier chapters on debugging.

Remember Section 9's hybrid pattern is itself a performance tool: when a Blueprint graph turns out to be both hot (runs often) and slow, the fix is often not to abandon Blueprint entirely — it is to move that one function into C++, expose it with BlueprintCallable, and keep everything around it exactly as it was.

12. Glossary

13. Exercises

Exercise 1 Design the Event Graph for a locked door. Requirement: while the player is standing inside the door's trigger volume, pressing the E key should open the door if the player has a key (a Boolean variable bHasKey on the player), or print the string "Locked!" if they do not. Pressing E while not near the door should do nothing. Describe (in words or an ASCII diagram like the ones in this section) which Event nodes you need and how the Branch logic should be wired.
Show answer

An InputAction event alone cannot tell whether the player is near the door — key presses are global. The graph needs a Boolean flag tracking "is the player currently in range," set by the door's own overlap events, then checked when the key is pressed.

[Event ActorBeginOverlap] | exec v [Set bInRange = true] [Event ActorEndOverlap] | exec v [Set bInRange = false] [InputAction: E] (Pressed) | exec v [Branch] Condition o<--- bInRange | True | False | v v [Branch] (do nothing -- not Condition o<--- bHasKey near the door) | True | False | v v [Open Door] [Print String: "Locked!"]

Three separate Event nodes feed this graph: the two overlap events only maintain the bInRange flag and do nothing else; the input event does the actual work, but only after checking bInRange first, then bHasKey nested inside it. Two Branches in a row here behave like a C++ if (bInRange) { if (bHasKey) { ... } else { ... } }.

Exercise 2 A teammate wired the coin pickup graph but reports: "the sound plays and the coin disappears every time, but the score never goes up." Here is what they built. Find the bug and explain the fix.
[Event ActorBeginOverlap] | exec v [Branch] Condition o<--- NOT bCollected | True | v [Set bCollected = true] | exec v [Play Sound at Location] [Call AddScore] | exec Amount: 10 v (sitting on the canvas, [Destroy Actor] nothing wired into it)
Show answer

The [Call AddScore] node has its data pin filled in correctly (Amount: 10), but no execution wire runs into it. Execution flow goes straight from Set bCollected = true to Play Sound at Location, skipping Call AddScore entirely. A node with no exec wire connected into it never runs, no matter how correctly its data pins are filled in — data pins only supply values, they do not trigger anything by themselves.

The fix: drag a new execution wire from Set bCollected = true's exec output into Call AddScore's exec input, then from Call AddScore's exec output into Play Sound at Location's exec input, restoring the full chain: Branch (True) -> Set bCollected -> Call AddScore -> Play Sound -> Destroy Actor.

Exercise 3 For each feature below, decide: pure C++, pure Blueprint, or a C++ base class with a Blueprint subclass (hybrid)? Give one sentence of reasoning for each.
  • (a) Pathfinding logic shared by 200 AI enemies on a level.
  • (b) A specific boss fight's phase-2 trigger: "when HP drops below 30%, play this cutscene."
  • (c) The player character's core movement and jump logic, which every playable character needs, but each character reacts to events (landing, taking damage) slightly differently for VFX and sound.
  • (d) A one-off puzzle in level 7 where three specific switches must be activated in the correct order.
  • (e) The save/load system that serializes player progress to disk.
Show answer

(a) C++. It runs constantly, for many agents, and is a stable algorithm once written -- the "low change, high performance need" case from Section 8.

(b) Blueprint. It is specific to one encounter, will be tweaked constantly during playtesting ("HP < 30%? Maybe 25% feels better"), and does not need to be fast -- it fires once.

(c) Hybrid. The core movement math belongs in a C++ base class so every character shares one correct, fast implementation. The per-character reactions (VFX, sound) are declared in C++ as BlueprintImplementableEvent functions and given a body in each character's Blueprint subclass -- the exact pattern from Section 9.

(d) Blueprint. It is one-off content for one level, likely built and tuned by a designer, with no performance concerns.

(e) C++. Serialization needs to be correct, fast, and stable -- a bug here can corrupt a player's save file, and the format does not change once it is settled.

You now know how to read a Blueprint graph the way you read C++: events start a chain, white exec wires set the order, colored data wires carry values, variables and functions work like their C++ counterparts, and Branches/loops are just if/for/while drawn as boxes. You also know the question that matters more than "Blueprint or C++": most real systems are both, a C++ base class carrying the fast and stable parts, a Blueprint subclass carrying the parts that change every playtest -- talking to each other through direct references, interfaces, or event dispatchers depending on how tightly they should be coupled.

← Back to all chapters