In programming, sometimes things go very wrong and you have no idea why. Bugs happen all the time, and a lot of this jobOr hobby. Whatever. involves tracking down and fixing your mistakes. On any sufficiently mature project you’ll probably spend more time testing and debugging new features than you spend writing them.
So it goes something like this:
You’re busy playing the game you’re developing when suddenly you crash to desktop. Looking a bit closer, it seems like you got a division by zero error. It seems the variable named “distance_to” was somehow set to zero. The odd thing is,that variable is used for calculating the distance to the closest quest marker, and you know for a fact you weren’t anywhere near one.
So clearly this bug isn’t your fault. Obviously your code is fine. This is probably a bug in the compiler. Or maybe the operating system. Maybe even the processor itself. To sort this out, you’re going to need to send an irate email to the guilty.
But before you fire off that salvo of email abuse, you figure you’ll have a quick look at your code, just on the off chance that this is somehow your fault.
It’s true that you can “solve” this problem by having the program check for a value of zero before doing any division with this variable. That would certainly stop the crash, but it wouldn’t fix the bug. According to how things are supposed to work, it should be impossible for this variable to be zero. Stopping the crash won’t address the fact that something is going wrong.
The problem looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Weather.Update (); Quests.Update (); MagicSpells.Update (); Towns.Update (); Damsels.Update (); Peasants.Update (); Player.Update (); Music.Update (); SoundFX.Update (); Crafting.Update (); Monsters.Update (); Network.Update (); Gui.Update (); Particles.Update (); Render.Update ();
The only place in the code that messes with the variable
distance_to is there on line 2, where the game looks at the player’s active quests, checks for progress, updates quest triggers, notifies them of quest completion, and that sort of thing. But the crash happens on line 13, when the interface tries to divide another variable by
distance_to, which has somehow been set to zero.
Keep in mind that the idea of a “line” of code can be pretty misleading, particularly when we’re looking at the program at such a high level. Each line in the above code could call other code that calls other code that calls other code. Picture a Matryoshka doll, except each doll contains five dolls which each contain five dolls, and so on. By the time you’re three levels deep you’ve got 53 = 125 dolls scattered all over the place. And this structure is a lot more than three levels deep.
Since these 15 lines of code comprise your core game loop, they will probably invoke a non-trivial percent of your entire codebase. These 15 lines could easily represent tens or even hundreds of thousands of lines of code.
That would make for a slow search if you were going to examine the whole thing a single line at a time. Calling this a “needle in a haystack” wouldn’t be hyperbole. The problem would be about that difficult.
Luckily, you’ve got a debugger.
Unluckily, you’re using Monodevelop to debug Unity code.
Welcome to Monodevelop
Right now you’ve got two programs running: Unity and Monodevelop. The idea is that Monodevelop will attach itself to Unity. It will watch what happens. It can show you where the program is and what all the variables are at any given moment. It can pause program execution so you can poke around and check what’s going on.
So all you need to do is fire the game up, and step through those 15 lines of code. After each step you can check the value of
distance_to, and hopefully you can track down what part of the code is setting it to zero.
For reasons that will become clear later, I wasn’t able to get proper screenshots of Monodevelop in action. The following image was swiped from the Unity manual:
That little window in the lower left is where the action happens. When you pause the program to look under the hood, that’s where it will show your variables.
So you hit F5 to begin debugging. This should attach Monodevelop to Unity so you can begin.
Except instead of jumping right into the action, Monodevelop pops up a dialog box asking you what process to attach to. You have two options: “Unity” or “Unity”. Which one is the right process? I’ll give you a hint: There’s no way to tell and it changes every time you restart the program.
You can select one or the other. Everything seems fine. Then you alt-tab over to Unity and Monodevelop beeps at you: “Couldn’t attach to process”. Note that it won’t tell you that you got it wrong until you alt-tab away.
And then five minutes later you run into the same problem because your head is full of code and you don’t have the patience or mental bandwidth to memorize this rando coin flip for this particular game session.
Whatever. Eventually you get the process attached. The program runs until it reaches the breakpoint you set on Quests.Update (); You look down to the watch window to see the state of the variables and it’s blank.
I don’t know why, but sometimes Monodevelop just… can’t. It has moods. Just close down this session and restart the debugger. Then alt-tab back to Unity and then GO BACK TO STUPID MONODEVELOP BECAUSE YOU PICKED THE WRONG PROCESS AGAIN.
Okay. All done. Now execution is sitting on Quests.Update(); and your variables are showing up in the watch window.
Again, I don’t have screenshots, but it will look something like this:
+GameManager +CraftingJobs +Damsels +Gui LastAutosave +MagicSpells +Monsters +Music +Network +Particles Paused +Peasants +Player PlayTime +Quests +SoundFX +Timer +Towns +WeatherSystem WindowedMode
You’ve got a big old list of variables, organized into a tree. Each of the items with a + in front of it is a collapsed branch.
The variable you want is under the +Player group. Somewhere in there is the variable
distance_to, and you need to watch that variable to find the one part of code that’s somehow setting it to zero.
So let’s take a look and make sure it’s not already zero. You click on the +Player variable to open up that branch. Only, instead of showing you all the variables in +Player, it has this level of meta-information about the variable itself, or C#, or whatever. I’ve never really figured out what they’re for, but they’re not relevant to your code. But underneath those variables is another branch. if you open THAT up, then you’ll see it’s got two more branches, one for “Static Members” and one for “Non-Public” members. Without getting into what these mean, let’s just say these are additional data about your variables. It’s not necessarily wrong to list them this way, but there are other ways to do this that wouldn’t bury your information under a bunch of layers of nesting like this.
The problem here is that the list is a bit fiddly. When you open a big branch, the vertical scroll shifts and you lose your place. So as you dig down through the structure you:
- Open a branch, which causes everything to shift.
- Find your way back to where you were before the shift.
- Scroll down through the now-open list and find the container for the thing you want.
- Click to open it, which sends you back to #1.
And Carmack help you if you need to look inside an array of values. Once you find the array, you open it up to see the values are stored in a list inside of that one, and then each and every value in the list is inside of its own branch. Like I said above, I don’t have screenshots of this but I’ve created this mock-up, which should give you a rough idea of what it’s like:
Despite the endless nesting and scrolling, you persevere. After twenty seconds of window-scrolling and branch-clicking, you finally arrive at
distance_to. You discover the value is set to something reasonable. Say 147.5. Whatever. That’s fine. It’s not zero.
So now you just need to advance “one” line of code, let Quests.Update () do its thing, and see if the value of
distance_to changes. You hit F10 to advance one line…
And then the variable window is completely reset. The view jumps all the way to the top and all the branches slam closed.
Once you’re done cursing and you’ve calmed down a bit, you grab your pickaxe and begin digging down through the layers of hassle and obfuscation again to reach
distance_to. Once that ordeal is over, you see that it hasn’t changed a bit. It’s still 147.5. So you advance one more line of code, the window resets again, you throw your computer out the window and live out the rest of your days as a crazed hermit in the mountains.
Or you use Microsoft Visual Studio.
VS seems to know what process to attach itself to and doesn’t ask me to guess a coin flip between “Unity” and “Unity”. It doesn’t lose track of the variables. The watch window looks like this:
There aren’t tons of extra layers of needless nesting and the structure basically reflects the structure of your code. And most importantly, the whole thing doesn’t reset the window every time you step through the code. Instead, the window stays still, and the program even highlights variables in red when their values change.
So you smack the step key 8 times and you see that
distance_to gets reset to zero someplace where you don’t expect. Congratulations, you found the problem in less than half a minute, rather than fighting through several minutes of wrestling with Monodevelop’s sadistic variable-hiding. This is a staggering boost to your productivity. (Or penalty, if you’re somehow trapped on MonoDevelop.)
It turns out that
distance_to was getting set to zero during… Crafting.Update ()? Apparently in the potion-crafting code you accidentally typed “distance_to” when you meant “distill_tonic”, and thus stored the value in the wrong variable. (This typo / bug was probably cause by autocomplete.)
You complete idiot.
The autocomplete in VS is a bit too eager for my taste. I DO want it to pop up suggestions, but I DON’T want it to “autocorrect” what I typed after I hit the spacebar. I’ve poked around in the options and haven’t found a way to turn it off, but the options screen is a vast labyrinth and I probably just need to keep looking.
A year ago, the connection between Visual Studio and Unity was a bit wonky and I couldn’t get the debugger to work. Thanks so much to the folks in the comments of this series who let me know that the whole thing is completely turnkey now. Your comments probably saved this project. I don’t think I could have endured much more of the MonoDevelop debug tools / torture device.
This is why I don’t have screenshots of the Monodevelop debugging circus. I switched the entire system over to Visual Studio and I’m afraid I’ll break something if I switch it back again. (Monodevelop can no longer read my project files after the switch, and I’m scared the problem might work both ways if I switched back to get screenshots.)
As nice as it is to have good tools, I should warn you that this project is actually winding down. I’m going to keep playing around with Unity (and keep writing about it here) but I’m going to abandon the procgen city stuff soon.
I’ll talk more about this in the next couple of weeks.
 Or hobby. Whatever.
The product of fandom run unchecked, this novel began as a short story and grew into something of a cult hit.
Let's ruin everyone's fun by listing all the ways in which zombies can't work, couldn't happen, and don't make sense.
A Lack of Vision and Leadership
People fault EA for being greedy, but their real sin is just how terrible they are at it.
The Plot-Driven Door
You know how videogames sometimes do that thing where it's preposterously hard to go through a simple door? This one is really bad.
Mass Effect Retrospective
A novel-sized analysis of the Mass Effect series that explains where it all went wrong. Spoiler: It was long before the ending.