level sizes

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
level sizes
by on (#66245)
I'm making a new game engine, and it currently handles levels that are 32x4 screens or 512x64 blocks long. If I make my engine support other level sizes, do I need a different tile collision algorithm for every level size, or is there a way to make your tile collsion algorithm adjust to different sized levels?

by on (#66247)
I don't think it would matter, unless you're wanting to account for off-screen collisions (heheh). I haven't implemented this stuff properly yet, but I would hope to have the collision code indexing the level based on the scroll position. So then it would go as far as your level data goes.

by on (#66248)
In a well programmed game level sizes shouldn't have anything to do with collision detection. You just need a solid way to read collision information from the map based on world coordinates, and that should work for levels of all sizes.

by on (#66249)
How can it not have to do with the size of the level? If I have 512 x 64 tiles, I have to multiply the y-tile by 512 and add it to the x-tile. If I have 256 x 128 tiles, I have to multiply the y-tile by 256 and add it to the x-tile.

by on (#66250)
psycopathicteen wrote:
How should I store the level then?

Depends on how much ROM you have to waste! =)

Since you're working on the SNES, which has much more RAM than the NES, it makes sense to have your whole map decompressed from whatever format you compressed it with to RAM. Once in RAM it should be nothing more than a 2D array, that you access with the good old formula Y * width + X.

by on (#66251)
psycopathicteen wrote:
How can it not have to do with the size of the level? If I have 512 x 64 tiles, I have to multiply the y-tile by 512 and add it to the x-tile. If I have 256 x 128 tiles, I have to multiply the y-tile by 256 and add it to the x-tile.

Just don't hardcode the width. I'm sure the SNES can handle a dynamic multiplication. If you think it will be too slow, you can set aside some RAM and pre-calculate pointers to each row, like I do with my NES game.

by on (#66252)
Too bad the Snes only has an 8 bit multiply.

by on (#66253)
Well, the NES doesn't have any built-in multiplication and it can handle arbitrarily sized levels just fine, I'm sure the SNES can do it as well.

In my NES game, the level map is composed of screens (256x256 pixel blocks), like Sonic 1 on the MD or Mega Man X on the SNES, and levels can be up to 64 screens tall. Since the width of the maps varies and I don't want to do a multiplication every time I want to access them, Before the level starts I pre-calculate the address of every row of screens and store in a table, so that the Y coordinate will indicate which address I must use and the X coordinate is used as an index from that address on.

by on (#66254)
So write a multiply function. The SNES one is 8x8=16 IIRC, and you can build whatever size you need out of that, and even do it with straight normal arithmetic if you so chose.

by on (#66255)
You could arrange your decompressed level as a set of 256x256 pixel areas, as tokumaru edited into his post while I was writing mine. Animal Crossing series does this, wherein the 16x16-metatile areas are called "acres". Under such a scheme, you could even have screens with nothing in them be a null pointer; AC uses this for the solid wall or water acres outside the area where the player character can walk. Sonic 2 for Genesis likewise uses supermetatiles, each 128x128 pixels or 8x8 metatiles. Levels have a fixed size, but an individual supermetatile (such as those representing emptiness) is reused several times in a level. The one thing that becomes more complicated with supermetatile reuse is destructible terrain; you have to reserve some parts of RAM for copy-on-write, which is why Sonic tends to use sprites for anything destructible (such as rings).

by on (#66257)
If I'm doing it by level, I'll use logarithmic multiplication.

If I'm doing it by room, I'll use the hardware multiplication.

Which method is the better method?


Just so you know, I'm doing v-ram loading a little bit different than conventional ways. I'm using the PPU's 16x16 pixel tile mode as opposed to metatiles, and I'm updating the screen as a whole as opposed to the conventional collumn and row method. I'm blanking out the top and bottom 16 scanlines to have more dma time. It might sound a little redundant, but it will make swapping out tiles so much easier.

by on (#66258)
Aren't rooms essentially small levels? I don't see much difference.

by on (#66260)
Perhaps "rooms" use screen-aligned scrolling as seen in The Legend of Zelda or Smash TV or The Legend of Zelda: Link's Awakening.

by on (#66263)
psycopathicteen wrote:
Too bad the Snes only has an 8 bit multiply.

It has signed 16x8 as well. See M7A, M7B, MPYL, MPYM, and MPYH.

by on (#66265)
On MD I have a so called "sector" setup, where there's always 4 active sectors that are decompressed into RAM and from within the objects are checked etc. Each sector is 16x16 metatiles in size, each metatile being 16x16 pixels. I only need 2Kbyte decomrpession buffer and all colission data etc. are derived from stage data in the zones. There's no MUL or DIV happening since there's no need, just old fashioned shifts ^^

Gotta love powers of two ^^ (and VDP address auto increment, I can toss out nearly all VRAM location calculations with the help of it :D )

One linitation is that going from one end of the stage to another will require decompressing inbetween sectors... that could be remedied if you use seek tables...

by on (#66271)
I don't see the problem if you only get 8x8 -> 16 bit multiplication. If you want to do, say, 16x16 -> 32 bit, all it takes it to compute LOW*LOW, LOW*HIGH, HIGH*LOW and HIGH*HIGH, and add/shift them together.
I guess the multiplication unit is the same used for more 7, and that it only works when mode 7 is not used (is this right or am I just making this one up ?). If this is the case you'd want to only use this if you're absolutely sure you don't need mode 7 in your game.

You'd want to look the NESdevwiki article about multiplication - of course it's different on the SNES because of 16-bit mode but you get the idea on doing a multiplication without using an hardware unit.
And for a level which has to do frequent multiplications variable * variable, but where one of the variables is only modified rarely, it's a good option to reserve some RAM to have self-modifying code doing the multiplication by this value (that is generated by software).

by on (#66276)
I'm using logarithmic shifts.

Code:
lda Y_TILE
ldx log(WIDTH)
loop:
asl
dex
bne loop


EDIT: I just thought of a faster code
Code:
lda Y_TILE
ora indication_bit
loop:
asl
bcc loop


It stops shifting when it detects the indication bit.

by on (#66278)
Or to reduce the loop overhead even further, you can build an unrolled ASL-slide in RAM and call that.

by on (#66284)
I just thought of something. For calculating the x tile, your required to shift right 4 bits, but for y tile, you can just AND it with #$fff0.