SusiKette's project help thread
Moderator: Moderators
Re: SusiKette's project help thread
Compressing to rows is cool because you can create a "library" of rows and reuse them throughout the levels, which will be fairly compressed when each row of 16 metatiles is replaced by a pointer to a compressed row. About 30 bytes per screen isn't bad.
If you use 256 or less unique rows per level you can cut the space they take by half, since you can use byte-sized indices to lookup the pointers to the compressed row data.
If you use 256 or less unique rows per level you can cut the space they take by half, since you can use byte-sized indices to lookup the pointers to the compressed row data.
- rainwarrior
- Posts: 8732
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: SusiKette's project help thread
Well, "metatiles" can be any shape, really. A 16x1 row can be effective, but so could, e.g. a 4x4 rectangle. Depends on the content of your game and what aligned shapes are most reusable.tokumaru wrote:Compressing to rows is cool because you can create a "library" of rows and reuse them throughout the levels, which will be fairly compressed when each row of 16 metatiles is replaced by a pointer to a compressed row. About 30 bytes per screen isn't bad.
If you use 256 or less unique rows per level you can cut the space they take by half, since you can use byte-sized indices to lookup the pointers to the compressed row data.
Re: SusiKette's project help thread
Rows are very good for vertical scrolling, so that's a plus. I do appreciate the use of multiple levels of metatiles too (in this case, 256x16 rows made of 16x16 blocks), it can really help improve compression without increasing the complexity too much.
Re: SusiKette's project help thread
That would be really nice (and maybe a bit easier too), however it entirely depends on the stage's design. Maybe I should take care of that first.tokumaru wrote:If you use 256 or less unique rows per level you can cut the space they take by half, since you can use byte-sized indices to lookup the pointers to the compressed row data.
Avatar is pixel art of Noah Prime from Astral Chain
Re: SusiKette's project help thread
I've been trying to figure out a way to do the level data decoding but I haven't really been making much progress at all and I'm starting to feel like I need a bit more help on it. Maybe some example code or something?
Avatar is pixel art of Noah Prime from Astral Chain
Re: SusiKette's project help thread
Arranged in smallest piece to largest piece:
To load it, you go from largest to smallest.
The scroll will tell you which row in level to use.
The address from that row will give you access to the metatile indices that make it up.
The metatiles indices will give you access to tile numbers and attributes.
Two things are not shown.
One, how to set up attributes.
Two, how to get the proper nametable address for the row. (But hint, the scroll position also tells you this.)
The code is untested, but even if it's subtly broken it should at least give you an idea of how this works. Just large pieces made of small pieces. You use what you know to get the position in the largest piece, then work down using the references.
Edit: Hah. Well there was one mistake. I copied the top twice instead of the top and the bottom. Fixed now. I'll keep looking.
Edit2: Standard warning. You can't write to $2007 whenever, it has to be when rendering is disabled or early in your NMI. One way to make the above code work is to just move the part that reads from metatiledump and stores to $2007 to your NMI.
Edit3: Found a missing '#' symbol too. Fixed.
Code: Select all
metatiletopleft:;Tile index of the top left corner of each 16x16 metatile
.db $00, $01, $02, $03
metatiletopright:;Tile index of the top right corner of each 16x16 metatile
.db $04, $05, $06, $07
metatilebottomleft:;Tile index of the bottom left corner of each 16x16 metatile
.db $08, $09, $0A, $0B
metatilebottomright:;Tile index of the bottom right corner of each 16x16 metatile
.db $0C, $0D, $0E, $0F
metatilepalette:;palette of each 16x16 metatile
.db 0, 1, 2, 3
;A 16x16 metatile is made of 8x8 tiles
row 0:;A row contains 16x16 tile indices.
.db 0, 1, 2, 3, 0, 3, 2, 1, 2, 0, 1, 3, 2, 0, 3, 1
row1:
.db 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3
;A row is made of 16x16 metatiles
rowlow:;Half the address to access a specific row index
.db low(row0);If you're not using NESASM, you'll want .db <row0 instead
.db low(row1)
rowhigh:;The other half
.db high(row0);If you're not using NESASM, you'll want .db >row0 instead
.db high(row1)
;A list of rows is made up of references to rows
level0:
.db 0, 0, 1, 0, 1
;A level is made of row numbers
The scroll will tell you which row in level to use.
The address from that row will give you access to the metatile indices that make it up.
The metatiles indices will give you access to tile numbers and attributes.
Code: Select all
;The scroll position will tell us what which row we're on.
;We divide it by 16 to get the row we need to draw
lda scrollylow
sta temp1
lda scrollyhigh
lsr a;shift the high byte into the carry
ror temp1;Shift the carry into the high byte of a copy of the low position of the scroll
;We have just done a 16bit divide by 2.
lsr a
ror temp1
;now we've divided by 4
lsr a
ror temp1
;now we've divided by 8
lsr a
ror temp1
;now we've divided by 16
ldy temp1;This is the row we want to load.
lda level0,y;We get which row we need to draw at that position
tay
lda rowlow,y;We get the low byte of where that row is
sta temp1
lda rowhigh,y;We get the high byte of where that row is.
sta temp2
ldy #0
metatileloop:
lda [temp1],y;If you're not using NESASM, you'll want lda (temp1),y instead
;The above loads the first byte of the given address. So, the leftmost metatile. Let's just copy it in a loop for now
sta metatiledump,y
iny
cpy #16
bne metatileloop
ldy #0;Now we write the left and right side of the top of the metatile alternating
metadrawloop:
lda metatiledump,y
tax;Get the index of the metatile so we can read the table
lda metatiletopleft,x
sta $2007
lda metatiletopright,x
sta $2007
iny
cpy #16
bne metadrawloop
;Top half is copied. Now the bottom half
ldy #0
metadrawloop2:
lda metatiledump,y
tax
lda metatilebottomleft,x
sta $2007
lda metatilebottomright,x
sta $2007
iny
cpy #16
bne metadrawloop2
;Done
One, how to set up attributes.
Two, how to get the proper nametable address for the row. (But hint, the scroll position also tells you this.)
The code is untested, but even if it's subtly broken it should at least give you an idea of how this works. Just large pieces made of small pieces. You use what you know to get the position in the largest piece, then work down using the references.
Edit: Hah. Well there was one mistake. I copied the top twice instead of the top and the bottom. Fixed now. I'll keep looking.
Edit2: Standard warning. You can't write to $2007 whenever, it has to be when rendering is disabled or early in your NMI. One way to make the above code work is to just move the part that reads from metatiledump and stores to $2007 to your NMI.
Edit3: Found a missing '#' symbol too. Fixed.
Re: SusiKette's project help thread
What about screens? I'm using them as a part of the compression as well. It'd be easier to loop sections of the level that way when you can just repeat the same screen multiple times.
EDIT: As a reference my level data looks like this:
EDIT: As a reference my level data looks like this:
Code: Select all
MetatileList:
T00: ; blank tile
.db $00,$00,$00,$00
T01: ; test tile
.db $1D,$0E,$1C,$1D
T02: ; question mark tile
.db $26,$26,$26,$26
T03: ; score part 1
.db $90,$91,$00,$00
T04: ; score part 2
.db $92,$93,$00,$00
T05: ; lives part 1
.db $94,$95,$00,$00
T06: ; lives part 2
.db $96,$97,$00,$00
T07: ; bombs part 1
.db $98,$99,$00,$00
T08: ; bombs part 2
.db $9A,$9B,$00,$00
RowList:
R00: ; blank row
.dw T00,T00,T00,T00,T00,T00,T00,T00,T00,T00,T00,T00,T00,T00,T00,T00
R01: ; test row
.dw T01,T01,T01,T01,T01,T01,T01,T01,T01,T01,T01,T01,T01,T01,T01,T01
R02: ; question mark row
.dw T02,T02,T02,T02,T02,T02,T02,T02,T02,T02,T02,T02,T02,T02,T02,T02
R03: ; status row
.dw T00,T00,T03,T04,T00,T00,T00,T05,T06,T00,T00,T00,T07,T08,T00,T00
ScreenList:
S00: ; test screen 1
.dw R00,R01,R00,R01,R00,R01,R00,R01,R00,R01,R00,R01,R00,R01,R00
S01: ; test screen 2
.dw R02,R02,R02,R02,R02,R02,R02,R00,R02,R02,R02,R02,R02,R02,R02
S02: ; test screen 3
.dw R00,R00,R03,R00,R00,R03,R00,R00,R03,R00,R00,R03,R00,R00,R03
LevelList:
L01: ; test level
.dw S00,S01,S02,S00,S01,S02
Avatar is pixel art of Noah Prime from Astral Chain
Re: SusiKette's project help thread
If you want screens, you add screens.
In my example, levels were made of rows. To load the rows, you found out where you were in the level, then loaded that row.
If you add screens, then levels are made of screens made of rows.
So you find where you are in the level, to get which screen, then find where you are in the screen to get that row.
Edit:
In my example, levels were made of rows. To load the rows, you found out where you were in the level, then loaded that row.
If you add screens, then levels are made of screens made of rows.
So you find where you are in the level, to get which screen, then find where you are in the screen to get that row.
Edit:
Code: Select all
lda scrollyhigh
asl a;Since you're storing both bytes of the screen together in your Levels. Multiply by 2 to get the offset for the start
tay;of the address.
lda L01,y;Get the low byte of the screen you're on
sta templo
iny
lda L01,y;Get the high byte of the screen you're on
sta temphi
lda scrollylow;Divide by 16 like before, except we don't need to do 16 bit because we already know what screen we're one
lsr a
lsr a
lsr a
lsr a
tay
lda (templo),y;Get the row.
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
Re: SusiKette's project help thread
Small note: if someone is using asm6 they can just type:Kasumi wrote: ;A row is made of 16x16 metatiles
rowlow:;Half the address to access a specific row index
.db low(row0);If you're not using NESASM, you'll want .db <row0 instead
.db low(row1)
rowhigh:;The other half
.db high(row0);If you're not using NESASM, you'll want .db >row0 instead
.db high(row1)
Code: Select all
rowlow:
.dl row0, row1 ;dl writes the low byte of an address
rowhigh:
.dh row0, row1 ;dh writes the high byte of an address
Re: SusiKette's project help thread
I THINK you can use .EQU to create a symbol containing all the elements, so you don't have to copy & paste the list every time you change it. This might work (haven't tested it):
Code: Select all
rows .equ row0, row1, row2
rowlow:
.dl rows
rowhigh:
.dh rows
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
Re: SusiKette's project help thread
^that's really cool tokumaru! Thank you for that idea; it works wonderfully!
edit: caution: this will make changing the file easier, like tokumaru said, but the characters used in the asm6 .lst file will be a little more than 1.5 times as many as needed for the copy/paste method. But, if filesize isn't an issue, go for it. Small changs to identical lines to avoid copy/paste isn't much of a problem for me.
edit2: When you consider that the section of each asm file you use this .equ optimization in will be about half as many characters in size, you actually save space, I think. 8 characters x 1.5 x 0.5 == 6 characters. Each of asm6's .lst lines has an added 32 characters so using .equ is a pretty even way of having less to change. Thanks again tokumaru!
final edit: Each line of characters saved would have to be at least 128 characters in length, I think, to break even with the 32 added characters appended to the front of each line in an asm6 .lst file.
edit: caution: this will make changing the file easier, like tokumaru said, but the characters used in the asm6 .lst file will be a little more than 1.5 times as many as needed for the copy/paste method. But, if filesize isn't an issue, go for it. Small changs to identical lines to avoid copy/paste isn't much of a problem for me.
edit2: When you consider that the section of each asm file you use this .equ optimization in will be about half as many characters in size, you actually save space, I think. 8 characters x 1.5 x 0.5 == 6 characters. Each of asm6's .lst lines has an added 32 characters so using .equ is a pretty even way of having less to change. Thanks again tokumaru!
final edit: Each line of characters saved would have to be at least 128 characters in length, I think, to break even with the 32 added characters appended to the front of each line in an asm6 .lst file.