arrangement of pattern tables in use by sprites

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

User avatar
GradualGames
Posts: 1106
Joined: Sun Nov 09, 2008 9:18 pm
Location: Pennsylvania, USA
Contact:

arrangement of pattern tables in use by sprites

Post by GradualGames »

disclaimer: major newbie alert (well...newbie to NES but definitely not to programming or assembly language) so forgive me if my post is hard to understand or misuses any terminology..I'll try to work with you on subsequent posts...

How do NES games generally organize their game character's sprites and animations? Go ahead and answer me now if you like, but what follows is my own guess (and how I would probably first attempt to do it once I get around to writing a game):

At this point in my research, having not yet studied much other than docs and an example program that manipulates a single sprite, my guess would be that game characters are organized as follows in the pattern table:

As a pseudo code example, say our character is 4 tiles wide and 4 tiles high, and has two frames of animation:

-each number is a row of 8x8 pixel tiles, where each repetition of that number is a single 8x8 pixel tile along that row. (and thus represents all 16 bytes of that tile)

;this is the pattern table
org:
;animation frame one (left foot in front of right for example)
1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4
;animation frame two (right foot in front of left for example)
1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4


And then, the programmer creates sixteen sprite entries and creates code for animation timing, and displays all of the patterns from the first frame, and then all the patterns from the second frame, and calculates offsets (for x y for all 16 sprites) on the fly from the top left of the game character himself.

That's my guess at this point---and from what I've read and understand, this is definitely one way of doing it...

Regards and Thanks,
-Zom
nes noob
AWJ
Posts: 433
Joined: Mon Nov 10, 2008 3:09 pm

Post by AWJ »

There's no "standard" convention to pattern table layout. It depends on what size and shape (square, rectangular, or irregular) of meta-sprite you're using, how complex your animations are, and of course on what hardware sprite size (8x8 or 8x16) you're using. If you look at five different commercial games you'll probably find five completely different implementations, and quite likely multiple methods in the same game (e.g. in a platformer like Ninja Gaiden, powerup items and small enemies could be simple 2x2 tile squares while the player and larger enemies could be irregularly-shaped)
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Here's how I do mine.

Most objects (entities in the game world, e.g. enemies, the character, bullets, swinging platforms, anything that has some sort of intelligence) are made of sprites, like you were saying. Some can be made with the BG, like really really big bosses, but most are made with sprites.

All sprite objects in my game are placed on the screen relative to the coordinates of the top-left corner of their "box". Their box is just a rectangle that encompasses the whole object. So in ROM, I have tables which define placements of sprites relative to this coordinate. They may look something like this:

Code: Select all

ObjectXAnimation1:
.db 4		;Object is made of 4 sprites
.db 0,$40,$02,0		;Relative Y, Tile ID, Attribute, Relative X for sprite 1
.db 0,$41,$02,8		;Relative Y, Tile ID, Attribute, Relative X for sprite 2
.db 8,$42,$02,0		;... Sprite 3
.db 8,$43,$02,8		;Sprite 4
So that says the object is to be drawn with 4 sprites, where all the relative Xs and Relative Ys are just added to the object's top left corner coordinates to determine their placement. So if the object's top left corner is located on screen at 155, 32, the first sprite will be at 155, 32, the next will be at 163, 32, the next will be at 155,40, then the last one will be at 163,40.

The beauty of doing it this way is you can have sprites made out of any tiles anywhere (so long as it's not more than 256 pixels away from the object's coords, because relativity is measure using 8 bits). You can also have a large object and only use like 2 sprites if you need to; you don't need to put in a bunch of empty space into the pattern tables.
User avatar
GradualGames
Posts: 1106
Joined: Sun Nov 09, 2008 9:18 pm
Location: Pennsylvania, USA
Contact:

great responses

Post by GradualGames »

Thanks for your responses. Yes, I assumed there were probably many ways of implementing this. Celius, my guess was to calculate the relative x and y on the fly---but that's a big waste of precious cpu cycles isn't it? I like your table technique. I'll try something like that when I make a game.

Regards,
-Zom
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: great responses

Post by tokumaru »

ZomCoder wrote:Celius, my guess was to calculate the relative x and y on the fly---but that's a big waste of precious cpu cycles isn't it?
I use the same method Celius mentioned, because this is the most versatile way I can think of. If your game only uses perfectly aligned grids of sprites, you could save ROM and CPU cycles by defining a single pair of relative coordinates, the dimensions of the grid and then only tile IDs and attributes for each of the sprites. It really depends on how complex your graphics are. You might even decide to use a single palette for the whole metasprite, something that will save even more ROM.

A good alternative would be to have 2 sprite rendering routines, one that deals with the more versatile complex sprites, and another to handle simpler grid sprites. Since the calls to the drawing routines usually are made from the code of each object, depending on their complexity you call one or the other, and then you won't be wasting cycles.

Now, about animations, they can also be implemented with simple tables. Each entry in the table should consist of a pointer to the sprite definition to use and a frame count indicating how long it should be displayed. A flag could be used to indicate the end of the animation. Each object will need to have a pointer to the animation frame they are currently using, and a counter (copied from the frame duration table) that is decremented each frame and causes tha animation to advance when it reaches 0.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

I was thinking of making a special drawing routine for 2x2 objects, because there are lots of them and it might be quicker as they don't have a variable size. Another thing to do is make an unflippable-object drawing routine where every object is drawn as is. They cannot be flipped, saving you tons of cycles. Basically you just add the coordinates and store.

One thing you'll definitely want to make is a one-sprite-object drawing routine. If an object is made of one sprite only, you should be able to get away without wasting cycles like you would in the really complex object drawing method.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

I was just thinking about this and Celius's way is the most obvious and best way of doing it, as far as I can see. I would've done that without seeing it, I like organizing things that way. :)

Something else just hit me as I was playing around: are all 64 sprites constantly in use? I store 00 in the whole block of sprite data, and when I accidentally put some graphics in ID 0, I noticed that it showed up in the corner of the screen. Of course it would, with 0 X, 0 Y...

So logically they're all constantly in use? It's no problem keeping graphics out of ID 0, I was just wondering. It's not very convenient to fill SPR-RAM with anything other than 0s because it would mess up the attributes.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

UncleSporky wrote:It's no problem keeping graphics out of ID 0, I was just wondering. It's not very convenient to fill SPR-RAM with anything other than 0s because it would mess up the attributes.
Falsitude! Yeah, I know that's not a word.

If you fill unused sprites with $FF, they will not be displayed, because they will be at Y = 255, and there are only 240 pixels vertically on the screen (224 for NTSC). They will always be hidden. If they're hidden, it doesn't matter what data they contain, as they aren't visible.

And also, all 64 sprites are renderable. There's no way to say disable sprite #5 from being rendered at a variable location. But you can do tricks to disable sprite rendering mid frame to not render sprites with coordinates past that scanline.
Last edited by Celius on Tue Jan 06, 2009 7:52 pm, edited 1 time in total.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

Celius wrote:
UncleSporky wrote: So logically they're all constantly in use? It's no problem keeping graphics out of ID 0, I was just wondering. It's not very convenient to fill SPR-RAM with anything other than 0s because it would mess up the attributes.
Falsitude! Yeah, I know that's not a word.

If you fill unused sprites with $FF, they will not be displayed, because they will be at Y = 255, and there are only 240 pixels vertically on the screen (224 for NTSC). They will always be hidden. If they're hidden, it doesn't matter what data they contain, as they aren't visible.
Oh, so the NES doesn't care if the attributes contain junk data if it's off the screen...good to know.

I just adopted your method for the first time and now I have a headache trying to figure out how to implement it. I sort of know the assembly, it's just putting it together...

How do I add the metadata value to the sprite's Y while maintaining a nice loop...should I use all the registers, or even a temp variable? That seems like the easy way out.

Code: Select all

ldx 0
ldy 4 ;object of 4 sprites
Repeat:
lda MetaData, x
<increment data at (SpriteTable, x) by a>
inx
lda MetaData, x
sta SpriteTable, x
inx
lda MetaData, x
sta SpriteTable, x
inx
lda MetaData, x
<increment data at (SpriteTable, x) by a>
inx
dey
beq Repeat
Hmm...
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

Celius wrote:(224 for NTSC)
Somewhat misleading.

NTSC NES's render a full 240 scanlines, not just 224. The 'extra' scanlines are visible on some TVs, and even if they're not, the game creator should not assume they're invisible.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Oh, you should know that these routines eat up many many scanlines lots of the time. My routine is pretty long. It's not a small little loop like that, because I have to take into consideration flipped sprites, attributes, and 16-bit coordinates.

There's no one answer "how to do it". What you want to do is develop a routine that can be fed these values:

Address of Animation Table (16 bits)
Y coordinate (probably 16-bit)
X coordinate (probably 16-bit too)
Flip Status (Usually like an attribute value for the whole object, 8 bits)

I have mine set up a little differently, so I feed 8 bytes into the routine to draw an object. With that system above, you'd feed 7.

Since there are 4 values (most likely 7 bytes), you'll definitely want to be using temp values and all registers to make the most time efficient loop possible. Again, these will probably take a very long time to execute.

EDIT: Oh yes, and what Disch said is true. All 240 scanlines are rendered, just not displayed on lots of NTSC TVs. So any Y value for sprites more than 240 is safe for hiding them.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

Since the adress of the table is always in the $8000-$ffff range, I use bit 15 of adress for horizontal flip status.

Oh and yeah Celius' way of doing it really seems like the standard. I used another format at first, but I eventually modified it so that it is exactly what Celius is suggesting. The best you can think of (it may waste a little ROM space tough, but is so flexible you don't care).

Also, I don't draw sprites form ther AI routine, but for a dedicated routine. The AI only tells the number of animation I should drawn. I do this for obvious sorting purposes : The sprites I need to draw must be sorted in some order, and I run object's AI in a constant order.

The main limitation of my way of doing it is that 1 object = 1 metasprite. I'd like to have a ghost with flames rotating arround him, if I make all rotations in the same metatsprites it will eat up a lot of ROM space defining all of these :cry:
I still have an engine to draw explosions that are always on topmost priority and that aren't objects (not directly at least). I could tweak it to draw other stuff as additional explosions.

I could re-arrange my engine so that the sort applies on AI, which will automatically maze sprites in the same order. That wouldn't be a bad idea in fact I just didn't have it.
Useless, lumbering half-wits don't scare us.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

Yeah, the dumb routine I wrote is a terrible hack way to do it that doesn't take anything important into account, I just wanted to see if I could get it do something. I'm actually glad to hear that it ends up being a complex and slow operation, since I'm most worried that I'll code that way when a short little routine would suffice.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

My engine for drawing metasprites doesn't know anything about objects really. At max, there can be 32 metasprites on screen at once, since there is a 256 byte buffer that holds 8 byte chunks for each metasprite. This buffer kind of acts like a stack, where you put the information for one metasprite on, then in the next "slot" you put the info for the next metasprite. In an enemy's AI code, it could place whatever it wants into the stack. An object could display itself as 2 or 3 metasprites if it wanted to, or even as none. Though this might be stupid, unless it's some invisible switch that triggers an event.

The great thing about this is that an object could actually put whatever it wants to as it's coordinates or it's graphics. There aren't any decisions that aren't made by the objects themselves. If an object wanted to, it could place it's graphics at 23 pixels south of it's own coordinates, which might come in handy for just a few special scenarios.

EDIT:
UncleSporky wrote: I'm actually glad to hear that it ends up being a complex and slow operation, since I'm most worried that I'll code that way when a short little routine would suffice.
Yeah, but eventually you'll be able to nicely optimize most of your code. Actually, lots of the time, I find myself optimizing code conceptually instead of just focusing on the routine I've written, scraping by for cycles. I think optimization could be categorized to Micro-optimization and Macro-optimization (kind of like microeconomics and macroeconomics). In Micro-optimization, you optimize the routine by rearranging some instructions, nickel and diming for cycles here and there. Like here:

lda #$23
sta $334
lda #$23

An example of "micro-optimization" would be to eliminate the last 'lda #$23'. Just little things like that.

But with Macro-optimization, you focus on the big picture of things. You focus on the concepts behind your code, and optimize those, usually causing you to completely rewrite routines. For example, if I decided to split an RLE compressed map into 4 quadrants so I wouldn't have to work through so much RLE to find a specific value (DW did this, I believe). This is where you REALLY save cycles.

By the way, I totally just made up the whole "micro" and "macro" thing, so those aren't real terms. Though I think they're good to describe what I'm talking about.
Last edited by Celius on Wed Jan 07, 2009 12:26 pm, edited 1 time in total.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

Celius wrote:My engine for drawing metasprites doesn't know anything about objects really. At max, there can be 32 metasprites on screen at once, since there is a 256 byte buffer that holds 8 byte chunks for each metasprite. This buffer kind of acts like a stack, where you put the information for one metasprite on, then in the next "slot" you put the info for the next metasprite.
My, this is a really great idea. Can I plagiarize it ? Oh seriously I was thinking of doing something like that, but I already changed my game engine extensively once. I don't know yet how I'll do. My engine is perfect as long as each object is either 0 or 1 metatile, but it can't be more (altough objects can decide to display explosions above them).

It's not stupid to have invisible enemies. I have a level who is not terminated by a boss. In order to terminate the level, I have an invisible enemy next to the exit and if the player step on him, instead of getting damaged it just set the stage end flag, like a normal Boss AI does when the boss is dead. However, by doing that I waste some ROM space, as I have to specify an additional enemy and specify dummy values for it's strenght and score reward values. But the size of the enemy, and his sprites maze sense (it's sprite is bland).

So yeah for me each enemy = 1 metasprite. If I were to do a stack system (of FIFO really, it doesn't really matter which one it is), it would be more complicated, more RAM just to optimize things for maybe 3 enemies who would use more than 1 metasprite in the entiere game and some invisible "enemies" that wouldn't neeed a dummy pointer to a metasprite who consist of zero sprites. On the other hand I have a lot of free RAM and this method sounds flexible. I'd still need to find a way to sort sprites in a certain maneer, becuase unlike most 2D games, the order of sprites in my game matters because it is top-down view and not side-scrolling.

Also, filling and emptying the buffer each frame seems like a waste of CPU time. Another method would be to have objects know which metatile slot(s) they take and only change the necessary. Altough that sounds like it would be a lot of bugs doing it that way.

My current game engine allows 8 enemies, 1 player and 8 explosions, which make I guess 18 metasprites in total. There is no way you'd need 32 of them I guess.
Useless, lumbering half-wits don't scare us.
Post Reply