Nevada Dev Log: Lists Start at Zero: The Madness of Computers

I’ve kept the increment node for illustrative purposes: it is unhooked (look for the “++” node) but it was the cause of this leveling up bug (when it was used instead of the addition node above).

I’ve completed my dialog pass (yay!) and have now embarked on something I actually find fun*: balancing the real gameplay. This is a huge pass: all of the interrogation battles right now have zero balancing. All enemies have default stats, so all you have to do is spam “question” and win the game (or probably eventually lose as your health slowly declines throughout play, since I also haven’t filled in the XP rewards, so you aren’t rewarded better stats or refreshed health either).

Needless to say that this is quite an important pass for the game to actually be, well, fun. Interrogations should require, minimally, that you achieved a certain character level beforehand. Setting an enemy character up is stone cold easy: I adjust the values in a data table. Those values are based on the approximate level I want the player to be at when the battle occurs. I’m starting with chapter 3, since chapters 1 and 2 are introductory. I figure you should be at least at level 2 when this battle happens, so I adjusted all the enemy stats accordingly.

However, as I expected, I immediately ran into a nasty, must fix NOW bug that I didn’t notice before for some reason. I installed a new cheat button to instantly reward me XP so I can level up without actually playing. I put this at increments of 100 XP. Level 2 is, well, 100 XP. So it hits that level immediately with no problem. The next level up occurs at 282 XP, so if I hit the cheat button again, nothing happens. Great. As expected.

When I hit that button once more to go to 300 XP… whoops. Level 3 never happens. It goes directly to level 4. Dang.

Figuring out how to fix a bug is a process of first figuring out what is causing it. If you can’t figure out the latter, any solutions you devise might be, well, fake news. And it is quite easy to fool yourself into thinking you’ve found the cause.

In my case, I thought that it had to be a double firing of the level up function. My thought went to this because leveling up is tied to event tick, which means the game engine is constantly checking to see if the player needs to level up, like at least 60 times a second or whatever.

Well, I thought, “the function is checking to see if the player XP is greater than or equal to 282, and the XP is 300. It is just hitting true twice before that number updates to the next value in the table, which is 519.”

Thinking this had to be the problem, I tried to patch in a few easy unreal solutions: gates and do onces. Both of these do the same thing in different ways: it blocks program execution until you explicitly open the gate again. When you have something happening more than once, you can usually put a gate or do once in there so it does the thing you want ONCE, then bam, STOPPED. Then, later, when you are absolutely sure things are cool, you use a custom event or something to open the gate back up.

I kept trying these solutions, to no avail. But I had misdiagnosed the bug, so I went down a weird rabbit hole of researching how do once and gate nodes work in Unreal’s documentation. Protip: usually if you find yourself looking at more and more exotic reasons for a bug, you’ve probably misdiagnosed the bug. They usually aren’t that weird. I did discover that do once and gate nodes often don’t work the way people think in functions because of how functions work in unreal blueprint: they are called and then killed off on the stack*, only to be born again when the function is called again. Get that? Functions do their thing, are yeeted, then are reborn. Guess what happens to things like a do once node once a function is reborn? You guessed it. It resets automatically. So it isn’t going to behave at all like a do once node when used this way. In plainer English: it functions like a “DO EVERY SINGLE DANG TIME” node.

I had my Eureka moment and simply took that code (it wasn’t a lot) and moved it out of a function and back in the event graph. That’s it, I figured it out, and I’ll have a great blog post about how functions work later.

Except, nope, it didn’t matter. I was still jumping from level 2 to 4.

More head on wall banging, and I finally found the true culprit. And it has to do with how computers number lists. This is a long blog post, heh.

Ok, so computers like to start lists at 0. And my player leveling data table starts, of course, at 0. I knew this, so I was careful, I thought, to make sure that when I get to the end of my level up function to take the variable that tracks which row of the leveling table to use and to bump it up one, so the UI readout is accurate. If I don’t do this, then “Player level 0” gets displayed, and level 1 is really level 2, and so on. I suppose I could just tell players to add one in their head, but that strikes me as a little amateurish!

The problem is that I goofed. I didn’t take that variable and then add 1 to it, then convert it to a string for the UI. I used the “++” or increment node. What this does is adds one to something and then sets the variable. So that variable that tracks the player row number was getting iterated and set twice. Once when it was supposed to in the beginning, and again when I was simply trying to set the game’s UI to report the proper player level. This doesn’t happen the first time you level up due to how I have things setup, but it happens every time after that.

A TL;DR summary: computers starting lists at 0 led me to use an incorrect operation on the variable that tracks what level a player is, causing the player to jump two levels each time. Ugh.

Anyway, with this fixed, I can actually go back to making the chapter 3 gameplay fun (I hope). Until next time, detectives!

*Writing dialog is fun, but it isn’t as fun as you think it is when you are trying to work up an entire game. That is probably one of the reasons why it is hard to have a good game story: you get so burdened with everything else working that simple stuff, like character dialog, feels like a massive chore.

*the stack is just a list of functions that a game engine (or any software) has to execute. This is internally managed by a game engine and you usually don’t have to worry about it.