I have recently been accused of writing things that no one understands.
“I don’t even know what half those words mean.” A friend remarked. “All that stuff you post about your game.”
The effort of trying to bring a glimpse of a guy trying to do some simple game design to the masses is as difficult as the coding, it seems.
The problem is that coding and technical stuff is jargony. It is tough to describe complex things, after all, without falling into the easy rhythm of just using the complex words.
And, it turns out, if that isn’t a metaphor to describe the challenges I encountered in my code this week, I don’t know what is.
Falling back on the easy, I mean.
It wasn’t so much that I shortcutted or made bad decisions, per se, but rather that in my effort to do something complex I described it to the compiler—I wrote code—in a way that, um, confused it a little bit.
My code got just complex enough that I ran into one of those juicy jargony words that begrudge programmers all over the world: race conditions.
Let’s back up a bit. What the hell is coding anyways?
You probably know or have heard that computers speak binary. Zeros and ones. Right? And that is true. Virtually every computer program every built for every consumer product relies on binary computation. (I could go on about quantum computers or analog computers or whatever, but let’s keep this simple, shall we?) Digital computers speak binary.
I’d love to be able to spit out a string of zeros and ones long enough to fill a library and not make any mistakes and do so well enough that it results in the computer making a game called Pleck’s Mart that you all want to eventually play. But I will never be able to do that.
I can, on the other hand, write down all those complex ideas in a text editor in a language called Rust (which I have been learning quite well) and then ask this little bit of software on my computer to turn it into that string of zeros and ones so that it becomes said game.
That’s coding. Writing out complex instructions for a compiler to turn your plan into binary numbers.
And knowing this, you should also know that this can fail in two basic ways: Either you can write code so bad and wrong that the compiler tells you it kinda sucks and you should fix this or fix that and go back and try again because it doesn’t understand—or, you can write code that makes enough sense to get translated but results in something unintended happening when the computer starts mathing and computing all those resulting zeros and ones. We call the first bad code and the second, usually, a bug.
The first fail, bad code, happens to me a hundred times per day, and I go back and fix stuff. I mistyped a variable or forgot to make something mutable (flexable and changable in the system) or missed a semi-colon at the end of a statement or—the list goes on.
The second fail happens alot too, but is occasionally more subtle in as much as you don’t notice that the results are wonky until you really do. Easy to spot bugs are incorrect coordinates on sprites or calling a valid but incorrect variable or switching the signs on a math calculation. The hard ones are things like the thing that bit me in the butt this week: race conditions.
To understand this you have to also understand something that is probably pretty basic but important to my point: computers are fast. Freaking fast, these days in particular, but they have always been way faster than we realize.
You can look at this little video of my game that I recorded yesterday of things on the screen and realize that there are about five hundred individual images on the screen, each a file that needs to be loaded into memory, and simultaneously the computer is reading information from the screen, the mouse, the keyboard, my gampad controller, playing music, loading up individual click sounds for the little interactions, checking the math to see if anything I do causes any two of those little image files in the pretend space of the screen to crash into each other, and about a hundred other things I can’t even keep track of—and it is doing all of that on repeat over a hundred times per second.
Per second!
And I, the invisible conductor of this crazy digital symphony, need to to have staged all of that down to the millisecond.
And when things are simple, nothing really deeply goes wrong. At least not wrong enough to matter.
But then things get complex and—well, let’s go back to that symphony analogy: I play in an orchestra so believe me when I say it is easy for things to get out of sync. The timpani is pounding out a steady beat but then the clarinet comes in half a second late and the violas sitting next to the clarinets try to pace them, but the violins are watching the conductor and timing themselves to the baton, and then the trumpets who can’t hear anyone but themselves way at the back decide to wing it and jump in too. The result might be music, but it more likely will just become noise.
One thing got ahead of the next thing and the thing that was supposed to be following in a planned order happened out of sync and—that’s kinda a race condition.
In coding, as I understand it, a race condition is when the code is not exactly wrong, but you as the coder have not put in the right set of rules to stop things from getting out of sync.
On a small program you might not even notice.
On a growing and complex program? Chaos ensues. If things don’t crash outright, then you suddenly have eighteen soundtracks playing because they are not properly being removed and replaced, or all the objects in your room spawn a mere half a second before the rest of the room making it look like ten cases of tomatoes just blipped and appeared in empty space for a moment, or else your player flashes brightly because the lighting effects took effect a beat too late to maintain the illusion of that shadow you had meticulously coded. But then, mostly, yeah, mostly it just crashes. Or bogs down to a crawl as eighty-seven thousand copies of your player sprite spawn into the program and the computer (as fast as it is) just throws in the towel because even it isn’t that fast. And then it crashes anyways.
These things are not laziness in coding, but they are me making assumptions about that long string of zeros and ones and assuming that the computer can make sense of them as much as I think it can without tripping over its digital shoelaces.
They are adding a dozen more musicians to the orchestra and just hoping they will all keep perfect time.
Thankfully Bevy, the game engine I am using, has mitigations for this. I have spent a few hours restructing my code to give it a little bit more of a compartmentalized order and some timing conditions. That, for people who thing I am being to jargony, just means that I can specifically tell the computer to wait for one thing to happen before starting something that is supposed to happen after it, and I can also tell something else to count to ten and hold its breath before doing something else. Like cues and an enforced script, mostly. Sheet music and each musician with their eyes glued to the conductor and noise cancelling headphones so that they can only hear themselves. No more winging it and hoping for the best.
All of these challenges are themselves spawning out of something good, tho: progress. Real progress. The game is moving forward. As much as my code is occasionally tripping over itself, that can be managed and fixed, and the result still is that the game does something this week that it didn’t do last week. It’s progressing.
In this phase I have dug into objects: stuff on shelves, mostly. I built the core functionality that allows the little guy (who up until now was just walking around exploring) to order objects from the shop, carry them around in an inventory and put them onto shelves, pick them up again, and put them somewhere else.
And it is weirdly satisfying to organize cases of vegetables in the store or the warehouse. So long as the game isn’t racing and crashing that is.