Skip to content

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