Reading from PPU for background collision

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
-Basti-
Posts: 40
Joined: Sun Sep 26, 2010 10:29 pm

Reading from PPU for background collision

Post by -Basti- »

Hi,
I need some help with reading from PPU. I am following the Nesdoug tutorial series and want to code a collision check between
a sprite and a background tile. Thus Doug mentions that reading from PPU while doing background collision is quite ugly, I would
like to give it a shot. Here is my code

Code: Select all

#define VOID_TILE			0x00
#define OBSTICLE_TILE		0x01
#define NAMETABLE			0x2000

#define BG_ADR(x,y)		((y<<2)|(x>>3))
#define MSB(x)			(((x)>>8))
#define LSB(x)			(((x)&0xff))

...

void render_routine(void){
	wait_until_nmi();
	PPU_ADDRESS = MSB(NAMETABLE + BG_ADR(projectile_x, projectile_y));
	PPU_ADDRESS = LSB(NAMETABLE + BG_ADR(projectile_x, projectile_y));

	if(PPU_DATA == OBSTICLE_TILE){
		PPU_DATA = VOID_TILE;
	}
        reset_scrolling();
}

...
This code correctly detects, that the projectile hits a OBSTICLE_TILE, but renders the VOID_TILE to a complete different
place on the screen (but on the same line). Can somebody help? Also it would be interesing, if it is common style to avoid
PPU reading while doing background collision cheks and therefore maintain internal collision maps, like in Nesdoug's tutorial series.

Regards
Sebastian
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Reading from PPU for background collision

Post by lidnariq »

1) PPU_DATA has a one-byte delay for reads. So you have to set the address, read, and then the next read has the value you cared about.
2) PPU_ADDR will automatically increment after every read or write. So you'd have to re-set the address between when you read from memory and when you wrote to it.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Reading from PPU for background collision

Post by tokumaru »

The problem is not that it's "ugly", it's just not practical. You can only access VRAM during vblank, which lasts about 8% of the time of a frame, so it simply doesn't make sense to cram VRAM updates and collision logic inside such a small slice of time, while a lot of the remaining 92% of the time goes to waste. This may work for really simple games where not much happens each frame, but as you add more objects and more action, this model quickly becomes unfeasible.
User avatar
Sumez
Posts: 919
Joined: Thu Sep 15, 2016 6:29 am
Location: Denmark (PAL)

Re: Reading from PPU for background collision

Post by Sumez »

One thing you can do that is still ugly, but at least feasible, if you're set on doing it this way, is use cartridge PRG-RAM and buffer writes to the nametable in a section of RAM that you're expecting to be identical to your nametable data, and read from that instead of using the PPU registers. It will eat up a massive 960 bytes (per nametable) that could have been used better, but the effect will be similar to what you're looking for.

I actually did exactly this for my recent Donkey Kong port, as I wanted the game to behave exactly like the arcade game down to minor details in the techniques used. The original game reads directly from video ram, but the NES can't do that, so I write to the buffer area in PRG-RAM instead, and copy it over whenever I need it, either managed blocks during vblank, or while turning off rendering.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Re: Reading from PPU for background collision

Post by Celius »

Sumez wrote:One thing you can do that is still ugly, but at least feasible, if you're set on doing it this way, is use cartridge PRG-RAM and buffer writes to the nametable in a section of RAM that you're expecting to be identical to your nametable data, and read from that instead of using the PPU registers. It will eat up a massive 960 bytes (per nametable) that could have been used better, but the effect will be similar to what you're looking for.
I think the main thing that is impractical is not even the RAM that is consumed, but trying to maintain that area of PRG-RAM so that it is identical to the PPU data. If you update your code that writes to $2007 so that it also writes to RAM, your updates to RAM won't benefit from the PPU auto-increment. You'll need to manually increment your index or address variable every time you write a value to $2007.

This is assuming that you are wanting to have access to any tile displayed on the screen at all times, including the HUD and other objects. If you're not including things like the HUD in your RAM copy, and you're dumping just the tiles that make up the background of your level, I would argue that that's not really creating a mirror of what is displayed, it's just decompressing your level into RAM, which is a common practice.

EDIT:
Sumez wrote:I actually did exactly this for my recent Donkey Kong port, as I wanted the game to behave exactly like the arcade game down to minor details in the techniques used. The original game reads directly from video ram, but the NES can't do that, so I write to the buffer area in PRG-RAM instead, and copy it over whenever I need it, either managed blocks during vblank, or while turning off rendering.
One thing I hadn't considered when writing my post originally was that you could use the RAM copy of the screen to actually perform writes to $2007. In that case, I could actually see this being useful.
Last edited by Celius on Tue Dec 05, 2017 3:23 pm, edited 1 time in total.
User avatar
Sumez
Posts: 919
Joined: Thu Sep 15, 2016 6:29 am
Location: Denmark (PAL)

Re: Reading from PPU for background collision

Post by Sumez »

That's pretty much exactly how I used it in my example... though it wouldn't have been necessary at all if the game didn't have logic that tries reading from the nametable data several times during a frame.
I definitely wouldn't have done it like that if I had to design the game from the ground up. :) I like to keep my collision data fast to decompress on the fly. Different methods for different games, though.
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Reading from PPU for background collision

Post by DRW »

-Basti- wrote:Also it would be interesing, if it is common style to avoid
PPU reading while doing background collision cheks and therefore maintain internal collision maps, like in Nesdoug's tutorial series.
A general hint: You should usually separate your game logic from the graphical operations.

For example, when you have a character moving on screen, you should declare an x and y variable and not use the coordinate values from the actual hardware sprites for calculations.

Likewise, if your opponent is a background image, you should use general variables for collision checks instead of acually reading the PPU values and checking whether the empty background tile is set.

Not only does this free you from the restriction of only being able to read the PPU during vblank.
You can also access the variables much faster since you don't always have to set a register first.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
-Basti-
Posts: 40
Joined: Sun Sep 26, 2010 10:29 pm

Re: Reading from PPU for background collision

Post by -Basti- »

Thank you all for your help. I think I will revise my thoughts and go back to collision maps. One more thing: how do scrolling games handle this? Do they switch between various collision maps according to the position of the player?

Regards
Sebastian
User avatar
Sumez
Posts: 919
Joined: Thu Sep 15, 2016 6:29 am
Location: Denmark (PAL)

Re: Reading from PPU for background collision

Post by Sumez »

The best practice I'd guess is having an algorithm that transforms an entity X/Y coordinate into a specific tile in your data, especially if you want to reuse the concept between both the player and enemies/other AI controlled stuff. The issue with scrolling games is of course that you'll be working with 16 bit coordinates on at least one axis.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Reading from PPU for background collision

Post by tokumaru »

-Basti- wrote:Do they switch between various collision maps according to the position of the player?
That wouldn't work well when characters/objects cross the boundary between two screens, because they'd need simultaneous access to two separate collision maps.

Games that scroll usually work with a "cache" of the level map, at least 2 screens wide, so that the game has a little bit of space to work with past the edges of the screen (i.e. objects won't run into invalid collision data as they exit the screen). This cache, much like the name tables, is updated with new data as the camera moves, so the player is always surrounded by valid collision data.

Other games just have access to the complete level map at all times (i.e. the map is stored in a random-access-friendly format in ROM or decompressed in full to RAM), so that every piece of the level is accessible at any time.

Either way, scrolling games benefit greatly from using coordinates larger than 8 bits, since the space in which characters/objects live is larger than a single screen. These coordinates then have to be converted from level space to screen space for display purposes, via the following formula: SpriteX = ObjectX - CameraX
User avatar
Dwedit
Posts: 4924
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: Reading from PPU for background collision

Post by Dwedit »

It just so happens that Dragon Quest 1 read all the tilemaps out of CHR-ROM.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Re: Reading from PPU for background collision

Post by Celius »

Another disadvantage to using hardware tiles for collision detection is that if you do, you really can't repurpose a hardware tile. For example, you can't use the same tile to draw a real wall and a fake wall, even though the graphics are identical. Metatiles have more flexibility this way, and multiple metatile definitions typically takes up less space than multiple hardware tiles (you also have more room to define metatiles in PRG-ROM than you do hardware tiles in CHR-ROM).
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Reading from PPU for background collision

Post by DRW »

-Basti- wrote:One more thing: how do scrolling games handle this? Do they switch between various collision maps according to the position of the player?
I guess this is done differently for different games.

My own game, "City Trouble", had an array with 34 values: The platform height for each of the 32 visible tiles + the tile left from the screen + the tile right from the screen. (If the scrolling doesn't align to 8 pixels, the part right from the screen is even visible.)

Whenever the scrolling aligned to 8 pixels, I shifted the values in the array to the left and loaded the rightmost position with the new value from the general level data.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Reading from PPU for background collision

Post by dougeff »

Your game (DRW) has auto-scrolling. So, load if scroll multiple of 8 makes sense here. Any game where scroll can shift 2 pixels in 1 frame might miss a load.
nesdoug.com -- blog/tutorial on programming for the NES
na_th_an
Posts: 558
Joined: Mon May 27, 2013 9:40 am

Re: Reading from PPU for background collision

Post by na_th_an »

DRW wrote:Whenever the scrolling aligned to 8 pixels, I shifted the values in the array to the left and loaded the rightmost position with the new value from the general level data.
As a hint for future games, you can avoid having to shift the entire array if you use a circular array with a moving, "virtual" first index. To "shift" the whole array left or right you just move the origin. Of course, this puts some overhead in the functions which read from the array (you have to perform a substraction and a modulus - and if you are using powers of two / check + substraction if you are not), so it's a matter of measuring if the time gained from not having to shift an array is more than the extra time spent in the accesses.

In my situation, for example, it's quite useful. I keep a 16x16 metatile area as a collision map, and "scrolling it" would be overkill, so the circular array idea is most suitable.
Post Reply