Managing complexity in Blueprints

Managing complexity in Blueprints
As you build larger projects with Blueprints, it’s easy to end up with an overwhelming sea of nodes. However, we’ve built in a number of different encapsulation and code reuse mechanisms to help you battle the chaos.

To encapsulate something is “to show or express the main idea or quality of (something) in a brief way”; in other words we can hide a complex sequence of nodes with a simple stand-in that conveys the same meaning or idea. You can still drill down and see how it works ‘under the hood’, but you don’t have to worry about the details when looking at the broader picture.

Code reuse is frequently paired along with encapsulation, but basically it’s the approach of applying the same idea (set of nodes) in different situations without having to duplicate your work each time. It’s obviously more effort to duplicate the work each time, and any bugs or feature changes would have to be done in each copy independently.

Here’s a quick comparison of the benefits and limitations of the different approaches:

[table=“width: 550, class: grid”]

Functions
Events
Macros
Collapsed Graphs


Execution Paths
One
One
Any
Any


Non-exec Outputs
Any
None
Any
Any


Latent actions
No
Yes
Yes
Yes*


Add components/timelines
No
Yes
No
Yes*


Other events
No
N/A
No
Yes*
  • A collapsed graph inherits the limits of the graph it is in, e.g., a collapsed graph inside of a function still cannot contain latent actions or timelines.

Collapsed Graphs
You can make a collapsed graph from a selection of nodes by right-clicking on one of the nodes and selecting ‘Collapse Nodes’. Any wires to nodes outside of the selection set will become inputs or outputs to the new collapsed node. Once collapsed, you can hover over the node to see a preview of the nodes in the contained graph.

CollapsedGraphNode.png

[collapsed graph]

Collapsed graphs can contain any kind of node that is allowed in their current context and have no limits on their inputs/outputs. Collapsed graphs may seem like the best pick of the bunch with no limitations on the nodes they contain, but they are limited to only encapsulation, not code reuse. If you copy-paste a collapsed graph, all of the nodes inside get duplicated too, making a new collapsed graph rather than referencing the original.

Functions and Events
You can create your own empty functions using the Add Function button on the ‘My Blueprints’ toolbar or turn a selection of nodes (if it meets the limitations) into a function using the ‘Collapse to Function’ option when right-clicking on a selected node. You can make new events using the Add Custom Event… action in the graph context menu. A function shows up as a separate graph in the My Blueprints list, while events go into an event graph which can contain many different events.

Functions and events can both be called from other places in the blueprint just like calling a function defined in C++. Both of them define a single path of execution from an outside caller’s perspective, although they can have branching or loops internally. Events can also have their execution flow delayed by a latent action or even merge into the flow for another event.

EventNode.png

[Event declaration]

EventOrFunctionCall.png

[Event or function call]

Functions are guaranteed to execute and return immediately by limiting what kinds of nodes can be placed in a function (latent actions, timelines, etc… are all prohibited). This allows them to return a value to a C++ caller, which isn’t possible when using events or macros.

Functions and events are also how C++ can call into Blueprints. C++ can define a function as a BlueprintImplementableEvent which can be implemented in a Blueprint. These functions have no built-in behavior, and will do nothing if called without a blueprint implementation, but they can instead be declared as a BlueprintNativeEvent, and provided with a C++ definition as well. Although these UFUNCTION() keywords mention just events, they will turn into either events or functions in a blueprint when implemented in a Blueprint, depending on whether they have any return values or not.

Macros
You can create a new empty Macro using the Add Macro option on the ‘My Blueprints’ toolbar, or turn a selection of nodes into a macro using the ‘Collapse to Macro’ option when right-clicking on a selected node. You can also create Blueprint Macro Libraries, sharing macros across many blueprints. This makes them one of the most effective means of Blueprint code reuse. I’ll go into more depth on macros and macro libraries in a future post.

MacroCall.png

[macro instance]

Macros can have arbitrary inputs and outputs, including execution wires. Macros can be called in the same way as functions or events from a blueprint, but they aren’t visible from C++ code. Like collapsed graphs, macros don’t really exist in a compiled blueprint; every instance gets expanded out into a unique set of nodes during compilation.

Since you can have more than one instance of a macro, some things like custom events are prohibited inside of the macro, since there’d be ambiguity in which one (or in which order) to execute them. Timelines and Add Component nodes are currently also prohibited due to implementation details, but we’d like to allow them in the future.

Comments
Comments are basically a present to future-you from current-you; explaining what your intentions were or making a note of potential issues and additional work to be done.

Comments.png

You can place a comment box around selected nodes by pressing C. By default these boxes move all contained nodes around with them, but you can change that behavior as well as the color in the Details panel. You can edit the comment just like any other editable title by double-clicking on it or pressing F2. You can also comment any other individual nodes using the field in the context menu for that node, which will show up as a bubble above the node.

Finally, you can write a comment that will show up in the Content Browser tooltip for your Blueprint by ‘Blueprint Properties’ and editing the ‘Blueprint Description’ in the Details panel. This is really useful for gameplay Blueprints that don’t have a thumbnail preview, but it’s still good to add usage notes even for things like level props that have a thumbnail.


Feel free to reply with questions or comments below or join me on Twitter at @joatski](https://twitter.com/joatski).

I would add blueprint interfaces, which can used to reduce the complexity too. You can write functions with it and add them to other blueprints.

edit: its only for abstract function declaration, not for implementation as mentioned later in this thread

Can you expand on this? I thought that blueprint interfaces could not have any blueprint networks in them, but only served as a way to have channels of communication between different blueprints?

Heya,

Hyperloop is correct, an interface is essentially a contract promising that you will implement a set of functions, but doesn’t include an implementation of any of those functions. Each Blueprint might implement them completely differently (or not at all if it doesn’t implement the interface, in which case the interface message calling it would just safely and silently fail).

If you want to write code once and share it between several Blueprints, the best way right now is to use a Macro Library. I’ve got another blog post in the works on them that should go up soon, but at a high level they let you make macros that can be called from any other Blueprint based on the same parent class (typically Actor).

[edit]Macros and macro libraries post is now up[/edit]

Cheers,
Michael Noland

Oh my Bad for the misconception. I thought the interfaces can do both: abstract and normal implementation of functions.

This is crazy, you have made it absolutely impossible to make modular construction scripts. The only way to reference one blueprint from another is with the “Add child actor component” functionality, which gives you no control over the created component at all, no passing references or triggering events or even setting variables. Not to mention that child actors get removed and readded to the scene every time the construction script is ran, severely slowing the editor.

The only alternative to adding child actors is macro libraries, and now it turns out that macros are severely limited in functionality, not being able to add components.

Isn’t the goal of a Component Entity system like we have here to allow for extreme composability? Why do blueprints have these weird limitations? Why can’t a blueprint be a component?

I don’t want to be rude, but I recommend you to study UE4 a bit more and behave yourself less agressive.

You are doing this, like, super wrong.

  • If you want to reference particular instance of Blueprint - you should create Actor variable and assign instance of blueprint to this variable. You’ll get access to variables, events, functions and etc.
  • If you want to reference base Blueprint itself - you should create Blueprint variable.
  • If you want to spawn instance of Blueprint - you should create Class variable, assign Blueprint class to it and then use “Spawn Actor from Class” node.

Well, why car can’t be a component of a car? It’s a matter of commons sense and architecture. You can find everything you are looking for in variables, component system serves another purposes.

I apologize, it was late and I had wasted a couple of hours trying to make my thing work and it was the third time I discovered things could not work like I was hoping they could.

Thanks for the tip, I tried this now and it certainly allows to have more control over the spawned actor and allows me to set properties, but it also runs into a limitation. Actors can not be spawned in a construction script. It inspired me to explore the child actor component again, and it seems I can cast the child actor to its class and then invoke its functions, so that’s nice.

This is not a matter of common sense, why can’t a boat be a component of a boat? But in my case, why can’t a window be a component of a house?

Anyway, you’re not being rude and I am sorry for setting an aggressive tone in the discussion, there’s a bunch of counter-intuitive limitations in blueprint that I feel make it hard to use blueprint for extending Unreal Engine with more than just self contained actors.

Thanks for the post, I think this is a really interesting topic and one well worth discussing out in the open. Let me see if I can talk about one thing at a time:

Child Actor Components do not allow very convenient customization

This is true at the moment, but we’d like to fix it. Right now we are trying to work through some nasty bugs related to CAC - this is a feature that is being used more heavily by our new community, but we are making progress! Once we think its stable, we want to make them work like adding a component, where selecting the component/node shows you the actor properties in the details panel and lets you configure. Also ‘Expose On Spawn’ variables should create pins just like a Spawn Actor node. This will probably take some time, but we will have it up on the roadmap trello soon so you can track our progress.

Child Actors being destroyed and re-spawned slow things down

We haven’t run into this too much yet, but it’s certainly a valid criticism. The reason that we ‘tear down’ the components and re-run the construction is that it makes procedural content very robust. We had a prefab system in UE3 but it was very buggy and complicated, mostly because it tried to merge the graph of components saved in the level with the new graph of components from an updated prefab. In UE4, we felt robustness was the #1 priority, so went with a model where we could throw out all components and guarantee a ‘clean slate’ each time we run the construction. One option if you are seeing slowness is to turn off the ‘run CS on drag’ option the Blueprint Properties. That way the CS only runs when you finish moving, not every frame of a move. There are probably some good optimizations we can make to the whole ‘rerun construction’ process as well that we will hopefully work on at some point.

Cannot create a Blueprint of a Component

This is something which is theoretically totally possible (and some big teams are already doing), we just haven’t had enough time to test it before wanting to enable it. I believe it is coming very soon though!

Just an update, 4.2 introduces the new function library blueprint feature. I thought this would be the solution to my troubles, but unfortunately calling “Add ChildActorComponent” does not even work in a function library blueprint. So it’s still impossible to have modular procedurally generated blueprints visible in the editor.

Hi ,

Have you tried just calling SpawnActor with an actor reference? You have to manually track the spawned actor and destroy it when your parent actor is destroyed, but otherwise it should work fine.

Cheers,
Michael Noland

Ah, I thought it wouldn’t work because of the no spawning actors in the construction script thing. I will try it tomorrow. Thanks for the hint :slight_smile:

Thank you very much for your explanation. That helps a lot.

One thing I was confused about is which is the best approach if I want to make some utility functions. For example, let’s say I want to a function/macro to find the nearest instance of a given class with a certain property (e.g., find the nearest instance of the cat class with the hungry property set to true). I know how to do this with a macro (I think). Is there any downside in using a macro instead of a function? For example, will it significantly bloat my code or run slower?

Can I do this with a blueprint function? If so, would I just make a blueprint class which inherits from actor, and put the FindHungryCat function along with other utility functions in there?

Basically, I want to know if I should always use macros or if/when/how to write a blueprint function instead.

Thanks a lot.

Sorry for the thread necro, but I was about to post this exact question (when to use macros vs. functions - in a best practices sense). There has been some concern on our team over some cyclical dependency/constant recompilation issues with blueprints, and both macro libraries and function libraries have been brought up as culprits. I’d love to get some official guidance on the benefits/pitfalls of heavy usage of macros and functions. Is there anything we should watch out for? How much is too much? etc.

Thanks!

For most intents and purposes, there is no significant difference between them in terms of cost, so use whichever one fits the job better. I generally use functions for things that make sense as functions, and macros for things where I want multiple execution wires out, etc…, but there’s not a hard and fast rule.

A function called several times ends up using less space in bytecode than a macro but has the overhead of the function call (although compared to the general overhead of Blueprints this isn’t very significant). Functions also support recursion while macros do not. Local intermediate terms in a function are also only allocated when that function is called, as opposed to being allocated for the lifetime of the object as is the case for event graphs (since Macros can be used either in event graphs or functions, this isn’t a strict win/loss for them, it depends entirely where they are used).

RE: cyclical dependencies, we’ve made huge improvements in 4.7 that should hopefully eliminate dependency issues and Blueprint COL corruption.

Cheers,
Michael Noland

That are some cool features to get things organized and clean. But things like collapsed graphs is clearly something that has to do with cleaning up. Putting your dirty logic in boxes or drawers. :wink:

When I prototype I don’t care at all about looks or having it organized the only thing that matters to me then is that stuff works when I hit play. I also have the habit to not use multiple reference nodes like Get Actor. So when I have a actor that I manipulate in a lot ways I have lots of lines coming out of that one node reaching to far away Set nodes. It has the advantage that you can quickly see where that particular actor gets manipulated by following the lines but it also gets unclear as you add more to the blueprint and with it more lines that also cross in all kinds of ways.

My proposal would be a switch that hides all lines and nodes of references (Gets) and instead shows the reference as the name in the set nodes. So you can have the best of both worlds by switching it.

I agree…
I have a big struggle to get this work properly: https://forums.unrealengine.com/showthread.php?68646-Still-a-problem-with-acessing-a-child-actor-component-s-variables

If this would work properly, UE4 would have basiically a fully working “prefab” system… dream:rolleyes: