(Note: This is Part 5 of my GBA by Example series. A list of my other GBA tutorials can be found here)
We’ve covered an awful lot of drawing in these posts, but it takes a lot more than drawing code to make a game. One of the key parts of building something playable is letting users actually be able to interact with our code, so today I’m going to go over how to get user input on the GBA. It’s going to be short and sweet, because it’s really not that complicated on this platform, which is great, because it means that we can spend more time on building an example program this week.
By the end of the post today, we’re going to end up with a simple program that displays a sprite and changes the background based on what button was last pressed. It’s going to look something like this:
Let’s get started :)
I assume if you’re interested in these posts, you already know what a GBA looks like. Just in case, here’s a photo with all the inputs shows:
The GBA has 10 buttons that the user can press while a game is running:
Each of these buttons can be in one of two states - down or up. Conveniently, the state of every button is stored in a single 16 bit value (with only the lower 10 bits used). This value is known as the Input Register. It, and the location of each key’s corresponding bit are as follows:
|0x FEDC BA98 7654 3210
|Ignored / Undefined Data
|Left Shoulder Button
|Right Shoulder Button
|DPAD -> Down
|DPAD -> Up
|DPAD -> Left
|DPAD -> Right
The only bit of weirdness with all of this is that the GBA represents keys which are in their Up (un-pressed) state with a value of 1, and keys that are pressed with a value of 0. This means that if we were to read the value of the input register while the Start button was pressed, we would expect to see a value of 0x0000 0011 1111 0111, notice that the bit that corresponds to the start button is 0, because the button is down.
Turning the above table into a set of constants representing which bit is set for each key looks lkke this:
and using the above table, a function that returns a non zero value if a key is down might look like this:
Because we aren’t immediately inverting the value in the input register (like Tonc does), the bitwise logic for this can be a bit unintuitive, so let’s walk through how the above function works.
For the example, let’s assume that we’re testing to see if the Start button is currently pressed:
This gives us 0, because of how the GBA stores key states (Remember, 1 is UP), so we just return whether our result == false so that we get a non zero value when the button is down
If instead of the Start Mask, we checked a different button, like the A Button:
The KEY_MASK constant is important for this function to work, because we have no idea that the top 6 bits of this value are being set to (whatever it is, it’s junk data), and we want to be sure that we’re only testing our key_code value against data that we expect is in the input register.
Always masking the KEY_INPUT register by the KEY_MASK value seems a bit excessive to me though. What I prefer to do (and what you’ll see elsewhere on line), is to use a function that will store the value in the input register in a 16 bit variable, and perform the masking then. This function is called once per frame, and then you don’t have to worry about OR-ing with KEY_MASK every time you want to read a value from the hardware:
This is great, but it only lets us test if the user is currently holding down a key, it doesn’t let us detect if the key has been just pressed. This is great for things like charging an attack, but not as good for something like triggering a jump, because it’s going to read as true for multiple frames unless your user has the reflexes of a cat.
The obvious next thing we need to do is to be able to detect if the user has just started pressing or releasing a button. To do this, we need to store a second input state variable, that holds the input of the previous frame. To determine if a key’s state is new, we just have to compare the current frame’s input register to the one from the previous frame. It makes sense to do this register-copying inside the function we use to store the current frame’s input:
Then all we need are two new functions to detect key press and release:
If you’re confused by the above, writing it out on paper really helps, but I’m going to skip walking through it here because it really only matters long enough to write the above functions.
When it’s all put together, your input handling code might look like this:
That’s literally all there is to input handling on the GBA! You can stop here if that’s all you’re after, but I took it a step further and built the program you saw at the start of the article. I’m going to walk through how to put that together below.
But for the remainder of this post, and all future posts, I’m going to put the input handling code above into input.h
All the sprites that I’m using for the example project can be found on github. It isn’t super compact, but for such a simple program, that’s not really that important. If you want to follow along as I build this, grab the data from there. If you just want the final product, you can find the whole thing on github here.
The function to load the sprite data is as follows:
I’m not going to walk through this, because we’ve already covered how to load and set up sprites in a previous post
I’m also using a simple 1 colour background in the gif from earlier, which I just created procedurally like so:
Again, I’m not going to talk too much about this, because I covered it last week.
Great! Now that that’s out of the way, let’s do something more interesting.
The most obvious thing to do is to draw a different sprite depending on what button is currently pressed. This is pretty easy since we laid our sprites out sequentially in memory:
And then move the sprite off screen when we don’t want to draw any text at all:
In addition to drawing a sprite, let’s animate our background. You’ll notice that the background I created earlier was just a single colour. Since the colours live in palette memory, we can change the colour of the background just by changing the first colour in the background palette.
To make things simpler, I just added the code to change the background colour ot the DrawSprite function from above. There are certainly better / cleaner ways to do this, but for a quick and dirty example, I think the following will do.
Finally, I added a single line to the ClearSprite function:
You can do a lot of interesting things by modifying palettes directly, like having parts of sprites flash when hit, or having different enemies use the same sprite but use different colours (like the old Legend of Zelda games did with red / blue enemies). What I’ve done here is the simplest possible example of doing something like that, but it’s effective nonetheless.
If you’re still with me, the hard part is over, and all that’s left is to write out the main function for our program, and make sure all the necessary #defines are there for things to work together.
If you want to grab a fully put-together, runnable version of the code, you can find it here, I’m going to omit it here because all the code is already available on this page, and I think a github repo is a far better delivery mechanism for that much code than pasting it here.
This has the disadvantage of only showing one key press at a time (and prioritizing some keys over others), but I’m ok with that, I just wanted a fun example program to show off input handling, and to provide more examples of how to use stuff we’ve done in articles past. I suppose modifying the above to show all the buttons that are currently pressed instead of one is left as an exercise to the reader? ;)
That’s it for this week! I’m kind of excited that we’ve covered enough ground that I can throw up some code and refer to previous articles instead of having to explain every line, but if that ended up being unclear today make sure to let me know via reddit or twitter, or wherever so I can adjust future articles.
Finally, As much fun as pumping these articles out every week is, I’m going to slow down a bit and do one every two weeks , so that I have more time for some other hobby projects. We’ve covered enough ground now that there’s no reason to wait around for me to post more before starting to build the GBA game of your dreams though, so get to it!
And as always, if you want to say hello, or ask questions, or point out mistakes I’ve made, I’m most easily reached on Twitter.