What’s a sprite? A sprite is a tile that can be moved freely all over the screen. Sprites are usually 8×8, but they can also be 8×16 (a little more complicated). I will be using 8×8 examples. Sprites are defined by the 256 bytes in the OAM part of the PPU. There are 64 sprites. That’s 4 bytes per sprite.
But 8×8 is so small. How do we make Mario so big? We combine multiple sprites to move together on the screen. This is called a metasprite. Look below. Small Mario is made up of 4 sprites, and large Mario is made up of 8 sprites.
Sprites on the NES have an annoying limitation. 8 sprites per horizontal line. That’s all you get, Any more than that, and then next sprite will disappear. You might have seen sprites flickering in NES games. To avoid disappearing sprites, it is common to rotate the order of the sprites in the OAM, so that the sprite that disappears alternates, creating flickering. That’s better than an invisible sprite, I suppose.
Another oddity, is that sprites are always shifted down 1 pixel. If you put a sprite at Y = 0, the top of the sprite won’t appear until the next line down. Look at Mario’s feet, and you see that he is 1 pixel into the floor. This might look ok for platform games, but a top down game might look better with sprites aligned to the background. We can do this easily by shifting the BG down 1 pixel.
Sprites can go anywhere on the screen. However, they are not very good at moving smoothly off the left side of the screen. There is an option (PPU Mask 2001 bits xxxx x11x), that if zero, you turn off the left 8 pixels of the screen, and THEN you can smoothly move off the left side of the screen.
Any sprite Y position >= $ef is off the screen. When you call the function oam_clear() or oam_hide_rest(), it puts the sprites Y at $ff, which is below the screen. Sprites don’t wrap around.
So… there are 4 bytes for sprites. Y, tile #, attributes, and X.
Attributes (copied from the wiki)
76543210 |||||||| ||||||++- Palette (4 to 7) of sprite |||+++--- Unimplemented ||+------ Priority (0: in front of background; 1: behind background) |+------- Flip sprite horizontally +-------- Flip sprite vertically
So how do we make sprites appear? Like writing to the background, you can only write to the sprites during v-blank, which is handled by neslib in the nmi code. The standard way to do this, is to set aside a 256 byte buffer, aligned exactly to xx00. The picture above is at $700, but neslib usually uses $200 (defined in crt0.s as OAM_BUF). The nmi code will do a quick OAM DMA and copy all the sprites from the buffer to the OAM.
Since we are using a buffer, you should be able to write to the buffer at any time. I prefer to clear the buffer every frame, and rebuild it from scratch every frame.
Clear the sprite buffer.
sprid = 0;
Set the index to the buffer to zero. sprid (sprite id #) is a multiple of 4.
sprid = oam_spr(x,y,tile,attribute,old sprid);
Push 1 sprite to the buffer. Remember the index to the buffer.
sprid = oam_meta_spr();
Push 1 metasprite to the buffer. Remember the index to the buffer.
When I made the graphics file, I put the sprite graphics in the second half. We have to remember to tell neslib that we want to use the second half for sprites…
And make sure to define a palette for both BG and sprites.
Ok, so, how do I make a metasprite? NES Screen Tool has a tool for making metasprites. I find it a bit difficult to use. Sometimes I just copy and paste a definition from a same sized metasprite (and change the tile #s). But, if you use NES Screen Tool, you can “put single metasprite to clipboard as C” and then paste it into the code. Then you can pass that to the oam_meta_spr() function.
oam_meta_spr(x,y,old sprid, * data).
The sprite definitions used by neslib (and NES Screen Tool) are out of order. It goes x,y,tile,attribute, as opposed to the NES’s actual byte order (y,tile,attribute,x). Keep that in mind, if you want to just retype it by hand, like I sometimes do.
If you want to have a metasprite that changes direction (and flips horizontally), then you should make 2 separate metasprites, one for each direction.
One limitation. None of these functions keep track of how many sprites are in the buffer. You could easily put in too many, and overwrite the ones in the beginning of the buffer.
This example uses 1 basic sprite and 2 metasprites, and moves then down 1 pixel per frame.
Oh, one more thing. If you are transitioning from one part of a game to another, and you turn off the screen, make sure you clear the sprites before you turn the screen back on so you don’t have 1 frame of junk sprites left on the screen.