Dev Diary #1: Working with Unreal Engine 4's Blueprint Scripting Language
The Skeleton War was developed almost entirely using Unreal Engine 4’s visual scripting language, known as Blueprints.
That, more than anything, speaks of the power that Blueprints have. Just about any logic, any algorithm can be implemented in Blueprints; the only limitation is in which Unreal functions are made available to Blueprints, and even then you can usually write a C++ function that does what you want and call it from a Blueprint.
For this project we wrote only a few such functions for operations such as sorting arrays and reading from the hard drive. So all of the game’s core logic was implemented in Blueprints.
Learning the Blueprint Language
When we started out almost a year ago, we started using Blueprints as a quick way to prototype our ideas in-engine. Between the three of us programmers we had little experience with them to begin with, but picked it pretty swiftly within the first week or two. The most difficult part of the learning curve was figuring out the roles each of the built-in superclasses played: GameMode, GameState, PlayerState, PlayerController, and so on. Familiarity with their purposes only came with lots of experimentation and time.
The language itself is rather elegant. It’s a lot prettier to look at than a screen of code and it’s a lot easier to quickly make sense of what a graph is doing than what a wall of code is doing. And the more you use Blueprints, the more you take advantage of the time-saving features it offers, like collapsing graphs to functions or macros and using math expression nodes.
Each Blueprint is essentially a class, and like in code, each class can have a number of different methods or functions. A Blueprint graph is comprised of many different types of nodes, each with input and output pins.
A simple example of one of our Blueprint functions, just to show roughly what it looks like. This function is called when the player presses the Bomb key.
In the graph above, the white line represents the execution flow, while other colours represent values that are grabbed when needed. Each colour represents a specific type of value: green for floats, red for Booleans, yellow for vectors, etc.
The “Make Transform” node is an example of using a struct, which you might be familiar with from C or other languages. The Transform struct contains two vectors and a rotator. A vector is itself a struct containing three floats, as seen at the bottom. You can make/break user-defined structs as well.
Later in the project, when it came time to start development in earnest, even though we were all comfortable coding in C++ we decided to stick with Blueprints since it seemed to fit our needs.
If I were to embark upon another project now, however, I think I’d start out in C++. Blueprints provide a great introduction to the Unreal libraries and class structure, but C++ remains more flexible, and more importantly, a lot easier to manage with source control.
Working with Source Control
This brings me to the major stickler with Blueprints: they’re a pain to merge. If you can get each of your developers working on different Blueprints, you’ll probably get along fine, but if you have the misfortune to be working on a Blueprint that someone else has also made recent changes to, you’ve got a big headache waiting for you.
Blueprints are simply .uasset files, which Git recognises as binary objects. Consequently there’s no nice auto-merging or line-by-line diff tool – though the Unreal Editor does offer a diff tool of sorts, it’s not really that practical to use for merging.
I found the best way to handle such conflicts is basically to back up your copy, then overwrite it with the latest version, and finally open up both versions at once and copy everything you did into the active Blueprint.
Even then, it’s not just a simple matter of copy-and-paste, because Blueprints don’t allow you to copy some things like variables and functions. In other words, it’s a pain. So coordination with your fellow developers is crucial to ensure you’re not stepping on each other’s toes.
We learnt all of these things on the job, with many of us learning how to use Git at the same time. Even with helpful GUIs like TortoiseGit and SourceTree, it’s important to understand the concepts behind actions like pulling and branching.
- Blueprints technically let you use gotos, since you can wire up the output of any execution node to the input of any other execution node. Normally gotos are a terrible practice in programming for the spaghetti code they give you, but when everything’s mapped out visually, it’s still relatively easy to follow the logic.
- Updating a blueprint between engine versions – 4.7 to 4.8, for example – can occasionally cause the blueprint to become corrupt, which it did for us. The blueprint is still usable, but acquires some annoying quirks that you have to be aware of, like default variable values being lost (you can set them in the constructor to fix this), and causing glitches when you compile them unless you delete the project’s Intermediate folder first.
- The Unreal Editor crashes. A lot. You sort of learn which actions make it crash and work around them as time goes on (for example, undos in a Blueprint graph are usually a no-no), but it’s still pretty important to save constantly. Hopefully these crashes will decrease as time goes on – already the latest version is up to 4.10, while we were using 4.8.
Ultimately, Blueprints are a lot of fun to work with, especially if you feel intimidated by walls of code. As an entry point into making games with Unreal, I heartily recommend it. And maybe once you’ve found your feet you can give Unreal’s C++ a go – it’s actually a lot nicer than standard C++ thanks to their custom variable types and libraries.
By way of disclaimer, these are all my personal opinions on Blueprints and not the entire team’s. Maybe I’ll convince one of them to write a blog post in the near future. In any case, I intend to write a few more of these, the next one focusing on UMG (Unreal Motion Graphics) and also offering a bit more insight into the actual development our game, not just Unreal.