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 <gb/gb.h>
#include "SimpleSprite.h"

UINT8 spriteX,spriteY;
INT8 velocityX,velocityY;

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.

				
					// 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;
    
// If the right button on the d-pad is held down
}else if(joypad() & 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.

				
					// 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;
    
// If the right button on the d-pad is held down
}else if(joypad() & 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 <gb/gb.h>
#include "SimpleSprite.h"

UINT8 spriteX,spriteY;
INT8 velocityX,velocityY;

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) {
    
        // 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;
            
        // If the right button on the d-pad is held down
        }else if(joypad() & 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.

We will declare a “joypadPrevious” variable. This variable will be a 8-bit unsigned integer. We will use this variable to keep track of the previous state of the joypad. We should declare this variable above our main function.

				
					UINT8 joypadPrevious;
				
			

After we’ve declared the variable, only one thing needs to be done before we can use it. We need to set it’s value during each loop of the main function’ while loop. Specifically, before we call our “wait_vbl_done” function, we should set it’s value to equal that of the joypad() function.

				
					joypadPrevious=joypad();

// Done processing, yield CPU and wait for start of next frame
wait_vbl_done();
				
			

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((joypad() & J_A) && !(joypadPrevious & J_A)){

    // Move the sprite downward
    spriteY+=8;
    
// If the "B" button was just presssed
} else if((joypad() & 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 <gb/gb.h>
#include "SimpleSprite.h"

UINT8 joypadPrevious;
UINT8 spriteX,spriteY;
INT8 velocityX,velocityY;

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) {
    
        // 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;
            
        // If the right button on the d-pad is held down
        }else if(joypad() & 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((joypad() & J_A) && !(joypadPrevious & J_A)){
        
            // Move the sprite downward
            spriteY+=8;
            
        // If the "B" button was just presssed
        } else if((joypad() & 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);
        
        joypadPrevious=joypad();

		// 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