Structs and arrays - what is best practice?

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

Post Reply
Copy_con^Z
Posts: 10
Joined: Tue May 04, 2021 8:49 pm

Structs and arrays - what is best practice?

Post by Copy_con^Z »

Hi!

I am building my first 'game' with a map with single screen rooms. As I've leaned more and more I've done changes to my code to make it as efficient as possible, given my very basic knowlege.

Currently my game world concists of multiple layers of indirect addressing. It is probably considered bad practice, but I can't come up with a better way to structure my data and still keep the map size down in the ROM.

The layout is as follows:

All screens are stored as data words, referring to 17 bytes arrays (byte 16 for attributes stored as words referring to arrays) which in turn are referring data words referring to arrays containing the meta tiles.

Everything works just fine, since the pointers are only loaded at screen transitions, but given the discussions abouts structs and arrays I would need some advice on how to make the code better (since I suspect you guys shiver when you see the example below).

Thanks in advance.

Code: Select all

WorldData: ;contains all screens in the game. Screens can be reused 
  .dw StartScreen, Restaurant, Pawnshop, Town1, Town1 ; ...
  

;Arrays containing the meta tile columns
StartScreen:
  .db $00,$01,$02,$01,$00,$02,$00,$01,$02,$01,$00,$02,$00,$01,$02,$01,	$00
Restaurant:
  .db $00,$01,$02,$05,$06,$07,$06,$07,$06,$07,$06,$08,$09,$0A,$02,$00,	$04
Town1:
  .db $00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,	$00
Pawnshop:
  .db $00,$01,$02,$05,$06,$07,$06,$08,$09,$0A,$06,$07,$06,$05,$02,$00,	$01
  

MetaTileDefinitions: ;Words referring to the actual meta tile arrays
  .dw CityTile1, CityTile2, CityTile3, CityTile4, CityTile5, HouseTileL, HouseTileWin, HouseTileWall, DoorTileL, DoorTileM, DoorTileR
  
  ;Arrays containing the actual tiles
  CityTile1: 
  .db $27,$27,$27,$27,$27,$27,$37,$37,$37,$37,$36,$34,$36,$34,$32,$32,$32,$32,$32,$32,$32
  .db $27,$27,$27,$27,$27,$3E,$3E,$3E,$3E,$3E,$36,$34,$36,$34,$32,$32,$32,$32,$32,$32,$32

CityTile2:                                          
  .db $27,$27,$27,$27,$27,$3E,$3E,$3E,$36,$34,$36,$34,$32,$32,$32,$32,$32,$32,$32,$32,$32
  .db $27,$27,$27,$27,$27,$27,$27,$3E,$36,$34,$36,$34,$32,$32,$32,$32,$32,$32,$32,$32,$32
  
CityTile3:                                        
  .db $27,$27,$27,$43,$42,$37,$37,$35,$36,$34,$45,$47,$49,$4B,$4B,$4B,$4D,$4F,$A5,$32,$32
  .db $27,$27,$27,$27,$27,$27,$3E,$3E,$3E,$3E,$44,$46,$48,$4A,$4A,$4A,$4C,$4E,$A5,$32,$32

;...
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Structs and arrays - what is best practice?

Post by dougeff »

I don't understand. Those arrays have 42 values (tiles).

Screens are 32 tiles wide and 30 tiles tall. So, why are there 42 values?

Ideally, you would want to compress this more. I see lots of repeated values. What tools are you using? (NES Screen Tool, for example)
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Structs and arrays - what is best practice?

Post by tokumaru »

I don't really get how all of this comes together either, but generally speaking, I don't think that having several levels of indirection is bad, as long as you're actually reusing the stuff you're indirectly referencing. But if you're not reusing the data, then you're just wasting space by introducing a bunch of pointers.

As for the performance impact of going through the multiple levels of indirection, don't even worry about it if this a one off operation (e.g. loading a new level), there's no way players will notice any difference. Only if you need to do it multiple times per frame will it actually have any impact on the performance of your game, in which case you may want to partially decompress the data to RAM, organize the data in a way that only the lower half of the pointers need changing, or unroll some of the code to help speed things up.

I once used a level map format full of indirection: each screen was divided into 4 blocks of 128x128 pixels, which were then divided into 4 blocks of 64x64 pixels, which were then divided into 4 blocks of 32x32 pixels, which were then divided into 4 metatiles of 16x16 pixels, which were finally divided into 4 8x8-pixel tiles. To access this I had a bunch of pointers in ZP, one for each quadrant of each type of block, and to reach any given metatile what I did was shift its coordinates to select which quadrant to read at each level, and just plug the index read at one level into the lower byte of the pointer to the next level. It was fast enough to not cause me any problems, and I had to do this several times per frame for collision detection.

At one point I considered having 256 (the number of metatiles in one screen) unrolled prices of code just reading a value from one of the quadrants and plugging it into the lower byte of the pointer to the next quadrant, but ended up deciding this was not necessary.
Copy_con^Z
Posts: 10
Joined: Tue May 04, 2021 8:49 pm

Re: Structs and arrays - what is best practice?

Post by Copy_con^Z »

dougeff wrote: Wed May 12, 2021 2:48 pm I don't understand. Those arrays have 42 values (tiles).

Screens are 32 tiles wide and 30 tiles tall. So, why are there 42 values?

Ideally, you would want to compress this more. I see lots of repeated values. What tools are you using? (NES Screen Tool, for example)
Sorry for being unclear. The 42 values are my vertical 16x168 meta tiles. 16 of these fill the screen horizontally. The area below is for status bar and doesn't need to be updated.

It most certainly needs to be compressed a lot more, but I'm not sure how. I made a clumsy attempt on RLE but that only managed to lower the byte count by around 15 %.

I'm using NES Screen Tool for browsing the CHR and basic layout of my meta tiles, but other than that I just type the values manually in Notepad++.
Copy_con^Z
Posts: 10
Joined: Tue May 04, 2021 8:49 pm

Re: Structs and arrays - what is best practice?

Post by Copy_con^Z »

tokumaru wrote: Wed May 12, 2021 3:16 pm As for the performance impact of going through the multiple levels of indirection, don't even worry about it if this a one off operation (e.g. loading a new level), there's no way players will notice any difference. Only if you need to do it multiple times per frame will it actually have any impact on the performance of your game, in which case you may want to partially decompress the data to RAM, organize the data in a way that only the lower half of the pointers need changing, or unroll some of the code to help speed things up.
Thanks!

By the way, your templates was the main reason why I dared to go from NESASM to ASM6

All of the metatiles are used many times throughout the game in different combinations with different palettes so I believe my method is decent in that respect, I was primarily worried about the levels of indirection and that I was treading down the wrong path.

I'm currently buffering one column of tiles to RAM Before Writing to PPU. It probably doesn't add to performance as the code is structured now. I draw vertically to the $2006 with the bit 2 in $2000, but since it doesn't 'wrap' but continues on to the attributes part (as I understand it after a lot of tinkering), I gave up on buffering for now.

Another thing I'm thinking a lot about is how to make the best use of RAM. As a newbie, leaning by doing, I have a very conservative approach to putting a lot of stuff there in fear of running out later on when there will be more action going on on screen than just drawing background and moving the player sprite forth and back.
Post Reply