PART I: THE NOT-SO-FINAL STRAWS
Although I can’t pinpoint the exact moment I decided that Unreal’s Paper2D system was not going to work out for my purposes, I can certainly offer the two realizations that ultimately got me there:
- This new (top-down 2D) project requires that I implement grid pathfinding, preferably A-star, in order to continue.
- I do not know how to code in C++.
That was essentially the rub, but the actual abandonment was not possible until I had pursued and exhausted what I thought was a logical next step:
3. I should just implement A-star using Blueprints, they’ve worked fine for everything else I’ve needed to do so far, in fact they are a joy to use, I’m sure this will be a cakewalk.
I’ll pause here for belly-laughs from any actual devs reading this.
So, my current path on my current project has me turning away from Unreal and walking back to Unity, but let me talk here for a bit about why I need to, and what I’ve learned about both engines.
I love Blueprints. For context: I’ve used high-level scripting languages (python and lua mostly) in my game development day jobs for years. I’m comfortable with them, and their speed and power in prototyping are limited only by what the engineering department is foolish enough to expose to the design department’s prying little fingers: we will break every toy they give us, guaranteed. I’m no stranger to “real scripting”. Still, despite the reputation of Blueprints as being a sort of toy, or noob-script, I have to say using them has improved my skill as a scripter. It’s one thing to write a hundred-line script that moves a character in response to the mouse, it’s another to actually see the flow of the logic from one function to the next, to see where every variable is being set and queried, to see the game working its magic, node by node. It’s demystifying, it’s educational, it makes abstract concepts concrete, it encourages wild exploration by offering you a lego-box of possibilities and a half-dozen valid ways to implement whatever you can imagine. I’m far from done with Blueprints, and look forward to returning to them for future prototypes.
There’s a Brian Kernighan quote that one of the Bungie engineers used to have as his email sig:
“The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.”
I always found this comforting, as it implied that Real Programmers were not unlike me, as my problem-solving strategy has always involved print statements, often after every line in a new function, sometimes twice per line if I’m having a particularly bad day. In C#, of course, that just means writing this:
print("my variable value is: " + myVar);
and sticking it between two existing lines of code. Blueprints, unfortunately, are less friendly to this approach. To print from a Blueprint, you need a Print node, but if you want to include variables you’ll also need an Append, and some reference nodes for the variables, and you’ll need to break some existing connections, wire the Print and Append nodes into the sequence, then hook everything back up, making sure not to miss any critical wires. Then, if you ever want to stop seeing this result on-screen, you’ll need to pass through the process backwards, unhooking and re-hooking, then dragging the Print / Append assemblage off to one side, in case you need it later.
Yes, you can set a print node to just not print with a (hidden) checkbox, but it’s still there in your logic chain, and after a dozen such additions it can be argued that the Blueprint system’s vaunted visual clarity has kind of gone out the window. So picture this print thing as representative of several similar kinds of headaches, and after some time I arrive at this conclusion:
BLUEPRINTS ARE GOOD FOR:
- Total gamedev noobs who are struggling with concepts around what the various files in a game actually do, what order they should do those things in, and how they should talk to each other. These are hard concepts! Blueprints are a great way to learn about them!
- High-level Zen Matrix game devs prototyping new features and games. If you don’t need a lot of feedback and debug support, and you just want to see your wild new idea on screen, Blueprints probably represent a fast path without a lot of overhead between you and a working demo.
BLUEPRINTS ARE MAYBE NOT SO GREAT FOR:
- Journeyman devs and designers like myself, who need to do a lot of debugging and iterate a lot on whatever mechanic or feature is being developed. Stringing nodes together with wires feels really fun and powerful for a while. When you’re on your sixth or seventh attempt at realizing a small piece of gameplay, you can’t help suspecting that writing the whole thing out might actually be faster, regardless of your skill as a coder. At the very least your turnaround time for something simple like adding a print statement is going to go way down, and at this skill level that’s really nothing to scoff at.
PART II: THE FOR-REAL FINAL STRAWS
So, I talked earlier about attempting to implement A-Star pathfinding in Blueprints, and while some connoisseurs of my rarefied dry humor might have taken that as a joke, it’s sadly a real and true thing that I attempted. It’s another testament to my affection for Blueprints that I can’t even really say I regret it, even though I would be the first to admit it was a bad idea.
Pathfinding algorithms, which I have been dabbling in more and more over the past several years, have been my first introduction to recursive, self-calling functions. This is a computer science concept that can seem a bit like witchcraft to arts-and-lit types like myself, and has required a great deal of head-scratching already, with more surely on the way. Trying to cobble such a thing together in Blueprints got me to really pay attention to the conditions for building closed and open lists, the way those lists are iterated through, and the way decisions about paths are made. I’m still more than half-lost in these concepts, but I think I’ve come a long way from the day when I would just download a library file and futz with the inputs until I got something satisfactory.
The problem, or one of the problems, is that representing this kind of complicated algorithm in Blueprints creates so much bloat that the visual clarity referred to previously is just totally gone altogether. I’d like to add an image here of the whole node graph, but unfortunately Unreal won’t even let me zoom out far enough to see the whole thing, so here’s a substantial chunk:
If the two advantages of Blueprints are speed and clarity, I somehow managed to worsen both. Surprisingly, this ungodly mess did sort of work, after a fashion. Here’s an image from the UE4 prototype, where it’s supplying the player with an awkward-but-valid path around a nearby wall:
Note that telling debug text at upper left: “Long path time!” What I finally came up with wasn’t A-Star at all, but some mutant variation on “greedy best-first with early exit”. What little understanding I gleaned on this topic is due almost entirely to a developer named Amit Patel, who maintains a truly excellent set of interactive tutorials on various pathfinding solutions over at his Red Blob Games site, and however I end up proceeding I owe him a debt. I’ve worked out some of the basic concepts, but I think I’m still hung up on comparing one potentially valid path to another: that “Long path time!” error comes about when the direct path is blocked by an obstacle, and the search area tries to balloon up over the entire game map. In a typical hack, I detect this and forcibly exit the algorithm before the player’s CPU melts down.
The other thing I want to touch on that influenced my decision to step back to Unity was UE4’s implementation of a “tilemap”, that big grid that holds all the little squares of grass, water, and dirt that make up a top-down 2D world. The in-editor tools for making and painting tilemaps are actually pretty slick, so it cames as some surprise to find out some time into the process that the tilemap really doesn’t know much of anything about its own tiles. For example: I’m playing the game, and my mouse is over the tile map, hovering above a single tile. Is it grass, dirt, or water? What are its x and y coordinates relative to the total map? Unity can tell me this instantly; Unreal has no idea.
The lengths I had to go to to make UE4 tilemaps functional are kind of ridiculous in retrospect. At first I thought, well, fine, I’ll just create an arbitrary data structure to represent the map, a nested array of rows and columns, and do some math with tile size and mouse position to key into that wherever I need to. Cue the realization that UE4 doesn’t do multi-dimensional arrays. Instead, the online wisdom suggests abusing a (really useful) data structure called a Struct: Since a Struct is basically a big grab-bag of variables, and a Struct can have an array as a variable, you just make a “tile_struct” which has variables for x and y position (as well as sprite image, terrain type, etc), then you make a “tile_row_struct”, which contains an array of tile_structs, then your map is an instance of “tile_column_struct”, which sports an array of tile_row_structs.
Easy peasy, right? Now try manipulating the values of individual tiles, which requires a special node (Set Members In Struct), which, if used in a function, does nothing to the map, which lives outside the function, so you need another function specifically for updating the map with a new version of whatever tile on which you just changed one of the member variables.
I tried, I really did, but eventually I realized I was spending too damn much time being clever about data structures, and too little time doing anything resembling game design. It was time to shove off.
Doing computer game design without a strong computer science background has always been a challenge, and always will be. I think people (like me) who come at the field from non-traditional angles have a lot to bring to the table, and I know the developers of both of these engines think so too: features like UE4’s Blueprints and Unity’s robust 2D tile handling are lifelines for those of us who want to focus on crafting fun experiences rather than mastering tech for its own sake. These outreaches are of course imperfect, but I’m glad to sample them all, and glad to try to meet the tech halfway as best I can, hopefully getting a little closer with each attempt. Game design is a negotiation… and a lot of the time you’re negotiating with your own limitations, so some gratitude is in order for everyone who makes it their mission to make your life a little easier. Tonight I’m raising a glass to all the tools devs out there: I appreciate your work.