The Blueprint survival kit: functions, events, macros¶
Most beginner Blueprint graphs get messy because every tool looks like "a box with pins." The survival skill is knowing which box creates a callable entry point, which one just organizes nodes, and which data container keeps related values together.
The one-minute version¶
- A function is a named chunk of logic you call when you want an answer or a reusable action. It can have inputs and return outputs.
- A custom event is a named execution entry point. It is good for signals, delayed flows, timers, and replicated event calls.
- A macro is a reusable node graph that expands into the caller. It can have multiple execution inputs/outputs and is useful for graph patterns, but it is easier to overuse than functions.
- A collapsed graph is mainly organization. It cleans up a graph but is not the same contract as a function or macro.
- Pure functions have no white execution pins. They are evaluated when an executing node needs their output, and can be evaluated more than once.
- Structs, enums, and arrays are the first data tools to learn: struct = bundle related fields, enum = named choices, array = ordered list of one type.
The three questions¶
When a graph starts getting tangled, ask:
1. Am I naming a reusable action or calculation?
-> function
2. Am I creating a gameplay signal or delayed/replicated entry point?
-> custom event
3. Am I packaging a repeated node pattern with flexible exec flow?
-> macro, carefully
If the answer is only "this graph is visually too big," use a collapsed graph or split the real responsibilities first. Organization is good, but hiding a bad design inside a single node does not make it easier to debug.
flowchart TD
Need[Graph is growing] --> Reusable{Need to call this by name?}
Reusable -->|Yes, one exec path or return value| Function[Function]
Reusable -->|Yes, event-style signal| Event[Custom Event]
Reusable -->|No, just visual cleanup| Collapsed[Collapsed Graph]
Reusable -->|Repeated flexible node pattern| Macro[Macro]
Functions¶
A Blueprint function is a named graph owned by a Blueprint class, Blueprint Function Library, or similar function owner. You call it from another graph when you need its behavior.
Use functions for:
- calculating a value, such as
GetHealthPercent; - wrapping a small action, such as
OpenDoor; - validating a rule, such as
CanInteract; - exposing a clean operation for another Blueprint to call through a reference.
Function: GetHealthPercent
Inputs:
CurrentHealth (float)
MaxHealth (float)
Outputs:
Percent (float)
Body:
if MaxHealth <= 0:
return 0
return CurrentHealth / MaxHealth
Functions have a clearer contract than loose nodes in an event graph. Inputs say what the function needs. Outputs say what the caller receives. Local variables inside the function are scratch values that disappear when the function returns.
Pure vs impure¶
| Function type | Blueprint shape | Use when |
|---|---|---|
| Impure | Has white execution input/output pins. | The function changes state, calls other impure nodes, or should run at one explicit point. |
| Pure | Has data pins only. | The function only answers a value question and should not change game state. |
Pure does not mean "free." It means "no explicit execution wire." The compiler evaluates a pure function when another node needs its output. If you wire one pure function output into three different impure nodes, expect it to be evaluated for those uses.
For cheap getters, that is fine:
GetHealthPercent -> Progress Bar Percent
For expensive work, such as searching the world, do not hide it in a pure function and then wire it everywhere. Run it deliberately, store the result, and make the cost visible.
Custom events¶
A custom event is a named execution entry point in an event graph. Something else must call it; it does not run by existing just because you named it.
Use custom events for:
- starting a delayed or timer-driven flow;
- receiving a signal from another graph in the same Blueprint;
- simplifying a graph where several execution paths should converge;
- replicated Blueprint events, when networking rules matter;
- editor-callable buttons for placed actors, when you deliberately mark them callable in the editor.
Custom Event: ApplyPoisonTick
Inputs:
DamageAmount (float)
Body:
Apply Damage
Print String "Poison tick"
BeginOverlap:
Set Timer by Event -> ApplyPoisonTick every 1.0 seconds
The big beginner difference from functions: custom events can be used as event entry points. They are the natural shape for timers and delayed event flows. Functions are the natural shape when the caller wants a return value now.
Do not use a custom event just to avoid making a function. If the logic has a clear input/output contract and no event-style behavior, a function is usually cleaner.
Macros¶
A macro is a reusable graph that appears as one node, but at compile time it is treated more like "paste this graph here" than "call this function."
Use macros for:
- repeated graph shapes with multiple execution exits;
- flow-control helpers;
- small convenience patterns that need wildcard pins or flexible exec flow.
Examples of macro-shaped ideas:
Is Valid with logging:
Input Object
Valid exec output
Not Valid exec output
Print label when invalid
Try Get From Array:
Array
Index
Found exec output + Item
Not Found exec output
Macros are powerful because they can look and behave like custom nodes. That is also why they can become dangerous. A macro hides real graph work at every call site. If the macro contains a lot of logic, latent behavior, world searches, or side effects, debugging the caller gets harder.
Use this rule: make a function unless you need a macro-only shape.
Collapsed graphs¶
A collapsed graph is visual organization. You select nodes and collapse them so the parent graph is easier to read.
That is useful for:
- keeping a one-off event graph readable;
- grouping setup nodes under a name;
- temporarily cleaning up while you decide whether the logic deserves a real function or macro.
It is not a strong reuse boundary. If you want to call the same logic from another Blueprint or give it a stable input/output contract, promote it to a function. If you want a reusable flow-control-shaped node, make a macro.
Structs, enums, and arrays in five minutes¶
These are not communication tools, but they stop your communication graphs from turning into loose pin soup.
Structs¶
A struct bundles related values into one value:
Struct: FInventoryItem
DisplayName (Text)
Icon (Texture)
Count (Integer)
Weight (Float)
Use a struct when several values travel together. A hit result, transform, and vector are all structs: they are a named bundle of fields.
Beginner rule: if a function has six related input pins, consider whether it really wants one struct input.
Enums¶
An enum is a named set of choices:
Enum: EDoorState
Closed
Opening
Open
Closing
Use an enum when a value should be one of a known set of states. It is clearer
than magic strings such as "open" and safer than several booleans that can
contradict each other.
Bad:
bIsOpen = true
bIsOpening = true
bIsClosing = true
Better:
DoorState = Opening
Arrays¶
An array is an ordered list of values of one type:
OverlappingPlayers = Array of BP_PlayerCharacter references
Use an array when you need zero, one, or many:
For Each Loop OverlappingPlayers
-> Is Valid Array Element
-> Apply Damage
Array indices start at 0, not 1. The first element is index 0; the last
valid index is Length - 1. An out-of-bounds warning is not an Accessed None
problem; it means the list did not contain an item at that number.
For long-lived "who is currently inside this trigger?" state, a Set can be better than an Array because it avoids duplicates. But Arrays are the first container most Blueprint beginners need.
When these tools fail¶
They mostly fail by compiling red, silently not being called, or making a graph harder to reason about.
| Tool | Common failure | What to check |
|---|---|---|
| Function | Caller cannot see it, outputs are wrong, pure function feels inconsistent. | Access/private settings, target reference, pure-vs-impure choice, repeated evaluation. |
| Custom event | Nothing calls it. | Search for call nodes, timer binding, dispatcher binding, or network event path. |
| Macro | Debugging is confusing or behavior differs at each call site. | Expand mentally at the call site; check wildcard pin types and side effects. |
| Collapsed graph | It hides a messy one-off instead of simplifying it. | Expand it and decide whether it should be a function/macro or split into smaller steps. |
| Struct | Data is stale or too broad. | Update the struct where the source value changes; keep fields related. |
| Enum | State machine has impossible transitions. | Switch on the enum and make every state explicit. |
| Array | Index warning, duplicate entries, stale references. | Check length, remove on EndOverlap/EndPlay, guard elements with Is Valid. |
What they do not do¶
- Functions do not magically run; something must call them.
- Pure functions do not cache by default. Store a result if you need one snapshot reused.
- Custom events do not return values to the caller.
- Macros do not make complex logic cheap or easy to debug.
- Collapsed graphs do not create reusable public behavior by themselves.
- Structs do not own Actors or keep referenced objects alive.
- Enums do not enforce gameplay transitions unless your graph does.
- Arrays do not filter invalid references or prevent duplicates by themselves.
The pattern everyone actually uses¶
For a door:
Enum EDoorState:
Closed, Opening, Open, Closing
Function CanInteract:
return DoorState == Closed or DoorState == Open
Custom Event ToggleDoor:
Branch CanInteract
if Closed -> Play open timeline
if Open -> Play close timeline
Function SetDoorState(NewState):
DoorState = NewState
Print String "DoorState=" + ToString(DoorState)
For an inventory pickup:
Struct FInventoryItem:
ItemId
DisplayName
Count
Array InventoryItems
Function AddItem(ItemData):
Find existing item by ItemId
if found: increase Count
else: Add item to InventoryItems
Custom Event PickupCollected:
AddItem
Destroy pickup actor
For a reusable guard:
Function GetHealthPercent:
pure calculation, no world changes
Macro Branch If Valid Target:
Input Target
Valid exec
Not Valid exec
Custom Event RefreshHud:
called when health changes
The key is not memorizing every node type. The key is giving each piece of logic the shape that matches how it is supposed to be called.
Lookalikes - which one do I want?¶
| Tool | Use when | Watch out |
|---|---|---|
| Function | You need a named action or calculation with a clear contract. | Pure functions can be reevaluated; impure functions need exec wires. |
| Custom Event | You need an event-style entry point, timer callback, delay path, or replicated event. | No return value; something must call it. |
| Macro | You need reusable graph shape with flexible execution pins. | Overuse hides logic and can make debugging harder. |
| Collapsed Graph | You need local visual cleanup. | Not a public reusable contract. |
| Struct | Related values should travel together. | Keep fields cohesive; do not make one giant "everything" struct. |
| Enum | A value should be one of named states. | Your graph still owns valid transitions. |
| Array | You need an ordered list of one type. | Check length and stale object references. |
| Set / Map | You need uniqueness or key-value lookup. | Useful soon, but not the first container a beginner needs. |
Rule of thumb: functions answer or do; events start flows; macros package graph shapes; structs/enums/arrays make the data readable.
Going deeper¶
- Breakpoints and watch values - how to inspect these tools while they run.
- Accessed None trying to read property - why object references inside variables and arrays can be empty.
- The frame: Tick, timers, and timelines - why timers usually call custom events instead of functions.
- Print String and the Output Log - quick feedback while shaping small functions/events.
- Official docs: Functions, Function Calls, Custom Events, Macros, Collapsing Graphs, Blueprint Variables, Struct Variables in Blueprints, Flow Control, and Blueprint Arrays.
- Engine source (requires engine access - we do not reproduce it here):
function entry nodes through
UK2Node_FunctionEntryinEngine/Source/Editor/BlueprintGraph/Classes/K2Node_FunctionEntry.h; custom events throughUK2Node_CustomEventinEngine/Source/Editor/BlueprintGraph/Classes/K2Node_CustomEvent.h; macro instances throughUK2Node_MacroInstanceinEngine/Source/Editor/BlueprintGraph/Classes/K2Node_MacroInstance.h; Blueprint compilation inEngine/Source/Editor/KismetCompiler/; Blueprint asset type data inUBlueprintatEngine/Source/Runtime/Engine/Classes/Engine/Blueprint.h.