It is currently Sun Nov 18, 2018 12:06 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 26 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Mon Apr 23, 2018 8:44 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10974
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Mon Apr 23, 2018 9:38 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6956
Location: Canada
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.

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.


Top
 Profile  
 
PostPosted: Mon Apr 23, 2018 10:32 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10974
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Tue Apr 24, 2018 11:59 pm 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 38
Location: Finland
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.


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.


Top
 Profile  
 
PostPosted: Sat Apr 28, 2018 12:16 pm 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 38
Location: Finland
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?


Top
 Profile  
 
PostPosted: Sat Apr 28, 2018 1:42 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1251
Arranged in smallest piece to largest piece:

Code:
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

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.
Code:
;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

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. :wink: Fixed.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Sun Apr 29, 2018 3:07 am 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 38
Location: Finland
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:

Code:
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


Top
 Profile  
 
PostPosted: Sun Apr 29, 2018 11:17 am 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1251
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:
Code:
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.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Mon Apr 30, 2018 8:44 am 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 940
Location: cypress, texas
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)
Small note: if someone is using asm6 they can just type:
Code:
rowlow:
.dl row0, row1 ;dl writes the low byte of an address
rowhigh:
.dh row0, row1 ;dh writes the high byte of an address


That's really convenient and cool for me because we just have to type out the .dl line and then copy/paste it (and just change the new line's l to a h). :) Thank you Loopy! :mrgreen: :D


Top
 Profile  
 
PostPosted: Mon Apr 30, 2018 12:06 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10974
Location: Rio de Janeiro - Brazil
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:
rows .equ row0, row1, row2

rowlow:
  .dl rows
rowhigh:
  .dh rows


Top
 Profile  
 
PostPosted: Mon Apr 30, 2018 2:04 pm 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 940
Location: cypress, texas
^that's really cool tokumaru! Thank you for that idea; it works wonderfully! :mrgreen: :D

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. :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 26 posts ]  Go to page Previous  1, 2

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group