How to handle Gameboy Joypad input
In this step of my How to make a Gameboy Game tutorial, we are going to give the player the ability to move a sprite around the screen. This tutorial will take a lot from the Drawing and Moving Sprites in Gameboy Games tutorial, so be sure you’ve taken a look at that one too.
Here’s what we are going to start with for this tutorial. We have our SimpleSprite.c from the previous tutorial on Drawing & Moving sprites, and this is our main.c:
#include
#include "SimpleSprite.h"
uint8_t spriteX,spriteY;
int8_t velocityX,velocityY;
uint8_t joypadCurrent=0,joypadPrevious=0;
void main(void)
{
DISPLAY_ON;
SHOW_SPRITES;
set_sprite_data(0,1,SimpleSprite);
set_sprite_tile(0,0);
move_sprite(0,84,88);
// Set our default position
spriteX=80;
spriteY=72;
// Set our default velocity to be moving down and to the right
velocityX=0;
velocityY=0;
// Loop forever
while(1) {
// Apply our velocity
spriteX+=velocityX;
spriteY+=velocityY;
// Position the first sprite at our spriteX and spriteY
// All sprites are render 8 pixels to the left of their x position and 16 pixels ABOVE their actual y position
// This means an object rendered at 0,0 will not be visible
// x+5 and y+12 will center the 8x8 tile at our x and y position
move_sprite(0,spriteX+4,spriteY+12);
// Done processing, yield CPU and wait for start of next frame
wait_vbl_done();
}
}
GBDK makes handling joypad input a really easy task. It’s “joypad” function does all the work, and returns an easily usable result. We can compare the result of this function with MACROS defined for user input. GBDK defines macros for each of the gameboys buttons:
- J_UP
- J_DOWN
- J_RIGHT
- J_LET
- J_A
- J_B
- J_START
- J_SELECT
To test if a button is down, we simply perform a bitwise and operation with the result of the joypad() function.
Here is an generic example:
// If the right button on the d-pad is held down
if(joypad() & J_RIGHT){
// Positive x velocity to move the sprite to the right
velocityX=1;
}
When the right direction on the d-pad is held down, the above if-statement will set the x velocity to be 1, effectively moving our sprite to the right. Let’s handle moving the sprite to the left also.
IMPORTANT!
You should never call the joypad() function more than once per frame. Since we need to test both directions (left and right), we’ll save the output of the joypad function in a variable named “joypadCurrent”. We’ll use that variable if our if/else statement instead of the function directly.
// We'll need to check the joypad input twice
// never call "joypad()" more than once per frame
// We'll save it's old value in our joypadPrevious variable
// We'll save it's new value and check that variable
joypadPrevious=joypadCurrent;
joypadCurrent = joypad();
// If the right button on the d-pad is held down
if(joypadCurrent & J_RIGHT){
// Positive x velocity to move the sprite to the right
velocityX=1;
// If the right button on the d-pad is held down
}else if(joypadCurrent & J_LEFT){
// negative x velocity to move the sprite to the left
velocityX=-1;
}
If we add this into our while loop, the sprite will move left and right. However, there is one thing we forgot. What if we are not holding left, AND we are not holding right? In this case, we want to stop the sprite from moving horizontally. All we need is a final else-statement where we set the velocityX variable to 0.
// We'll need to check the joypad input twice
// never call "joypad()" more than once per frame
// We'll save it's old value in our joypadPrevious variable
// We'll save it's new value and check that variable
joypadPrevious=joypadCurrent;
joypadCurrent = joypad();
// If the right button on the d-pad is held down
if(joypadCurrent & J_RIGHT){
// Positive x velocity to move the sprite to the right
velocityX=1;
// If the right button on the d-pad is held down
}else if(joypadCurrent & J_LEFT){
// negative x velocity to move the sprite to the left
velocityX=-1;
}else{
// Zero x velocity means no horizontal movement
velocityX=0;
}
That should do it for basic movement. Here is what your main.c file should look like:
#include
#include "SimpleSprite.h"
uint8_t spriteX,spriteY;
int8_t velocityX,velocityY;
uint8_t joypadCurrent=0,joypadPrevious=0;
void main(void)
{
DISPLAY_ON;
SHOW_SPRITES;
set_sprite_data(0,1,SimpleSprite);
set_sprite_tile(0,0);
move_sprite(0,84,88);
// Set our default position
spriteX=80;
spriteY=72;
// Set our default velocity to be moving down and to the right
velocityX=0;
velocityY=0;
// Loop forever
while(1) {
// We'll need to check the joypad input twice
// never call "joypad()" more than once per frame
// We'll save it's current value and check that variable
joypadPrevious=joypadCurrent;
joypadCurrent = joypad();
// If the right button on the d-pad is held down
if(joypadCurrent & J_RIGHT){
// Positive x velocity to move the sprite to the right
velocityX=1;
// If the right button on the d-pad is held down
}else if(joypadCurrent & J_LEFT){
// negative x velocity to move the sprite to the left
velocityX=-1;
}else{
// Zero x velocity means no horizontal movement
velocityX=0;
}
// Apply our velocity
spriteX+=velocityX;
spriteY+=velocityY;
// Position the first sprite at our spriteX and spriteY
// All sprites are render 8 pixels to the left of their x position and 16 pixels ABOVE their actual y position
// This means an object rendered at 0,0 will not be visible
// x+5 and y+12 will center the 8x8 tile at our x and y position
move_sprite(0,spriteX+4,spriteY+12);
// Done processing, yield CPU and wait for start of next frame
wait_vbl_done();
}
}
What about "tapping" buttons?
Often you only want to perform a specific action, when a button is “tapped”. That is, the first time is pressed, not just when it’s held down. This can be handled for all buttons using one extra variable.
You might of noticed previously, there was a variable created named “joypadPrevious”. Before we saved the “joypadCurrent” variable, we would update the “joypadPrevious” variable.
// We'll need to check the joypad input twice
// never call "joypad()" more than once per frame
// We'll save it's current value and check that variable
joypadPrevious=joypadCurrent;
joypadCurrent = joypad();
Now that we’ve done that, we can use it. We can check if a button was “tapped” similar to how we determine if a button is held down. Using a bitwise and operation. However, for “tapping” we want to negate the result and compare against the normal joypad() function.
Let’s use the A & B buttons to move the sprite downward and upward respectively.
// If the "A" button was just pressed
if(joypadCurrent & J_A) && !(joypadPrevious & J_A)){
// Move the sprite downward
spriteY+=8;
// If the "B" button was just presssed
} else if(joypadCurrent & J_B) && !(joypadPrevious & J_B)){
// Move the sprite up
spriteY-=8;
}
That’s it for basic input. Here is our final main.c file:
#include
#include "SimpleSprite.h"
uint8_t spriteX,spriteY;
int8_t velocityX,velocityY;
uint8_t joypadCurrent=0,joypadPrevious=0;
void main(void)
{
DISPLAY_ON;
SHOW_SPRITES;
set_sprite_data(0,1,SimpleSprite);
set_sprite_tile(0,0);
move_sprite(0,84,88);
// Set our default position
spriteX=80;
spriteY=72;
// Set our default velocity to be moving down and to the right
velocityX=0;
velocityY=0;
// Loop forever
while(1) {
// We'll need to check the joypad input twice
// never call "joypad()" more than once per frame
// We'll save it's current value and check that variable
joypadPrevious=joypadCurrent;
joypadCurrent = joypad();
// If the right button on the d-pad is held down
if(joypadCurrent & J_RIGHT){
// Positive x velocity to move the sprite to the right
velocityX=1;
// If the right button on the d-pad is held down
}else if(joypadCurrent & J_LEFT){
// negative x velocity to move the sprite to the left
velocityX=-1;
}else{
// Zero x velocity means no horizontal movement
velocityX=0;
}
// If the "A" button was just pressed
if((joypadCurrent & J_A) && !(joypadPrevious & J_A)){
// Move the sprite downward
spriteY+=8;
// If the "B" button was just presssed
} else if((joypadCurrent & J_B) && !(joypadPrevious & J_B)){
// Move the sprite up
spriteY-=8;
}
// Apply our velocity
spriteX+=velocityX;
spriteY+=velocityY;
// Position the first sprite at our spriteX and spriteY
// All sprites are render 8 pixels to the left of their x position and 16 pixels ABOVE their actual y position
// This means an object rendered at 0,0 will not be visible
// x+5 and y+12 will center the 8x8 tile at our x and y position
move_sprite(0,spriteX+4,spriteY+12);
// Done processing, yield CPU and wait for start of next frame
wait_vbl_done();
}
}
I hope that was clear and helps you in your Gameboy game development journeys. As always, if you found this helpful please do mention the Junkyard on social media. It helps a lot. If you have any questions, please don’t hesitate to reach out. Your feedback helps The Junkyard improve, so tutorials can be clear and helpful.
The source code for this tutorial, and all other tutorials, can be found on our Github page: https://github.com/LaroldsJubilantJunkyard