gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > Simple side-scrolling shooter bug

#95515 - Rogmatic - Sun Jul 30, 2006 12:42 am

I'm trying to code a very simple side-scrolling shooter, so far all I've coded is the player's 'ship' sprite, and the ability to move that sprite using the arrow keys.

However, I've come across a very frustrating bug while coding the player's shots.

It's set to fire a shot only if a) there are less than 10 shots on the screen already and b) if it's been 10 iterations of the main loop since the last shot.

I've set it to turn off the shot sprite if it reaches the far side of the screen or if there are 10 shots 'in play', however, whenever a shot is 'turned off', all other shots on the screen stop moving.

I've spent the last 3 or 4 hours trying to fix this, hopefully you guys will see something I'm not.

Here's the code:

Code:

#include "gba.h"
#include "player.h"
#include "playershot.h"

OAMEntry OAMCopy[128];
int PlayerShotOffset;
int PlayerShots;
int Delay;
int NextShot;

void PlayerShoot();

typedef struct
{
   int x,y;
   OAMEntry* oam;
   int gfxID;
}Sprite;

Sprite player;
Sprite playershot[9];

void WaitForVblank(void)
{
   while(! (REG_DISPSTAT & DISPSTAT_VB));
   while(REG_DISPSTAT & DISPSTAT_VB);
}

void InitOAM(void)
{
   int i;
   
   for(i = 0; i < 128; i++)
      OAMCopy[i].attribute[0] = SPRITE_OFF;
}

void UpdateOAM(void)
{
   int i;
   
   for(i = 0; i < 128 * sizeof(OAMEntry) / 4; i++)
      ((u32*)OAMMem)[i] = ((u32*)OAMCopy)[i];
}

void GetInput(void)
{
   if(!(REG_KEYS & KEY_UP))
      if(player.y > 2)
         player.y = player.y - 3;
   
   if(!(REG_KEYS & KEY_DOWN))
      if(player.y < (158 - 16))
         player.y = player.y + 3;
   
   if(!(REG_KEYS & KEY_LEFT))
      if(player.x > 2)
         player.x = player.x - 3;
   
   if(!(REG_KEYS & KEY_RIGHT))
      if(player.x < (238 - 16))
         player.x = player.x + 3;
   
   if(!(REG_KEYS & KEY_A))
      if((PlayerShots <= 10) && (Delay <= 0))
         PlayerShoot();
}

void PlayerShoot(void)
{
   if(PlayerShots >= 10) {
      playershot[PlayerShots - 1].oam->attribute[0] |= SPRITE_OFF;
      PlayerShots--;
      NextShot = 0;
   }
   PlayerShots++;
   playershot[NextShot].x = player.x + 20;
   playershot[NextShot].y = player.y + 4;
   playershot[NextShot].oam->attribute[0] ^= SPRITE_OFF;
   NextShot++;
   Delay = 10;
}

void MoveShots(void)
{
   int i;
   
   for(i = 0; i <= PlayerShots; i++)
   {
      if(playershot[i].x >= 240)
      {
         PlayerShots--;
         playershot[i].oam->attribute[0] |= SPRITE_OFF;
         NextShot = 0;
      }
      playershot[i].x = playershot[i].x + 4;
   }
}

void MoveSprite(Sprite* sp)
{
   sp->oam->attribute[1] &= 0xFE00;
   sp->oam->attribute[1] |= (sp->x & 0x01FF);
   
   sp->oam->attribute[0] &= 0xFF00;
   sp->oam->attribute[0] |= (sp->y & 0x00FF);
}

int main(void)
{
   int i;
   Delay = 0;
   
   SetMode(MODE_0 | OBJ_ENABLE | OBJ_MAP_1D);
   
   InitOAM();
   
   player.oam = &OAMCopy[11];
   player.x = 10;
   player.y = 10;
   player.gfxID = 11;
   
   player.oam->attribute[0] = COLOR_256 | SQUARE;
   player.oam->attribute[1] = SIZE_16;
   player.oam->attribute[2] = player.gfxID;
   
   for(i = 0; i < 9; i++)
   {
      playershot[i].oam = &OAMCopy[i];
      playershot[i].x = 0;
      playershot[i].y = 0;
      playershot[i].gfxID = 0;
      
      playershot[i].oam->attribute[0] = COLOR_256 | SQUARE | SPRITE_OFF;
      playershot[i].oam->attribute[1] = SIZE_8;
      playershot[i].oam->attribute[2] = playershot[i].gfxID;
   }
   
   PlayerShots = 0;
   NextShot = 0;
   PlayerShotOffset = playershot[0].gfxID * 8 * 4 / 2;
   
   
   for(i = 0; i < 16 * 16 / 2; i++)
   {
      OBJ_GFXMem[player.gfxID * 16 + i] = playerData[i];
      OBJ_GFXMem[PlayerShotOffset + i] = playershotData[i];
   }
   
   for(i = 0; i < 256; i++)
      OBJPaletteMem[i] = playerPalette[i];
   
   while(1)
   {
      GetInput();
      MoveShots();
      MoveSprite(&player);
      for(i = 0; i <= PlayerShots; i++)
         MoveSprite(&playershot[i]);
      WaitForVblank();
      UpdateOAM();
      Delay--;
   }
}


Note: I'm using the include files that come with the PERN project source code.

Thanks in advance.

#95522 - sgeos - Sun Jul 30, 2006 1:49 am

Your code can be better factored. In the process you might figure out what the error is. I'd implement the following functions:
Code:
void init(int pX, int pY, int pGfxId);
void FreeShot(int pShotId);


Also, you declare:
Sprite playershot[9];

And check:
if(PlayerShots >= 10) {

That looked like a bug to me. Don't use magic numbers, use macros instead. Try:
Code:
#define SHOT_MAX 10
Sprite playershot[SHOT_MAX];
if (SHOT_MAX < PlayerShots) {

Why 256? Why 158 - 16? What are these numbers? I might be able to figure it out, but if you a senior *nix system admin that spots bugs %1000 more effectively than I do, he's certainly not going to know.

Some believe that the only literal constants that should be in your code are 0, 1 and -1.

-Brendan

#95574 - Rogmatic - Sun Jul 30, 2006 10:05 am

Alright, thanks for the input, I'll make those changes ASAP, and get back to you.

#95586 - Cearn - Sun Jul 30, 2006 11:50 am

I haven't tested it, but this might be the problem.

You have N shots, which you use up from 0 to N-1. Say you fire a 3 shots, on screen the order will be 2, 1, 0, because earlier shots will have travelled a bit already. Now shot 0 goes out of range; it's turned off, PlayerShots (you might consider consistent naming too playershot vs PlayerShot might become confusing) is decreased, but because the shot itself is still moving! And because it's a low index shot, the next frame it'll still be out of range, PlayerShots again decreases until after 10 frames it reaches 0 and all the other shots stop being considered.

#95627 - Ultima2876 - Sun Jul 30, 2006 4:38 pm

Only thing is, I don't like having to search through header files for defines for each and every number in the code, especially insignificant things.

#95993 - Rogmatic - Mon Jul 31, 2006 8:05 pm

Thanks for all your advice everyone, haven't had much time to make changes lately, but I'll let you guys know when I do.

EDIT: Thanks alot Cearn, that seems to have been the problem. I was getting to something like that, just needed a little push, I guess. Thanks again everyone.

#96167 - sgeos - Tue Aug 01, 2006 7:16 pm

Ultima2876 wrote:
I don't like having to search through header files for defines

The point is that the exact number generally doesn't matter. If and when it really does, you can always do this instead of touching the header files:
Code:
printf("%d\n", MY_CONSTANT);

I'd rather see defines at the top of the C file, or local variables, than nothing at all.

-Brendan