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 > moving sprite changes color....weeeeeird

#19434 - CyberSlag5k - Tue Apr 20, 2004 2:18 am

Ok, I have my sprites all up and working now. I can move my little soldier guy around the screen. The thing is, when any part of him goes higher than the top of the screen if he goes back down the entire sprite turns completely tan and if you do it again, he disappears (or turns black, not sure). If he goes up above the screen and keeps going up unitl he comes up through the bottom, everything is fine. Craziness. Here's code.

Code:

//gba test
//Mike Pateras

//main.cpp

#include"gba.h"
#include"keypad.h"
#include"palette.h"
#include"FF.h"
#include"soldier.h"
#include "dispcnt.h"
#include"sprite.h"

using namespace std;

typedef struct
{
   int posX, posY;
   u16 spriteFrame[4];
   int activeFrame;
   u16 OAMSpriteNum;
}Sprite;

Sprite soldier;

u16* videoBuffeer = ((u16*)0x6000000);
u16* paletteMem = ((u16*)0x5000000);
u16* OAM = ((u16*)0x7000000);

OAMEntry sprites[128];
pRotData rotData = (pRotData)sprites;

void copyOAM()
{
   u16* temp;
   temp = (u16*)sprites;
   for(u16 i = 0; i < 512; i++)
      OAM[i] = temp[i];
}

void initializeSprites()
{
   for(u16 i = 0; i < 128; i++)
   {
      sprites[i].attribute0 = 160;
      sprites[i].attribute1 = 240;
   }
}

void VSync()
{
   while((volatile u16)REG_VCOUNT != 160)
   {
   }
}

void getInput()
{
   if(!(*KEYS & KEY_LEFT))
   {
      soldier.posX -= 1;
         if(soldier.posX < 0)
            soldier.posX += 512;
   }
   if(!(*KEYS & KEY_DOWN))
   {
      soldier.posY += 1;

   }
   if(!(*KEYS & KEY_RIGHT))
   {
      soldier.posX += 1;
   }
   if(!(*KEYS & KEY_UP))
   {
      soldier.posY -= 1;
         if(soldier.posY < 0)
            soldier.posY += 256;
   }
}

void updateSprites(OAMEntry *spritePointer)
{
   spritePointer->attribute0 = COLOR_256 | SQUARE | soldier.posY;
   spritePointer->attribute1 = SIZE_64 | soldier.posX;   

}

int main()
{
   soldier.posX = 20;
   soldier.posY = 40;

   setMode(MODE_1 | OBJ_ENABLE | OBJ_MAP_1D);

   for(int i = 0; i < 256; i++)
   {
      OBJPaletteMem[i] = palette[i];
   }

   initializeSprites();

   sprites[0].attribute0 = COLOR_256 | SQUARE | soldier.posY;
   sprites[0].attribute1 = SIZE_64 | soldier.posX;
   sprites[0].attribute2 = 0;

   for(int i = 0; i < 2048; i++)
   {
      OAMData[i] = obj0[i];
   }
   for(int i = 0; i < 2048; i++)
   {
      OAMData[i + 2048] = obj1[i];
   }
   for(int i = 0; i < 2048; i++)
   {
      OAMData[i + 4096] = obj2[i];
   }
   for(int i = 0; i < 2048; i++)
   {
      OAMData[i + 6144] = obj3[i];
   }

   soldier.OAMSpriteNum = 0;
   soldier.activeFrame = 0;
   soldier.spriteFrame[0] = 0;
   soldier.spriteFrame[1] = 128;
   soldier.spriteFrame[2] = 256;
   soldier.spriteFrame[3] = 384;

   



   while(1)
   {
      getInput();
      updateSprites(&sprites[0]);
      VSync();
      copyOAM();
   }




   return 0;
}


I can't really find anything wrong...maybe you guys can.

Also, if I'm going to enable mode 4, do I have to begin loading my sprite data starting at OAMData[512]? I'm still a little unclear on that. I've read and reread 3 different tutorials and am fairly sure that's what it means. Please correct me, though, if I'm wrong.

Thank you.

#19437 - CyberSlag5k - Tue Apr 20, 2004 2:29 am

I've found my updateSprites function to be a little off. I've changed it to:

Code:

void updateSprites(OAMEntry *spritePointer)
{
   spritePointer->attribute0 = spritePointer->attribute0 & 0xFE00;
   spritePointer->attribute0 = spritePointer->attribute0 | soldier.posY;

   spritePointer->attribute1 = spritePointer->attribute1 & 0xFE00;
   spritePointer->attribute1 = spritePointer->attribute1 | soldier.posX;   


Still didn't take care of the problem though. In fact, it made it a little worse. Now if I come up from the bottom the sprite grows to twice it's size and disappears after a little bit. Very odd.

#19440 - CyberSlag5k - Tue Apr 20, 2004 2:53 am

Here's a link if you'd like to download the .bin to see what's happening first hand. Probly the best way to descirbe it.

http://homepages.udayton.edu/~pateramj/main.bin

#19441 - sajiimori - Tue Apr 20, 2004 3:15 am

Sprites have 9 bits for the x attribute field and 8 bits for y. When you leave those boundaries (such as going off the top or left of the screen), it overflows into other fields and messes up your sprite. Before you OR the position into the attribute, clear the high bits by ANDing the x with 0x1FF and the y with 0xFF.

#19442 - CyberSlag5k - Tue Apr 20, 2004 3:30 am

Code:

   spritePointer->attribute0 = spritePointer->attribute0 & 0xFF;
   spritePointer->attribute0 = spritePointer->attribute0 | soldier.posY;

   spritePointer->attribute1 = spritePointer->attribute1 & 0x1FF;
   spritePointer->attribute1 = spritePointer->attribute1 | soldier.posX;   



Is that what you meant? I think I have mixed something up because my looks like a little grey striped box and moves in very odd ways.

#19446 - yaustar - Tue Apr 20, 2004 4:20 am

Code:
void getInput()
{
   if(!(*KEYS & KEY_LEFT))
   {
      soldier.posX -= 1;
         if(soldier.posX < 0)
            soldier.posX += 512;
   }
   if(!(*KEYS & KEY_DOWN))
   {
      soldier.posY += 1;

   }
   if(!(*KEYS & KEY_RIGHT))
   {
      soldier.posX += 1;
   }
   if(!(*KEYS & KEY_UP))
   {
      soldier.posY -= 1;
         if(soldier.posY < 0)
            soldier.posY += 256;
   }
}

you are doing a check if the value falls below 0 which is good. But what about the upper limit? ;)
_________________
[Blog] [Portfolio]

#19447 - CyberSlag5k - Tue Apr 20, 2004 4:47 am

Ahh, good call. I guess I followed my tutorial a little blindly there. Lesson learned.

#19455 - Cearn - Tue Apr 20, 2004 10:07 am

What sajimori was talking about was clearing the high bits of the x and y values, not of the attributes. This should work:
Code:

   // clear y-field in attr0
   spritePointer->attribute0 &= ~0x00ff;
   // clear high-bits of soldier.Y and put it in attr0
   spritePointer->attribute0 |= (soldier.posY & 0x00ff);

   // ditto for x (only with 9 bits)
   spritePointer->attribute1 &= ~0x01ff;
   spritePointer->attribute1 |= (soldier.posX & 0x01ff); 

Can I suggest putting this in a function that does not rely on there being a soldier in the game? Something like void SetObjPos(OAMEntry *obj, int x, int y) or something.
Btw, clearing the high bits of posX and posY automatically wraps around at the boundaries due to the 2s complement nature of signed integers, so you won't need those extra ifs in getInput() anymore. You could just use
Code:

void getInput()
{
   if(!(*KEYS & KEY_LEFT))
      soldier.posX -= 1;
   else if(!(*KEYS & KEY_RIGHT))
      soldier.posX += 1;
   if(!(*KEYS & KEY_DOWN))
      soldier.posY += 1;
   else if(!(*KEYS & KEY_UP))
      soldier.posY -= 1;

  soldier.posX &= 0x01ff;
  soldier.posY &= 0x00ff;
}

Or you could let updateSprites take care of the wrapping. Note that I've also used "else if" for the left-right (and up-down) distinction. You shouldn't be able to do both at the same time, right? Also, if the changes are going to remain at 1, you may want to use the ++ and -- operators.

#19468 - CyberSlag5k - Tue Apr 20, 2004 4:51 pm

Thanks Cearn and Sajiimori. That took care of the problem. One last thing though. The way it runs now I can move the sprite above the top boundary of the viewer and it'll continue as normal until it comes up from the bottom again. However, if I move it to the left boundary, it'll instantly zap to the other side of the screen instead of going partially offscreen.

Here's a link because that's kind of hard to explain:

http://homepages.udayton.edu/~pateramj/main.bin

#19474 - Miked0801 - Tue Apr 20, 2004 6:23 pm

You are mapping your position to an 8-bit number. That means left/right it will wrap almost immediately (as the screen is 240 wide.) Top/bottom is only 160 so it travels further.