Larold’s Jubilant Junkyard” has become “Larold’s Retro Gameyard“. I’ve been working on this re-branding and migration for a while. All old tutorials should redirect you to this new site. If you see any errors, please let me know! See here for more information: What happened to Larold’s Jubilant Junkyard?

common errors in gbdk-2020 projects

Troubleshooting common GBDK-2020 Errors

Game Development can be very difficult at times. Writing retro Game Boy games can have it’s own difficulties. There’s a lot of internal mechanics you need to have an understanding of. In this tutorial, I’ll troubleshoot some of the basic problem’s you’ll run into with the GBDK-2020 suite.

In addition to this tutorial, i have a tutorial on Debugging your GBDK-2020 game. That tutorial wont discuss specific issues, but it will go over three tools to help you overcome them. With that said, I’ll start with the problem that happens the most for me.

Number #1 – Understand Type Limitations

A lot of confusion can occur when you go outside of a variable’s range. Unlike with modern engines/languages, C nor GBDK will prevent you from doing this.

The following is perfectly acceptable to the compiler:

uint8_t myUnsignedVariable = 300;

Here are the ranges for 8-bit and 16-bit integers:

TypeGBDK TypeMinimumMaximum
Signed 8-Bit Integerint8_t-128127
Unsigned 8-Bit Integeruint8_t0255
Signed 16-Bit Integerint16_t-32,76832,768
Unsigned 16-Bit Integeruint16_t065,535

If you try to give a variable a value outside of it’s proper range, the Game Boy will wrap that value. Consider the following code

// the player's x position starts at 50
uint8_t myPlayerX=50;


void UpdateGame(void){

  // if the A button was JUST pressed
  if((joypadCurrent & J_A) && !(joypadPrevious J_A)){
    myPlayerX+=50;
  }
  
  
  DrawPlayer(myPlayerX);
}

Here’s how that value will change over time:

Execution NumberExpected ValueActual Value
15050
2100100
3150150
4200200
5250250
630044
735094

If the “myPlayerX” variable is a unsigned 8-bit integer, On the 6th button press the player will appear to loop around (near) back to start. This is because both 300 and 350 are larger than the maximum value of an 8-bit unsigned integer: 255.

If you have weird issues in general, make sure your variable types & math suite the ranges you expect of them. If you need to use negative values, use signed integers. If you don’t, use unsigned integers. The next issue, for “buffer overflow”, is somewhat related

Buffer Overflow with Arrays

Like with single variable values, c nor GBDK will prevent you from writing outside of the bounds of an array. For example, It will let you try to write to the 10th item, of a 5 element array.

uint8_t items[5];
uint8_t numberOfItems=10;

void UpdateItem(void){

  items[numberOfItems]=3;
}

The result is unexpected behavior. The Emulator/Game Boy may even start writing to other bytes in RAM. This can be especially problematic when using loops.

uint8_t items[5];

void UpdateAllItem(void){

  for(uint8_t i=0;i<10;i++){
      items[i]=3;
  }  
}

The best thing to do, make constants for your array sizes and always check which index you are writing to.

#define INVENTORY_MAX_SIZE 5

uint8_t items[INVENTORY_MAX_SIZE ];
uint8_t numberOfItems=10;

void UpdateItem(void){
  if(numberOfItems<INVENTORY_MAX_SIZE){
    items[numberOfItems]=3;
  }
}
void UpdateAllItem(void){

  for(uint8_t i=0;i<INVENTORY_MAX_SIZE ;i++){
      items[i]=3;
  }  
}

The Background/Window

I can’t see the background/window

  • Make sure you’ve enabled the background/window with “SHOW_BKG” or “SHOW_WIN” respectively.
  • If you’ve enabled the window, the window may be covering the background. if you don’t need it, hide the window, if you do, move it downwards (or hide via interrupts)
  • Make sure you actually have tile data for the background/window in VRAM.
  • Make sure the display is on
  • Make sure your color palettes don’t have all the same color

My full level doesn’t show when scrolling

if you’re dealing with a large map (larger than 256x256px), a little more logic is needed for map scrolling. The buffer for the tilemap and window is 32×32. Those 32 rows and columns will repeat as you scroll.

For tilemaps larger than 32×32 tiles in size, As the camera moves you’ll need to draw new columns/rows in the direction you are moving. See my tutorial on drawing Large Maps: Drawing Large Maps in GBDK. Or, see the GBDK Large map Example.

Sprites

I cant see my sprite(s)

  • Make sure sprites are enabled with “SHOW_SPRITES
  • Make sure they are not at 0,0. Each sprite is internally drawn with a -8,-16 offset. Sprites drawn at 0,0 will not be visible.
  • Make sure they have valid tile data in VRAM
  • Make sure they have valid tile indices specifed.

My sprites are in the wrong position

All sprites have a -8,-16 offset when drawn (Pandocs for more info). This means when drawing sprites, always add 8 to the x position and 16 to the y position.

DrwPlayer(playerX+8,playerY+16);

If you are scrolling the map, you’ll need to subtract the metaphorical camera’s true position.

// This positiions the camera on the player, except when they reach the edges of the map
cameraX = MIN(levelWidthInPixels-160,MAX(0,playerX-80));
cameraY = MIN(levelHeightInPixels-144,MAX(0,playerY-72));

// Subtract our camera position to make the object (visually) move with the camera
DrwPlayer(enemyX-cameraX+8,enemyY-cameraY+16);

My sprites have the wrong colors.

On Game Boy Color you can have 8 different color palettes for sprites. On your original Game Boy models you only have 2 different models.

For GBC you can switch the palette using the 3 least-significant bits of the sprite props attribute. For DMG, you can change the palette using the 5th least-significant bit. See the Pandocs for more information: Pan Docs – OAM Byte 3

My Sprites are Glitched

  • When adding your sprite tiles into VRAM, Make sure you aren’t writing to VRAM from the wrong bank.
  • Make sure you are writing valid tile data.
  • If you’re using metasprites and using banking, you need to be in the same bank when you add the tile data to VRAM and when you draw the metasprite.

Here’s an example of drawing metasprites that are banked. You can create a common function for drawing banked metasprites.

// Important note: This function is NONBANKED
uint8_t move_metasprite_banked(const metasprite_t **metasprites, uint8_t index, uint8_t base_tile,uint8_t sprite,uint8_t x, uint8_t y, uint8_t bank) NONBANKED{

    uint8_t previous_bank = CURRENT_BANK;

    SWITCH_ROM(bank);

    uint8_t count = move_metasprite(metasprites[index],base_tile,sprite,x,y);

    SWITCH_ROM(previous_bank);

    return count;
}

With that created, you can use that function

// This can be called from any bank
sprites+=move_metasprite_banked(MyBank3Metasprite,0,0,sprites,x,y,3);
sprites+=move_metasprite_banked(MyBank2metasprite,0,0,sprites,x2,y2,2);

Random sprites stay on the screen

Every update of your game may use a different amount of sprites. if you stop using sprite, but don’t move it offscreen, the Game Boy will continue to render it where it was previously.

When you are done drawing all of your sprites, Use the “hide_sprites_range” function to hide any sprites that were drawn before but no longer needed.

Metasprite not fully drawing

The Game Boy can only draw 10 sprites per scan line. After the 10th sprite, other sprites on that scanline will not be drawn. Additionally, The Game Boy can only draw 40 sprites maximum. If your object is really large, you might run out of sprites.

If you are running out of sprites (per line or overall), there is no coding solution. When designing your game, being aware of how large your sprites are, and what other possible sprites will be on screen with them at the same time.

GBDK-2020 Compilation Errors

Multiple definition of “_myVariableName” when compiling

This means you’ve defined a variable more than once. This usually happens when you define a variable in a header file, or include a .c file. neither of these should EVER be done in header files.

Defining a variable in a header file:

// Inside SomeHeaderFile.h
uint8_t myVariableName;

Including a .c file:

// Inside SomeCFile.c or SomeHeaderFile.c
#include <SomeOtherCFile.c>

In header files, make all references to your variables external:

// Inside SomeHeaderFile.h
extern uint8_t myVariableName;

If you need to access the variables of another .c file, declare them in a header file:

// Inside MyHeaderFile.h

extern uint8_t myVariableName;

void MySpecialFunction(void);

For more information, You should read up on definition vs declaration here: Geeks for Geeks – Difference between Definition and Declaration

ROM is too large for number of banks specified

This one means you’re trying to put more content in the ROM than you configured. The solution is to increase the number of ROM banks when compiling your ROM file. You can do this using the ‘ -Wm-yo<N>’ argument. Where <N> is a power of 2, or A for autobanking.

This also requires a memory bank controller, or MBC for short. See the GBDK-2020 docs for more information: GBDK 2020 Docs – Setting the MBC and number of ROM & RAM banks available.

Warning: Multiple write of XXXXXXX bytes at 0xYYYYYYY

This means you have written too much data into a single bank. This includes constants and functions. Each ROM bank is 16kb. To troubleshoot this issue, use the romusage command line tool. This tool is included with GBDK-2020 as of the 4.3.0 release. If you are not up to date, you can download the executable file here: https://github.com/bbbbbr/romusage. When run, it will show you all the relevant ROM/RAM bank data:

Leave a Reply

Your email address will not be published. Required fields are marked *

THANKS FOR READING!

If you have any suggestions, and/or are confused about anything: feel free to leave a comment, send me a email, or a message on social media. Constructive criticism will help the gameyard, and others like yourself. If you learned something from this tutorial, and are looking for more content, check other these other tutorials. 

Troubleshooting common GBDK-2020 Errors

Thanks for reading my tutorial, here are the files for that tutorial. 

Download Instructions

Sign-Up for the "Gameyard Newsletter" for MOre Game Development News

If you like Retro Game Development, subscribe to the Gameyard Newsletter. Stay up-to-date with all the latest Game Boy news. It’s free, and you can unsubscribe any time!

Be sure to check your email inbox for a confirmation email.