Let’s start with the good news… I’ve finally got the mechanics of my new menu system up and running properly! Now players can use items, equip weapons, activate Ambiences, and change stats, all with a few nimble key presses. It’s definitely a much nicer, cleaner system than the previous, clunky menu systems. That being said, however, the interface appearance isn’t completely finished yet – but it’s getting close, and looking fairly good too.
In working on this new menu system, however, I ran into a few interesting bugs and errors, all with a common theme – incorrect data types that good old GameMaker didn’t know how to deal with.
Data Types, The GameMaker Way
For those of you who use GameMaker, you’ll know that the GameMaker interpreted language, GML, is very simple and has extremely flexible syntax (sometimes too flexible). The simplicity of GML, while being easy for non-programmers to use, comes at a cost – especially in terms of more advanced, “hard-core” coding functions that other languages support. For example, it’s always been frustrating not being able to define abstract objects in GameMaker, for things such as customizable or power-up-able weapons or items. The alternative methods are fairly big and slow, code-wise, so for Ambience I’ve decided to stick to simple cookie-cutter items while still allowing some other things to be customized, like stat distributions and loadouts.
Similarly, variable assignment in GML can be a double-edged sword: the simplicity of it gives you problems later on. Consider for example, a “real” language like Java (which I know a tiny bit of). In Java, where a simple variable definition and assignment looks like this:
Int foo = New Int(); foo = 1;
Notice the data type of the variable “foo” has been set to “Int”, or Integer. So if I tried to make foo a string, for example, that would throw an error. On the other hand, GML has a much dodgier approach, in which variables are defined and assigned like so:
foo = 1;
But here you haven’t told GameMaker what data type “foo” should be! I think GM simply “assumes” that it’s an integer or something, depending on how you define it, but then you could go ahead and simply redefine it later as follows:
foo = 1; foo = "Hello World";
and GM would have no problem at all with changing the data type of foo on the fly. But a sensible language like Java (or C++, or pretty much every other programming language in existence) would throw an error at that last line, if foo had previously been declared as an Integer.
So what weird and wonderful things did this lead to?
Part 1: Keeping it Real
When I first tested the menu system and tried to activate an Ambience, the game stubbornly refused and decided instead to do nothing. Literally…
Yes, that’s right. Not only did my character do nothing, he even failed to do nothing. You can’t get much worse than that.
Anyway, the problem here was that the Ambience to be used didn’t transfer correctly from the menu system to the “activate-an-Ambience” system. It went in as the Ambience of Wind, and came out as nothing with a capital N. Something, evidently, had been lost in translation. (By the way, that “Nothing” is actually the “default” case that turns up if the game can’t figure out what item or Ambience it’s using. It’s like the next best thing to a “throw-catch” construction, which GM also doesn’t support.)
It turns out that the error was a result of that flexibility and lack of explicit data types I talked about earlier. Specifically, it was because – wait for it – GameMaker decided to pass an integer argument as a string.
The only way I discovered this was by chance – during debugging, I decided to ensure the specific arguments were treated as reals, and not something else. And it worked. All I had to do was tell GM the obvious – that a number really was a number – and it fixed the problem.
Thank goodness I worked that one out quickly (it took only about 15 minutes!) – otherwise it could have easily taken hours to figure out what was a very subtle problem.
Part 2: The Ambience of “FATAL ERROR”
But the data type shenanigans weren’t over yet. I compiled and ran the game again, and ended up with this:
In addition, GM also presented a terrifyingly long stack frame for me to sift through to try and find the error – something like eight or nine scripts on the stack in all, which you can see in the GIF above. (To its credit, though, GM Studio’s stack frame readout is immensely useful when you’re trying to work out where an error’s coming from.) After staring despairingly at the stack frame for a while, I did the only thing I could think of doing in that moment to try and identify the problem: I opened all the scripts and put debug comments in them, then ran the game again and replicated the error. At least now I could trace the path of arguments as they were passed from script to script.
A brief word on how this system works: Ambience’s turn-based system is dealt with using a very long “master script”, which keeps track of the progress through each turn using a numerical value – I like to call it a “phase”. At each phase, the turn script will do something, then increment the phase to the next relevant value, and keep going until the turn is finished. When you decide to use an item or activate an Ambience via the menu, the turn script is called and triggers a chain reaction of events which results in that item or Ambience being used. That chain reaction was what I needed to hunt through to find the error.
Let’s walk through the debug readout to see where things went wrong. I think I’ve explained things fairly well here – the numbers you see after the script name are just the arguments that have been passed to each script.
And here’s the offending script – the one which passed on the array and caused the error:
This time, the error was actually due to my oversight – I was telling GM to pass a generic array to the effect creator script, which was actually looking for the colour represented by that array! In simpler terms, I had used the wrong script to get the colour I needed. So, I changed the script to the right one, like so:
And finally, instead of an exploding game, I got my exploding ring of the right colour. And everything else worked fine, too.
It all just goes to show that data types are really important to know about and keep track of – especially if you’re one of those people who started their programming journey by learning GML (like me!) That’s also why I’d recommend that GML programmers have at least an overview of some other language, like C++ or Java, to complement their knowledge of GML. That way, you can understand more about how things work under the hood, and you’ll also more equipped to deal with bugs and errors. (In any case, it’s certainly better than “Nothing”.)