Fundamental Blueprint Practices
- 1 Blueprint as programming language
- 2 Official treasures
- 3 Critical issues you can easily avoid
- 4 Clean code architecture
Blueprint as programming language
The best feature of blueprints: it allows us to start coding video games logic without any programming knowledge.
The worst feature of blueprints: it allows us to start coding video games logic without any programming knowledge.
There's no accidental copy-pasting text above. Visual scripting is a huge step forward giving people freedom of creation.
Nowadays everyone can create a new project and start with prototyping their idea. Learning engine and programming on the go.
The only problem is that official documentation fails to explain anything beyond "how to click into blueprints".
If you are lucky, you could already encounter people or YouTube videos explaining to you a few basic concepts of "engineering thinking".
- How to avoid killing your framerate and start asking yourself "how much performance do these scripts cost us"?
- How to avoid creating "gigabyte blueprints" - scripts that reference half of the game and you don't even know it, so you end up loading half of the game while opening Player's blueprint.
- Concepts of code architecture and Object-Oriented Programming. These what you touch every day, but probably you didn't know the name for it.
These are relatively easy things to grasp if someone explains it to you, but it can a lot of new concepts to "digest" at once.
No worries, you can simply learn one thing, use it to compose better blueprints, repeat it, improve your skills. And come back to this resource to learn the next things.
And don't let anybody fool you. Your job title maybe not a "gameplay programmer" and you may be unable to use classic programming languages yet, but blueprint visual scripting is still programming.
Epic did a great job of explaining many things about blueprint lately, although these materials are scattered on their sites.
This is a convenient list for you. This wiki guide is going to repeat and rephrase a lot of advice from official materials, you can approach it any order.
Critical issues you can easily avoid
Use Timers instead of Tick and Delay
Performing a lot of operations on blueprint Tick is one of the main reasons for the low performance of your gameplay code. Plus it makes blueprint messy and difficult to maintain if you call a lot of things on Tick.
Note: You should never use Set Timer by Function Name in blueprints. It's an unsafe practice. If someone would change the name of the function, this timer will stop working.
Use Set Timer by Event mentioned at the end of this article.
Calculations of blueprint node networks aren't magically cached
One of the common assumptions of new blueprint users is that result of blueprint operations is somewhat automagically cached in this network. It's not. If you'd connect a wire to a network of nodes doing some calculation, these calculations are performed as many times as you would connect wires to it. You need to save the result to a variable of operations after calling it for the first time and get this variable value later.
|proper image needed|
Avoid using "Get All Actors of Class"
It's an expensive function. Basically iterates on all actors in the world, checking every single one of them, if it matches your criteria. Good programmer almost never uses it, and then mostly for quick debugging purposes.
To obtain needed actors, simply register your actor to some manager when actor appears in the world. In blueprints, you'd call it on Begin Play most of the time. Use your custom manager class or extend class like Game Mode for this purpose.
If you're got the C++ project already, consider defining your manager in C++. Especially that Programming Subsystems are perfect for this purpose.
Avoid casting to blueprint classes
As official documentation Balancing Blueprint and C++ says
Avoid Casting to Expensive Blueprints: Whenever you cast to a Blueprint class BP_A (or declare it as a variable type on a function or another Blueprint) from BP_B it creates a load dependency on that Blueprint. Then if BP_A references four large Static Meshes and 20 sounds, every time you load BP_B it will have to load four large Static Meshes and 20 sounds, even if the cast would fail. This is one of the primary reasons it’s essential to have either native base classes or minimal Blueprint base classes that define the important functions and variables. Then, you should make your expensive Blueprints as child classes.
How to avoid that?
- Try to cast to blueprints that you know are already loaded into memory
- Use event dispatcher instead of calling a function on the target blueprint
- Use interfaces
- Use components containing specific features i.e. Inventory Component instead of putting this code into MyShopkeeper blueprint
- Use systems identifying actor instances by something else than class, i.e. Gameplay Tags
- Create native C++ base classes - it can be crazy useful if you write most of your logic in blueprints
Learn about hard references, soft references, and reference chain
- Hard references are the default type object reference in the blueprint, so you already use it all the time. If blueprint A references to blueprint B, opening A in the editor or loading it in-game automatically loads blueprint B.
- Reference chain. If blueprint A loads blueprint B automatically, this also means that blueprint B loads all its referenced blueprints automatically and all the assets used by those blueprints. That's why its super-easy to mess this up. If your Player Blueprint references all game systems directly, loading this blueprint will load all the game systems and assets used by these systems. So you end up loading gigabytes of data and waiting a minute to open a Player Blueprint. No worries, designers in professional studios often do this mistake.
Solving this requires using 2 "solutions" all the time.
- Soft references. It's the way to connect things without automatically loading references. You have to manually call loading it only when it's needed, i.e. loading sound or cutscene just before you want to play it, not at the beginning of the game.
- Clean code architecture. This is a big topic, but a fundamental part of the programmer's work.
Clean code architecture
You need to think about how to decouple things: isolate systems, features, classes and assets. It could be initially difficult to understand which way of doing things is clean. It will come by practice and constant thinking "is it proper way of doing things? can this be improved?".
In properly implemented game Player Blueprint does never know about the existence of a door or shopkeeper. Interaction of player with door or shopkeeper is executed by the Interaction Component. This component doesn't know about the existence of any actor class using it, i.e. Player, Door, Shopkeeper. You could eventually create enums or gameplay tags to define interaction types.