A function is a collection of nodes compiled into a single individual unit, that can be called over and over from other places. A function does not support calling latent nodes like Delay.
They also only have one input execution wire, and one output execution wire.
A macro on the other hand, is basically a template for nodes and supports both latent functions as well as multiple input and output execution wires.
Unlike functions, which is compiled to a single unit, which is then called again and again, a macro is compiled as if you inserted all its nodes directly into your blueprint. This also means, that if you don’t use your macro, nothing will be compiled.
A real world analogy (of sorts) would be, if you needed to make 10 cars, a function would be one single factory which you then tell to make a car, 10 times. You get 10 cars, but all produced by the same single factory. With a macro on the other hand, you would build 10 factories to produce each car.
So, to sum it up:
Function
- Represents a single unit that is compiled once, and can then be called over
and over. - Does not support latent nodes.
- Has one input execution wire, and one output execution wire (Unless pure).
- Can have multiple inputs and outputs.
- Calls to functions can be replicated in network games.
Macro
- Represents a template of nodes, where each reference to it will be replaced by the actual nodes at compile time, and thus is only compiled if used.
- Supports latent nodes.
- Can have multiple input execution wires, and multiple output execution wires (Including no execution wires at all).
- Can have multiple inputs and outputs.
- Cannot be replicated, as they aren’t “tangible”.
Let’s say you have a function called GetCircleCircumference, defined like so:
And your event graph looks like this:
When you compile, your function is compiled to a single unit (one single factory), and your event graph tells that single function to calculate the circumference of a circle 5 times.
However, if you instead use a macro, defined like so:
And much like before, your event graph uses it to calculate the circle circumference 5 times:
You have much the same behaviour, but instead of having one single compiled unit you call over and over, all references to the macro are replaced by the contents of the macro, and thus, what is actually compiled, is this:
The fact that macros are inlined wherever they’re used, also means that when you don’t use them, nothing from the macros is compiled and thus, if it contains errors you won’t get any messages about it.
It is also important to keep in mind, that due to the inlining, errors you get in macros, are actually caused from where you use them!
For instance, consider the following macro called GetWorldLocationOneMeterInFrontOfComponent:
It does exactly what it sounds like. Takes a component as input, and outputs the location one meter in front of it.
If we compile now, nothing is actually compiled from the macro, as we’re not using it.
Now, say we use it to print the location when the game starts:
When we hit compile, the compiler outputs an error inside the macro:
As we can see, the Target inputs are connected to the input; however, due to the fact that it’s inlined, the error is actually occurring where we use it, like it would, had we used the nodes directly there. It is in fact compiling to the following, which makes it clear why we get the error:
Wiring the input of the macro where we use it, will satisfy the compiler:
Which it then compiles to:
And of course, if we had used it in two places without wiring the macros’ input, we’d get 4 compile errors of missing connections.
Hope that helps you understand the difference between functions and macros.
PS: You can right click on a macro and choose “Expand Node” to inline it directly in your event graph (Which is essentially what the compiler does to all macros before compiling).