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.

DS development > Multiple Sprites Issue

#140170 - jonezer4 - Fri Sep 14, 2007 1:42 am

I've been developing a card game using Patatersoft's manual as a guide, but I've run into a few problems when moving on to multiple sprites as opposed to the single one in his tutorial. I have something that's close to working, but I'm certain I've made several mistakes in it, which is resulting in the fact that only my first 6 sprites work, anything after that goes to hell.

Some background. I'm using 8x8 tiled sprites, in a 1D fashion (I have 24 sprites all lined up in a straight downward line, one on top of the other) and they are each 64x64 in size. When I set up video I'm specifying sprites as "DISPLAY_SPR_1D"/

Code:
short int spriteGfxID = 160; //Don't understand why it's this. It's 64 in patater's manual, but that gave me strange results

   for (int n = 0; n < 6; n++) //HACK 6 the most that works currently
   {
   
   spriteEntry[n].attribute[0] = ATTR0_COLOR_256
                           |ATTR0_ROTSCALE_DOUBLE;// | //able to rotscale
                           //in tater's manual he specifies the y position here, but that gave me strange results when rotating
                           
   spriteEntry[n].attribute[1] = ATTR1_ROTDATA(0) |
                           ATTR1_SIZE_64| //size 64x64
         
                           n*64*8; //This was just the x position in pater's manual, but that gave me weird results when rotating. Again, this appears to work, but I don't really know why.
                           
   spriteEntry[n].attribute[2] = spriteGfxID * (n); //I thought this should be spriteGfID squared times n, but that doesn't work
   }


When I DMAcopy in the image data, I use this, spriteNum being which sprite image I want to copy in (0 for the top sprite, 1 for the next one down, etc.)

Code:
   //copy the sprite graphics in obj graphics mem
   dmaCopy(SPRITE_BIN+(spriteNum*64*64),   
   &SPRITE_GFX[spriteGfxID * 16 * spriteNum], //to address
            64*64);   //size


Note, spriteGfID is always 160, but I am putting different multiples of that into attribute[2];


So like I said, there's several things that I don't particularly understand about what's going on in there, despite having read tatersoft's manual and several posts on here, but it works, for the most part. The problem is, it only works for the first 6 sprites and then everything goes to hell, which leads me to believe I'm doing something horribly wrong. I'd like to have a better understanding of all this stuff too, instead of just hacking until it halfway works. Any help or guidance would be greatly appreciated. Thanks!

#140177 - knight0fdragon - Fri Sep 14, 2007 2:28 am

ok this part

spriteEntry[n].attribute[1] = ATTR1_ROTDATA(0) |
ATTR1_SIZE_64| //size 64x64

n*64*8

64*8 = 512 which means that your sprite for some reason goes 512 * n, that has to be a mistake, since the max that x can be is 511, and since you are not logically ANDing it with 511, the numbers will corrupt all other data withing the attribute


as for this spriteEntry[n].attribute[2] = spriteGfxID * (n); //I thought this should be spriteGfID squared times n, but that doesn't work


right now you have it set at 160 * n, which is also a mistake
now I do not know how you set up the sprite block counter in the display register, but I am going to assume it is 32, which means that each index = 32 bytes. So if you were doing 16 color sprites, this would equal 1 8x8 tile, since it is 8(width)*8(height)*[1/2](bytes per pixel)

what you are doing is 256 color, so it would be 2 indexes for every 8x8 block, and since a 64x64 sprite has 64 blocks in it, you would need a total of 128. Now with this, each sprite would consume 128 indexs, and the max indexing you are allowed is 1023, thus giving you only 8 sprites.

What you need to do is change the size of bytes an index takes, so you want something like DISPLAY_SPR_1D_SIZE_256, or something to that extent. This would knock your index count to 16, so you would set your spriteGfxID to 16, which will give you 64 sprites
_________________
http://www.myspace.com/knight0fdragonds

MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206


Last edited by knight0fdragon on Fri Sep 14, 2007 2:40 am; edited 1 time in total

#140179 - jonezer4 - Fri Sep 14, 2007 2:37 am

knight0fdragon wrote:
ok this part

spriteEntry[n].attribute[1] = ATTR1_ROTDATA(0) |
ATTR1_SIZE_64| //size 64x64

n*64*8

64*8 = 512 which means that your sprite for some reason goes 512 * n, that has to be a mistake, since the max that x can be is 511, and since you are not logically ANDing it with 511, the numbers will corrupt all other data withing the attribute


Okay. Is that the only mistake you see? Also, what exactly is that data, so I know why it's capped at 511 in this instance?

Thanks again!

#140182 - knight0fdragon - Fri Sep 14, 2007 2:42 am

Check my post again, I editted it. For a description of all the attributes in OAM, look for OAM on gbatek

basically the X coordinate of the oam can go from 0 to 511, and y can go from 0 to 255
_________________
http://www.myspace.com/knight0fdragonds

MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206

#140188 - jonezer4 - Fri Sep 14, 2007 3:11 am

knight0fdragon wrote:

right now you have it set at 160 * n, which is also a mistake
now I do not know how you set up the sprite block counter in the display register, but I am going to assume it is 32, which means that each index = 32 bytes.


Where is that and how do I set it to 32?

Thanks so much for your in depth help. I always forget to research GBA stuff, so I'm heading over to GBA Tek now to read up.

#140191 - jonezer4 - Fri Sep 14, 2007 4:06 am

Okay, this is what I have now. The sprites are pretty ugly right now, so I'm sure I have more stuff messed up

Code:

videoSetMode(MODE_5_2D   | DISPLAY_BG3_ACTIVE
                     | DISPLAY_SPR_ACTIVE   
                     | DISPLAY_SPR_1D_SIZE_256
                     );      

...

short int spriteGfxID = 16;


void initSpritesMain(SpriteEntry* spriteEntry, SpriteRotation* spriteRotation)
{
   //init OAM
   initOAM(spriteEntry, spriteRotation, MAIN_SCREEN);

   //TODO: MAKE A SAFEGUARD FOR THIS! NUMOFSPRITESMAIN VAR
   for (int n = 0; n < CARDS_PER_PLAYER; n++)
   {
   spriteEntry[n].attribute[0] = ATTR0_COLOR_256
                           |ATTR0_ROTSCALE               | 0;         //Setting Y to zero for now
                           
   spriteEntry[n].attribute[1] = ATTR1_ROTDATA(0) |
                           ATTR1_SIZE_64 //size 64x64
                           | 0;      //Setting X to zero for now
            
   spriteEntry[n].attribute[2] = spriteGfxID * n;

   setSpritePriority(&spriteEntry[n],1);//setting all sprites to lowest priority

   
   //update the OAM
   updateOAM(spriteEntry,MAIN_SCREEN);


}

...
//Copying image data in
//Should this change?

dmaCopy(sprites_bin+(64*64*spriteNum),
   &SPRITE_GFX[spriteGfxID * spriteNum],       
   64*64);

#140192 - knight0fdragon - Fri Sep 14, 2007 4:11 am

dmaCopy(sprites_bin+(64*64*spriteNum),
&SPRITE_GFX[64*64/2* spriteNum], //divide by 2 becuase of 16bit addressing
64*64);
_________________
http://www.myspace.com/knight0fdragonds

MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206

#140195 - jonezer4 - Fri Sep 14, 2007 4:31 am

knight0fdragon wrote:
dmaCopy(sprites_bin+(64*64*spriteNum),
&SPRITE_GFX[64*64/2* spriteNum], //divide by 2 becuase of 16bit addressing
64*64);


On the plus side, all the sprites appear to be getting image data, but they're all composed of several different images. The top 10 percent of each sprite is one image, while the next 40% of the sprite is another image, and so on.

#140208 - jonezer4 - Fri Sep 14, 2007 6:41 am

Fixed. I was missing one critical line of code

I just had...
Code:
   //Use main Screen (bottom) for image
   videoSetMode(MODE_5_2D   | DISPLAY_BG3_ACTIVE
                     | DISPLAY_SPR_ACTIVE   //sprites!
                        |DISPLAY_SPR_1D_SIZE_256
                     );   


And didn't realize, even with DISPLAY_SPR_1D_SIZE_256 there, I still had to feed it"DISPLAY_SPR_1D" also.


Thanks again Knight for all your help.

#140302 - jonezer4 - Sat Sep 15, 2007 5:29 am

Alright, one problem down, on to the next!

This one's not so bad. I can now make 32 rot_scale_double sprites, which makes sense, but I can only display approximately 11 at a time. Once I go over that, bits and pieces of the sprite aren't displayed (you see right through them to the background).

Also, it runs fine on my emulator. The problem is just on the actual DS.

Is this just a legitimate limitation? If so, why exactly is that (just for future reference)?

Thanks.

#140305 - knight0fdragon - Sat Sep 15, 2007 5:44 am

I would need to see code, because you can have up to 32 rotating sprites
_________________
http://www.myspace.com/knight0fdragonds

MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206

#140310 - jonezer4 - Sat Sep 15, 2007 6:10 am

Here's the pertinent code. I'm moving the sprites after this so they're not all on top of each other, and DMA copying data into them using your previously described method. Like I said, it all works on the emulator, so I'm not too sure what the issue is with the DS hardware not displaying all the sprites.

Code:
void initSpritesSub(SpriteEntry* spriteEntry, SpriteRotation* spriteRotation)
{
   //init OAM
   initOAM(spriteEntry, spriteRotation, SUB_SCREEN);
   
   //TODO: MAKE A SAFEGUARD FOR THIS (TOTALSPRITES OR SOMETHING)
   for (int n = 0; n < 32; n++)      //32 is the most rotscale_double sprites we can have
   {
   
   spriteEntry[n].attribute[0] = ATTR0_COLOR_256
                           |ATTR0_ROTSCALE_DOUBLE
                           | 0;// | //able to rotscale
   
                           
   spriteEntry[n].attribute[1] = ATTR1_ROTDATA(n) |
                           ATTR1_SIZE_64| //size 64x64
                           0;
                           
   spriteEntry[n].attribute[2] = spriteGfxID * n;

   setSpritePriority(&spriteEntry[n],1);            //setting all sprites to lowest priority
   
   }
   //copy in the sprite palettes
   dmaCopy(cardPalette, //pointer to palette address
   (uint16 *)SPRITE_PALETTE_SUB, //to address
   cardsPal_bin_size); //size of data to copy
   
   //update the OAM
   updateOAM(spriteEntry,SUB_SCREEN);   
}


If there's anything else aside from that let me know. Thanks!

#140312 - knight0fdragon - Sat Sep 15, 2007 6:25 am

um, you never set up the rotating information for the sprites
you need something like

pSpriteRotation spriteRotations = (pSpriteRotation)sprites;

spriteRotations[N].hdx = 1<<8;
spriteRotations[N].hdy = 0<<8;
spriteRotations[N].vdx = 0<<8;
spriteRotations[N].vdy = 1<<8;
_________________
http://www.myspace.com/knight0fdragonds

MK DS FC: Dragon 330772 075464
AC WW FC: Anthony SamsClub 1933-3433-9458
MPFH: Dragon 0215 4231 1206

#140313 - jonezer4 - Sat Sep 15, 2007 6:28 am

Ah, sorry, I had that in initOAM
Code:

//Initialize the OAM
void initOAM(SpriteEntry * spriteEntry, SpriteRotation * spriteRotation, int screenPass)
{
   //For all 128 sprites on the DS, disable and clear any attributes they
   //might have. This prevents any garbage from being displayed and gives
   //us a clean slate to work with.
   for(int i = 0; i < 128; i++) {
      spriteEntry[i].attribute[0] = ATTR0_DISABLED;//0 works too?
      spriteEntry[i].attribute[1] = 0;
      spriteEntry[i].attribute[2] = 0;
   }

   //5 looks card specific, maybe do 128, or maybe just init 5 sprites to begin with      
   for(int i = 0; i < 32; i++) {
      spriteRotation[i].hdx = 256;
      spriteRotation[i].hdy = 0;
      spriteRotation[i].vdx = 0;
      spriteRotation[i].vdy = 256;
   }


Also, this is what the first definition of sprites looks like:

Code:
//////////////////////////// Sprite setup ////////////////////////////////
   //create the sprite entry table
   SpriteEntry * spritesMain = new SpriteEntry[128];
   SpriteEntry * spritesSub = new SpriteEntry[128];

   //Create the sprite rotation table, assigning it to the same location
   //as spritesMain because the attributes overlap in memory
   SpriteRotation * spriteRotationsMain = (SpriteRotation *)spritesMain;
   SpriteRotation * spriteRotationsSub = (SpriteRotation *)spritesSub;

#140333 - tepples - Sat Sep 15, 2007 12:38 pm

knight0fdragon wrote:
I would need to see code, because you can have up to 32 rotating sprites

What happens is that the PPU runs out of cycles on one scanline and just doesn't render further sprites. The NES and Super NES have the same issue when more than 64 or 256 sprite pixels are displayed on one scanline.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#140460 - jonezer4 - Sun Sep 16, 2007 5:38 pm

tepples wrote:
knight0fdragon wrote:
I would need to see code, because you can have up to 32 rotating sprites

What happens is that the PPU runs out of cycles on one scanline and just doesn't render further sprites. The NES and Super NES have the same issue when more than 64 or 256 sprite pixels are displayed on one scanline.


Ah, okay. That seems to match my issue.

If I change the sprites from "rot_scale_double" to "rot_scale" they all appear, which is an easy sacrifice to make, but then, I have issues where instead of the sprites hiding when I try to hide them, some the image flips over backward, and some do nothing at all.

I'm flipping the 8th bit to hide, which works fine for the _double, but for whatever reason doesn't work on regurl old "rot_scale". Is hiding on a different bit depending on the sprite size?

Code:

Code:
// Hide a Sprite
void hideSprite(SpriteEntry * spriteEntry, bool hide)
{
    if (!hide)
   {
   
        spriteEntry->attribute[0] |= BIT(8); //bit 8 on 0x0080
    } else
   {
 
        spriteEntry->attribute[0] &= ~BIT(8); //bit 8 off 0xFF7F
    }
}

#140464 - tepples - Sun Sep 16, 2007 5:52 pm

jonezer4 wrote:
If I change the sprites from "rot_scale_double" to "rot_scale" they all appear, which is an easy sacrifice to make

One thing you can try is using rot_scale_double only for those sprites that need it.

Quote:
but then, I have issues where instead of the sprites hiding when I try to hide them, some the image flips over backward, and some do nothing at all.

I'm flipping the 8th bit to hide, which works fine for the _double, but for whatever reason doesn't work on regurl old "rot_scale". Is hiding on a different bit depending on the sprite size?

If you want to hide a sprite, set it to rot_scale_double and then hide it the way you've been hiding it.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.