How do you make the player animate simular to the smurfs?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
User avatar
Hamtaro126
Posts: 765
Joined: Thu Jan 19, 2006 5:08 pm

How do you make the player animate simular to the smurfs?

Post by Hamtaro126 » Sun Nov 25, 2007 10:20 pm

I recently found ''The Smurfs (E).nes'' and it uses CHR-RAM. One thing i wondered about the game's CHR-RAM player animation though, I need to implent a ''Animate Character using only 1 Character in 32x32 (or 16x32) Slots in the CHR-RAM'' in my game.

I need some help in getting 16x32(or 32x32) characters in CHR-RAM to update only in one Character place!

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

Post by tokumaru » Sun Nov 25, 2007 11:10 pm

There is no trick, really. You just have to redraw the patterns during VBlank. That's a lot of data to update during VBlank though, so you'll have to code very efficient code to do this (unrolled loop), or keep the PPU disabled for a while after VBlank ends to get a little extra time. You'll probably have to do both actually!

I do exactly this in my current project. All the game graphics are stored in the ROM as blocks of 256 bytes (16 tiles). Every frame there is time to copy one of these blocks to a place in the pattern table. Those 256 bytes are scrambled a bit, to make reading them faster.

All blocks are inside 16KB ROM banks, meaning there are 64 of them in each a bank. The pattern-updating routine will switch in the bank with the desired block of tiles, load the X register with the index of the block inside that bank (0 to 63), set the destination address through $2006 and execute the following code:

Code: Select all

	lda Byte00, x
	sta $2007
	lda Byte01, x
	sta $2007
	lda Byte02, x
	sta $2007
	(...)
	lda ByteFE, x
	sta $2007
	lda ByteFF, x
	sta $2007
This code takes quite a bit of ROM space (1536 bytes), but makes it possible to update 256 bytes worth of patterns in 2048 CPU cycles (about 18 scanlines). That's almost the entire VBlank time, so I sure have to delay the start of the frame a bit in order to do other things while rendering is still off (update the palette, metatiles, sprites, etc). Those other routines should be pretty efficient too, since so much time was dedicated to copying patterns already.

Also, as I said before, the tiles are a bit scrambled. The first byte of each of the 64 blocks in the bank are stored together, and this list is pointed by the "Byte00" label. Then follow all the second bytes (pointed by "Byte01"), and so on.

User avatar
Memblers
Site Admin
Posts: 3770
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers » Sun Nov 25, 2007 11:14 pm

At 16 bytes per tile, 32x32 pixels is gonna be 256 bytes to update. That's pushing right up to the limit of how much CHR you can update during a vblank period. Assuming you're doing a sprite-DMA, and no other CHR updates (like nametable, palette) you'll have about 7 cycles on average to transfer each of those 256 bytes.

Load the animated tiles into a buffer before vblank, if indirect reads are too slow.
Also, as I said before, the tiles are a bit scrambled. The first byte of each of the 64 blocks in the bank are stored together, and this list is pointed by the "Byte00" label. Then follow all the second bytes (pointed by "Byte01"), and so on.
Cool trick! :)

User avatar
Dwedit
Posts: 4236
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit » Sun Nov 25, 2007 11:23 pm

If you're using MMC3, you can get extra copy time by having an IRQ go off 8 scanlines before vblank. Turn off the screen then, and think of it as an early vblank.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

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

Post by tokumaru » Sun Nov 25, 2007 11:48 pm

Memblers wrote:Cool trick! :)
Heh, I needed a way to easily index those blocks without using indirect indexed addressing, in which case each read would use 5 cycles (plus the 2 cycles to increment Y after every byte). This is the best I could come up with so far...
Dwedit wrote:If you're using MMC3, you can get extra copy time by having an IRQ go off 8 scanlines before vblank.
I kinda assumed UNROM, since that's what the Smurfs game is. But yeah, you can use the scanline IRQ if you are using the MMC3, as long as you respect the rules for it to work right.

I always use 8x16 sprites and access both sides of the pattern tables, so that IRQ is worthless for me. I don't know what the designers of the MMC3 were on when they decided to take away such an important feature of the NES so that you could use their IRQ... I'm sure many of you guys will say that is not such an important feature, but for me it is.

Anyway, you should debug this game's NMI routine and see what it does during VBlank exactly. Since it has a few black scanlines at the top, I bet it keeps rendering disabled for those few extra scanlines.

tepples
Posts: 21755
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples » Mon Nov 26, 2007 9:10 am

In 8x16-pixel sprite mode, you have 128 distinct spaces in $1000-$1FFF. This is enough to double-buffer all 64 sprites.

But updating CHR RAM might be easier in a (E) game because unlike (JU) games, which have about 2300 cycles to do vblank updates, (E) games have about 7500 due to the extra blank scanlines of 50 Hz TV.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Mon Nov 26, 2007 10:51 am

How would double-buffering help? Either way you can still update VRAM only during vblank.

tepples
Posts: 21755
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples » Mon Nov 26, 2007 12:24 pm

blargg wrote:How would double-buffering help? Either way you can still update VRAM only during vblank.
For atomicity. If it takes longer to update all the tiles that you want to update than a single vblank, you won't show any half-updated tiles.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Mon Nov 26, 2007 3:58 pm

For atomicity. If it takes longer to update all the tiles that you want to update than a single vblank, you won't show any half-updated tiles.
You could still avoid updating only part of a particular object's sprite tiles without using double buffering, though it might result in less throughput due to an unoptimal granularity (dedicated double buffering would always do 16 tiles per frame, while this might do less on some frames if there were no combination of whole objects to update that totaled exactly 16 tiles). You'd only need double buffering if more than 16 tiles had to change on the same frame, for example if they were all part of a single object.

Post Reply