[UE5] Function vs Macro: How They Work and When to Use Each

Created: 2025-12-12Last updated: 2026-07-19

A visual explanation of how Blueprint Functions and Macros differ under the hood. Covers the two decisive differences, whether Delay works and whether execution pins can branch, plus a hands-on skill cooldown built with both tools.

When you open the right-click menu to bundle up some logic you use repeatedly, Function and Macro sit right next to each other. It's easy to get stuck on which one to pick.

Both look like "tools for grouping logic," but they work in completely different ways . A Function is a subroutine that gets called; a Macro is a stamp whose contents get copied into place at compile time. That difference is where the decisive distinctions come from: whether you can use Delay, and whether you can branch execution pins.

This article explains the difference between Functions and Macros, starting from how they work. At the end, we build a "skill cooldown" for an action game using both tools.

A figure comparing a machine box and a stamp on a scale, representing Functions and Macros

What You'll Learn

  • The mechanical difference: a Function is called, a Macro is copied in
  • The two decisive differences: whether Delay works and whether execution pins can branch
  • The selection rule: Function by default, Macro only for the exceptions
  • Hands-on: build a skill cooldown by splitting the work between a Function and a Macro

Sponsored

Function Basics: The "Function" That Gets Called

A Function is exactly what a function is in programming. It takes input, processes it, and returns a result. It works as a subroutine that is called from somewhere and returns when it's done .

A Function is called and returns, while a Macro gets copied into place like a stamp
TraitDetails
Execution flowOne entry (execution pin), one exit (Return). Runs from start to finish in one go
Latent nodesDelay and Timeline cannot be used
Local variablesCan be defined inside the Function (see Blueprint Variables 101)
RecursionSupported
Pure FunctionYou can create a "pure calculation" node with no execution pins
DebuggingThe execution flow is clear and easy to follow

For example, checking "is the target within range" is a textbook Function.

Function: IsWithinRange (Pure, Input: TargetLocation, Range → Output: Boolean)
  → Calculate the distance between your location and TargetLocation → return whether it is <= Range

It changes no state, it just computes a value and returns it. Making logic like this a Pure Function lets you call it as a clean node with no execution pins.

Sponsored

Macro Basics: The "Stamp" That Gets Copied In

A Macro is not a function. It's a stamp that registers a group of nodes . Rather than an instance being executed each time you call it, its nodes are expanded (copied) into the calling graph at compile time .

TraitDetails
Execution flowCan have multiple entries and exits (execution pins can branch on the way out)
Latent nodesDelay and friends can be used
Local variablesNot supported (pass values through input pins or wires)
RecursionNot supported (the expansion would go on forever)
ExpansionNodes are copied into every place you use it, so graphs get fat
DebuggingYou end up following expanded nodes, which is a bit trickier than a Function

The headline capability that only Macros have is branching execution pins .

A Function has one entry and one exit, while a Macro can have several exits. Only a Macro can branch the flow on its way out

Just as a Branch node has two exits for True and False, nodes that "leave through a different path depending on a condition" can only be built as Macros. The stock DoOnce, Gate, and For Each Loop nodes are Macros the engine ships with.

The other exclusive is latent nodes like Delay . A Function operates under the promise of "run all the way through and return," so it can't wait partway.

Delay can't go inside a Function but can go inside a Macro. Anything that waits on time belongs in a Macro or the Event Graph
Sponsored

Comparison Table: Where the Decisive Differences Are

It all comes down to "called" versus "copied in."

A contrast diagram showing a Function as a closed box with one entry and one exit, and a Macro as a dashed box with multiple exits
ItemFunctionMacro
How it worksGets called (subroutine)Gets copied in (inline expansion)
Execution pinsOne in, one out onlyCan have multiple entries and exits
Latent nodes (Delay etc.)Not supportedSupported
RecursionSupportedNot supported
Local variablesCan be definedCannot be defined
Effect on the graphA single call node is enoughExpanded into every place it's used, bloating the graph
DebuggingEasySomewhat trickier

The Rule: Function by Default, Macro Only for Exceptions

The rule when you're unsure is simple: default to a Function, and reach for a Macro only when you need something a Function can't do .

  • Use a Function: pure calculations and state checks, logic that doesn't need branching execution pins, complex logic where you want easy debugging, recursion
  • Use a Macro: when you want to branch execution pins on the way out, or when you need latent nodes like Delay

There are three common mistakes. (1) Trying to put a Delay inside a Function (this is a compile error; move it to a Macro or the Event Graph). (2) Overusing Macros (expansion copies inflate both your graph and your compile times). (3) Recursion in a Macro (infinite expansion, which errors out).

Sponsored

Hands-On: Building a Skill Cooldown With Both Tools

To get the distinction into your hands, let's use both tools on a single mechanic. The subject is a skill cooldown : an action game's special move, a shmup's bomb, an RPG's spell cast. "Mash the button all you like, it only fires once per interval" shows up in every genre.

Mashing the button still fires only one magic bolt, with a cooldown timer overhead. Mash all you want, one shot per three seconds

Here's the split. "Can I fire?" is a calculation, so it's a Function. "Wait three seconds and unlock again" waits on time, so it's a Macro.

Setup

Add the variables the check needs to your character Blueprint. Match the default values too.

VariableTypeDefaultRole
CurrentManaFloat50.0Current mana
SkillCostFloat25.0Mana one skill use costs
bIsStunnedBoolfalseWhether you're stunned

For input, receive a key event such as Left Mouse Button for testing (in a real game, replace it with an Enhanced Input IA_SkillEnhanced Input intro).

  1. The Function side: CanUseSkill (Pure): create a Pure Function that returns True only when CurrentMana >= SkillCost and NOT bIsStunned. It's a pure check that doesn't wait on time, so it's Function territory
Function: CanUseSkill (Pure / output: bResult / Boolean)
  → (CurrentMana >= SkillCost)  ─┐
  → (NOT bIsStunned)            ─┴→ AND Boolean → Return (bResult)
  1. The Macro side: the Cooldown Macro: create a new Macro with an execution pin and Time (Float) as Inputs and an execution pin (Then) as Output, then wire the body like this
Macro: Cooldown
  In → DoOnce
        Completed → Sequence
                      Then 0 → Out: Then                          ← let this activation through
                      Then 1 → Delay (Duration: Time) → DoOnce's Reset  ← re-unlock after Time seconds

DoOnce's Completed has a single execution pin, so it can't wire directly to both Out and Delay (a Blueprint execution output pin connects to only one destination). Insert a Sequence node in between: Then 0 lets the activation through, and Then 1 runs the cooldown timer.

Inside the Cooldown Macro: DoOnce lets the first call through and Delay unlocks it three seconds later. DoOnce is the lock, Delay is the locksmith

DoOnce is "a lock that closes after letting one call through," and Delay is "the person who comes to unlock it three seconds later." This body needs a Delay, so it cannot be built as a Function . Try searching for the Delay node inside a Function. It won't appear in the list (or it will error out).

  1. Combine them into the activation logic: wire Input Event → Branch (Condition: CanUseSkill) → True → Cooldown (Time: 3.0) → Spawn the projectile and you're done

Hit Play and mash the button. No matter how many times you press it, only one projectile comes out per three seconds, and after three seconds exactly one more gets through. Hold the button and wait ten seconds and you get exactly 4 shots (at 0, 3, 6, and 9 seconds). That count is your answer check. If a projectile fires on every press, check whether the Delay's pin is wired to Then instead of Reset.

To feel the division of labor, set bIsStunned to true and press Play. Even once the cooldown is up, no projectile fires. That single change shows that "can I fire?" (the Function) and "throttle the spam" (the Macro) are separate parts.

Two things to take away.

  • Pick the tool by role: calculation and checks go in a Function, control of time and flow goes in a Macro. Even within one mechanic, splitting the parts by what each tool is good at is what good design looks like
  • This Cooldown Macro is reusable forever: dashes, invincibility frames after a hit, preventing repeated message popups. It works for every "stop the spam" situation. Grow it into a general-purpose Macro

Bonus: Good to Know for Later

  • Don't confuse it with Collapse Nodes: right-clicking a group of nodes and choosing "Collapse Nodes" is purely visual tidying, and it can't be reused . If you want reuse, make it a Function or Macro (the collapsed node's menu also offers "Collapse to Function / Macro")
  • To share across multiple Blueprints: gather Functions into a Blueprint Function Library and Macros into a Macro Library, and you can call them from anywhere in the project. A general-purpose Cooldown Macro is worth putting there
  • When you want intermediate values in a Macro: Macros have no Local Variables. The moment you need complex intermediate calculations, the right move is to extract that part into a Function

Summary

ToolHow it worksBiggest strength
FunctionGets called (subroutine)Clear execution flow, easy debugging, local variables
MacroGets copied in (inline expansion)Branching execution pins, latent nodes like Delay

The phrase to remember: Function by default, Macro only for time and branching . Group your logic with that rule and your Blueprints stay readable and hard to break.

The next step after grouping logic is tidying the whole graph. Head to 10 Techniques for Organizing Blueprint Graphs. The design approach of "drop per-frame Tick and build around events" is covered in Designing Without Event Tick.

What could you use today's Cooldown Macro for in your own game? Dashes, special moves, stopping door spam. There's no shortage of uses.