8x16 and whatever else unreg wants to know

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

unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: 8x16 sprite is really a 16x32 pixel image?

Post by unregistered »

tokumaru wrote:
unregistered wrote:Wat is the level map? What info does it hold other than the metatile index? I don't think I have a level map.
If you have a level, you should have a level map. Storing straight name/attribute table is not efficient at all, even if you compress each screen, because you don't take advantage of all the redundancy of blocks that repeat.

A level map is usually a 2D grid of metatiles, where each byte is a metatile index. The level map can be used both for rendering (read a row/column of metatiles from the map, use their indexes to load the tiles that make up the metatiles and write them to VRAM) and for collision (get the metatile index and use it to get the metatile's collision information).

Here's a straightforward representation of a level that uses 32x32-pixel (4x4 tiles) metatiles:

Code: Select all

;16 tiles for each of the 4 metatiles
;metatile:  $00  $01  $02  $03

MatatileTile00:
	.db $00, $04, $08, $0c
MatatileTile01:
	.db $00, $04, $09, $0c
MatatileTile02:
	.db $00, $04, $0a, $0c
MatatileTile03:
	.db $00, $04, $0a, $0c
MatatileTile10:
	.db $00, $05, $0b, $0d
MatatileTile11:
	.db $00, $05, $0a, $0c
MatatileTile12:
	.db $00, $05, $0a, $0c
MatatileTile13:
	.db $00, $05, $0b, $0d
MatatileTile20:
	.db $00, $06, $06, $0e
MatatileTile21:
	.db $00, $06, $06, $0c
MatatileTile22:
	.db $00, $06, $06, $0c
MatatileTile23:
	.db $00, $06, $06, $0e
MatatileTile30:
	.db $00, $07, $07, $0f
MatatileTile31:
	.db $00, $07, $07, $0f
MatatileTile32:
	.db $00, $07, $07, $0f
MatatileTile33:
	.db $00, $07, $07, $0f

MatatileAttributes: ;4 palette indexes for each metatile
	.db %00000000 ;palettes for metatile $00
	.db %01010101 ;palettes for metatile $01
	.db %01010000 ;palettes for metatile $02
	.db %11000011 ;palettes for metatile $03

MetatileCollision: ;%00 = air, %01 = solid, %10 = water, %11 = hazard
	.db %00000000 ;metatile $00 is all air
	.db %01010101 ;metatile $01 is all solid
	.db %01010000 ;only the top of metatile $02 is solid
	.db %11110101 ;the top of metatile $03 hursts the player, the bottom is solid

LevelMap01: ;this small level is 16x8 metatiles (512x256 pixels) large
	.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.db $00, $00, $00, $00, $00, $02, $02, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $02, $02
	.db $00, $00, $00, $01, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.db $01, $01, $01, $01, $00, $00, $00, $00, $00, $01, $01, $01, $03, $03, $01, $01
	.db $01, $01, $01, $01, $00, $00, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01
See how everything connects? Whenever you want to check what is present at a certain map location, you convert the coordinates into an index which you can use to read a metatile index from the level map. Once you know the metatile's index, you can use it to get any information you want about it. If you are rendering the background, read from the MatatileTileXX tables and and write the tile indexes to the name tables, and the attributes to the attribute tables. If you're testing for collisions, read from the MetatileCollision table to know how the objects should react to the metatile.

Se how one thing points to another, that points to another and so on? This is how you manage to reuse your level data effectively compressing it to much less than it would be if you had raw name/attribute table data. The data above is just 200 bytes. If I were to represent that in uncompressed form, this 2-screen wide level would have been 2048 bytes large.

Anyway, this is not a lesson about compression (although the maps are indeed compressed), this is to show you one of the possible ways in which a level can be represented, and how you can access everything on it. Does this make sense to you?
Yes, tokumaru, that makes sense to me. :) Thank you! :D ...I need to redo my attribute table code... cause it would save so much space if keep accessing the pallet values through my sister's metatile definitions. That will be challenging to accomplish... but space will be saved. :) It'll be a learning experience! :D
Last edited by unregistered on Fri Sep 28, 2012 3:43 pm, edited 3 times in total.
3gengames
Formerly 65024U
Posts: 2284
Joined: Sat Mar 27, 2010 12:57 pm

Re: 8x16 sprite is really a 16x32 pixel image?

Post by 3gengames »

In your metatile stream, you can use the top 2 bits of the unused metatile data for the attribute info. :)
User avatar
tokumaru
Posts: 12536
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 8x16 sprite is really a 16x32 pixel image?

Post by tokumaru »

Mine was just a straightforward example of how a bare bones metatile system works. Details such as metatile dimensions, types of collision, palette selection, bit ordering, etc. should be defined by the programmer according to the needs of their game.
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: 8x16 sprite is really a 16x32 pixel image?

Post by unregistered »

Sorry for editing my post up there 3 times! :shock: It's how it should be? With all of tokumaru's post copied from the bottom of the previous page? It could be bad for slow internet connections... but it kind of helps someone from having too much trouble going to the bottom of the previous page... in my opinion. :oops: :) Thanks 3gengames. :) And thanks, tokumaru, that helps me too. :)
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: 8x16 sprite is really a 16x32 pixel image?

Post by unregistered »

Ok... so we represent a screen with 240 bytes if we have 16x16 metatiles... cause it takes less space! :) But to move a player around in a screen it would require 240 x 4 = 960 bytes? Right? So there needs to be a routine that would create the screen in 960 bytes of memory. Then collision detection would be possible for me, I guess. :? I cant overcome the want of 960 bytes per screen.
Shiru
Posts: 1161
Joined: Sat Jan 23, 2010 11:41 pm

Re: 8x16 sprite is really a 16x32 pixel image?

Post by Shiru »

You don't need to unpack metatile data of the whole screen to do collision, you can just 'unpack' certain tile in a given position when you need to check it.
User avatar
tokumaru
Posts: 12536
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 8x16 sprite is really a 16x32 pixel image?

Post by tokumaru »

unregistered wrote:But to move a player around in a screen it would require 240 x 4 = 960 bytes?
That depends on how large you want your maps to be... Ideally, you will not store the whole map in RAM unless you're using extra WRAM, because the measly 2KB the NES has isn't enough for large levels, specially considering that this memory has to be used for many other things besides levels. If you are mot applying any other kind of compression on top of the metatiles, the obvious choice would be to just leave the maps in ROM. If you do decide to keep the maps in RAM, you'll need at least 3 screens worth of data that you can reload as the screen scrolls.
Then collision detection would be possible for me, I guess. :?
As long as you can read data from the map, which you need to do for rendering the tiles to the name tables, for example, you can just as easily check for the solidity of the metatiles.
I cant overcome the want of 960 bytes per screen.
Again, they don't need to be in RAM. You can access level maps from ROM just fine.
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: 8x16 sprite is really a 16x32 pixel image?

Post by unregistered »

Shiru wrote:You don't need to unpack metatile data of the whole screen to do collision, you can just 'unpack' certain tile in a given position when you need to check it.
I'm so glad I asked that question! THANK YOU SHIRU! :D That's a great name for my method 'unpack'! It is going to replace my collisionU method! :)
tokumaru wrote:
unregistered wrote:Then collision detection would be possible for me, I guess. :?
As long as you can read data from the map, which you need to do for rendering the tiles to the name tables, for example, you can just as easily check for the solidity of the metatiles.
IEncredible!! Yes you've told me this before, but NOW I UNDERSTAND!!!!! :mrgreen:

I'm so excited... right now about trying collision without having to create a table/map correctly first!! :D It's so much good news and happiness for me, Thank you so much Shiru and tokumaru!!!! :D

---
3gengames, I understand now why you didn't understand my need for a method that would allow either an increment by 2 or an increment by 32. I don't need it anymore!! :lol: :D :) I don't have to create a perfect table copy first because I already have access to the info. :)

edit
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: 8x16 sprite is really a 16x32 pixel image?

Post by unregistered »

unregistered wrote:
Shiru wrote:You don't need to unpack metatile data of the whole screen to do collision, you can just 'unpack' certain tile in a given position when you need to check it.
I'm so glad I asked that question! THANK YOU SHIRU! :D That's a great name for my method 'unpack'! It is going to replace my collisionU method! :)
I'm working on GRAVITY now.... :) Our girl character falls down to the bottom of the screen. And keeps falling... through four screens... and then stops above the two metatile rows of solid ground. Why does she fall four screens before stopping?

Ok, she stays on the same screen... she falls from top to the bottom like I want it to, but she keeps falling through the top of the screen and goes down toward the bottom. Sorry, it's lunch time right now and so I hope to return to an answer... if not I'll figure it out myself, i think.

Code: Select all

0C3AF                           unpack:
0C3AF                           
0C3AF                           ;128  64  32  16   8   4   2   1   
0C3AF                           ;$80 $40 $20 $10 $08 $04 $02 $01
0C3AF A9 26                         lda #<MetatileRhombus
0C3B1 85 0C                     	sta rhombusCollision_low
0C3B3 A9 C8                     	lda #>MetatileRhombus
0C3B5 85 0D                     	sta rhombusCollision_high
0C3B7                           	
0C3B7 A9 05                     	lda #<MetatileCollision
0C3B9 85 1A                     	sta UCollision_low
0C3BB A9 C9                     	lda #>MetatileCollision
0C3BD 85 1B                     	sta UCollision_high
0C3BF                           	
0C3BF A5 04                     	lda oX ;getting ready to divide my X coordinate by 8
0C3C1 4A                        	lsr a
0C3C2 4A                        	lsr a
0C3C3 4A                        	lsr a
0C3C4 AA                        	tax
0C3C5                           	;sta tC+0
0C3C5                           	
0C3C5                           	
0C3C5 A5 05                     	lda oY
0C3C7 4A                        	lsr a ;divide my Y coordinate by (2 * 2 * 2) = 8
0C3C8 4A                        	lsr a ;<----------------------------*
0C3C9 4A                        	lsr a ;<--------------------------------*
0C3CA A8                        	tay
0C3CB 85 32                     	sta tC+1
0C3CD                           	
0C3CD                           	;x*16=row
0C3CD                           	;row+y=LINEAR_POSITION
0C3CD                           	
0C3CD 8A                        	txa
0C3CE 0A                        	asl a ;multiply x by (2 * 2 * 2 * 2) = 16
0C3CF 0A                        	asl a ;<----------------*
0C3D0 0A                        	asl a ;<--------------------*
0C3D1 0A                        	asl a ;<------------------------*
0C3D2 85 33                     	sta linearly
0C3D4                           	
0C3D4 18                        	clc
0C3D5 98                        	tya
0C3D6 65 33                         adc linearly	
0C3D8 85 33                     	sta linearly
0C3DA A8                        	tay
0C3DB                           	
0C3DB                           	;GRAVITY
0C3DB C6 31                     	dec tC
0C3DD 10 02                     	bpl +skip
0C3DF                           	
0C3DF B1 0C                     	lda (rhombusCollision_low), y
0C3E1                           +skip
0C3E1                                 ;if the metatile is not solid
0C3E1 0A                        	  asl a    ;<pushes bit #7 into carry.
0C3E2 90 09                     	  bcc +skip
0C3E4                           
0C3E4                                   ;then fall to the metatile below...
0C3E4                           
0C3E4 B1 1A                     	    lda (UCollision_low), y
0C3E6 A5 05                     	    lda oY
0C3E8 18                        	    clc 
0C3E9 69 08                     	    adc #8
0C3EB 85 05                     	    sta oY
0C3ED                           +skip  
0C3ED                           	
0C3ED E6 31                     	inc tC
0C3EF                           	
0C3EF 60                        rts ;end of unpack and end of daprg-collisionU
edit
User avatar
tokumaru
Posts: 12536
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 8x16 sprite is really a 16x32 pixel image?

Post by tokumaru »

There must be something wrong with the way you're reading the map... I can't make sense of the formula you're using to read metatiles... is it (x / 8) * 16 + (y / 8)? I have no idea what you're trying to do with that.

The exact formula obviously depends on how your maps are stored, but the typical way to convert pixel coordinates into map offsets is more like (y / MetatilesWidth) * NumberOfMetatilesPerRow + (x / MetatileHeight). So if your metatiles are 16x16 pixels the formula is (y / 16) * 16 + (x /16). If your metatiles are 32x32 pixels, it's (y / 32) * 8 + (x / 32). Once you have that offset, you can read a the index of the metatile at that location of the map and check it's collision information.

EDIT: Damn 8) smiley!
Last edited by tokumaru on Fri Oct 05, 2012 1:54 pm, edited 1 time in total.
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: 8x16 sprite is really a 16x32 pixel image?

Post by unregistered »

tokumaru wrote:There must be something wrong with the way you're reading the map... I can't make sense of the formula you're using to read metatiles... is it (x / 8) * 16 + (y / 8)? I have no idea what you're trying to do with that.
YEAY THANK YOU SO MUCH TOKUMARU! :D Ok... ... :) My idea was pulled out of this post:
Bregalad[color=#BFFF40], in a link from [url=http://forums.nesdev.com/viewtopic.php?p=83391#p83391]tokumaru's post on page 19[/url],[/color] wrote:You have a table arranged like this (I put random characters in it) :
A X U W O L T
S Z W K Y M Q
S U E U W P E
Q S G E I S L

Now you want to got the P that is in the third row and the sixth column.
You have two indexes, one is 2 (the count starts from zero, so the third row is number 2), and 5 (also start from zero).
The problem is that all the table is stored lineary in your ROM, so there it would be :
A X U W O L T S Z W K Y M Q S U E U W P E Q S G E I S L

The formula to know witch letter is the one you're looking for is :
There is 7 rows so, multiply the row index by 7. Scince the last letter of row 0 is at column 6, the first letter of the second row will be just after it, so effectively it's number 7.
Then, just add the column index to get the final index number.
In that case, 2*7=14; 14+5=19
Looking in the list above, the 20th letter is effectively P. It's number 19, but it's the 20th because the index starts from zero.
I guess the part that was kind of confusing to me was when Bregalad said, "There is 7 rows so, multiply the row index by 7." There aren't 7 rows!!! More like 3 rows I think. 4 rows numbered 0 through 3. So that was difficult for me. :?
tokumaru wrote:The exact formula obviously depends on how your maps are stored, but the typical way to convert pixel coordinates into map offsets is more like (y / MetatilesWidth) * NumberOfMetatilesPerRow + (x / MetatileHeight). So if your metatiles are 16x16 pixels the formula is (y / 16) * 16 + (x /16). If your metatiles are 32x32 pixels, it's (y / 32) * 8 + (x / 32). Once you have that offset, you can read a the index of the metatile at that location of the map and check it's collision information.
Yes, ok I'm going to try this out, tokumaru, right now... :)
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: 8x16 sprite is really a 16x32 pixel image?

Post by unregistered »

tokumaru wrote:The exact formula obviously depends on how your maps are stored, but the typical way to convert pixel coordinates into map offsets is more like (y / MetatilesWidth) * NumberOfMetatilesPerRow + (x / MetatileHeight). So if your metatiles are 16x16 pixels the formula is (y / 16) * 16 + (x /16).
Isn't that equal to y + (x/16) ? That seems crazy to me. :shock: :?

edit: that's quite a lot to think about... I have to go mow now. So I'll have to come back to this... :)
User avatar
tokumaru
Posts: 12536
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 8x16 sprite is really a 16x32 pixel image?

Post by tokumaru »

Bregalad's formula is correct, for when there are 7 items per row. The formula for reading data from a 2D array which is stored in memory linearly is always Y * ElementsPerRow + X, that doesn't change. But you also have to take into consideration that the base unit is the type of element you are accessing, in this case, metatiles. If you have pixel coordinates, you have to first convert them to metatile coordinates, hence the need to divide both X and Y by the dimensions of your metatiles before applying that formula.

Another thing that will affect how you apply the formula is how your levels are stored in RAM/ROM. If you store it screen by screen, then ElementsPerRow will always be the same, because the number of metatiles per screen doesn't change. If you don't divide your level in screens, then ElementsPerRow will be the length of the entire level, and it will vary from level to level. IMO, things are easier if you divide the levels into screens, because the metatile offsets will always fit in 8 bits, and the multiplications/divisions can be easily done with shifts.

Here comes another (untested) example of data arrangement (and how to access it):

Code: Select all

LevelMap:

	;first screen:
	.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	;(another 12 rows here)
	.db $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10

	;second screen:
	.db $00, $00, $00, $00, $00, $10, $10, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.db $00, $00, $00, $00, $00, $10, $10, $00, $00, $00, $00, $00, $00, $00, $00, $00
	;(another 12 rows here)
	.db $10, $10, $20, $20, $20, $20, $20, $20, $20, $21, $22, $23, $00, $00, $00, $00

	;third screen:
	.db $22, $00, $22, $00, $22, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.db $22, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	;(another 12 rows here)
	.db $20, $20, $20, $20, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10, $10

	;(keep going for as many screens as you want)

;Reads the index of the metatile at position (PointX, PointY) in the level map
ReadMetatile:

	;get the index of the screen
	ldy PointX+1

	;multiply it by 240 (using a look-up table) and add it to the base
	;address of the level to create a pointer to the screen we need
	clc
	lda ScreenOffsetLo, y
	adc LevelMap+0
	sta ScreenPointer+0
	lda ScreenOffsetHi, y
	adc LevelMap+1
	sta ScreenPointer+1

	;calculate the index of the metatile: (Y / 16) * 16 + (X / 16)
	lda PointX+0
	lsr
	lsr
	lsr
	lsr
	sta Temp
	lda PointY+0
	and #%11110000
	ora Temp
	tay

	;read the metatile
	lda (ScreenPointer), y

	;return
	rts
Now you can do whatever you want with that information. Want to know if that metatile is solid? Do something like this:

Code: Select all

	tax
	lda MetatileCollision, x
	and #BITTHATINDICATESWHETHERAMETATILEISSOLID
	beq +NotSolid
Want to know what is the top left tile of that metatile?

Code: Select all

	tax
	lda MetatileTopLeft, x
User avatar
tokumaru
Posts: 12536
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 8x16 sprite is really a 16x32 pixel image?

Post by tokumaru »

unregistered wrote:
(y / 16) * 16 + (x /16).
Isn't that equal to y + (x/16) ? That seems crazy to me.
Not really, because the lower 4 bits are cleared in the process of shifting right and back left. So as a shortcut you can just do AND #%11110000 instead of LSR LSR LSR LSR ASL ASL ASL ASL. It works the same. Stop thinking about math as you learned in school for a minute and try to see it as the 6502 sees it (which is much simpler than what you learned in school, IMO).
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: 8x16 sprite is really a 16x32 pixel image?

Post by unregistered »

tokumaru wrote:
unregistered wrote:
(y / 16) * 16 + (x /16).
Isn't that equal to y + (x/16) ? That seems crazy to me.
Not really, because the lower 4 bits are cleared in the process of shifting right and back left. So as a shortcut you can just do AND #%11110000 instead of LSR LSR LSR LSR ASL ASL ASL ASL. It works the same. Stop thinking about math as you learned in school for a minute and try to see it as the 6502 sees it (which is much simpler than what you learned in school, IMO).
WOW!!!!! :!: :!: :!: :!: :!: :idea: :idea: :idea: :idea: :idea: :D
AGah... I've read through these posts so many times... :D it's bed time... will finish this post tomorrow....
Gg
oodnight yall. :)

edited once more: aaaah... tokumaru, thank you very very much! :)

edits #2: Good morning! :) Goodness... i was really sleepy... sorry. THANK YOU SO VERY VERY VERY MUCH TOKUMARU!! :D
Post Reply