My question(s) is, what is the best way to store level data and/or do you need a special PICO-8 level editor to do that? I am asking because I'm currently making a game with more levels than one map can handle.
Hmm well there's lots of ways to go about it since you could use any part of memory or the code space to store arbitrary data.. but I think a method that could work that i've tried (I'm still noobish though.) is to use the map data to encode more than 1 tile per. so like you read the map data as 1 tile per 2 and then you use all of the tiles to store half of the possible tiles in 2x less space etc. but I'm sure there's better ways to go about it and ya since the data is just a list of numbers it would make sense to make a tool (but not needed.) to visually show the tiles and then output the mapdata.
Also PS: some people use the spritesheet pixels to encode mapdata, this is the same thing as the memory thing I said but I guess this way you could use sget and such which might be nicer than the alternative of peeking.
Thanks for the info, even though I don't know how to do that, I was originally thinking about using tables since I think have more than enough tokens (5008/8192). An example is shown below
leveldata1 = {1,4,2,3} --insert level data into this table leveldrawtile=0 for leveldrawnum in all(leveldata1) do poke(leveldrawtile+8192,leveldrawnum) --draw the tiles to the map leveldrawtile+=1 map() end |
Make sure you add these sprites in
[0x0] | |
[0x0] | |
[0x0] | |
[0x0] | |
You can save a lot of tokens by replacing the first line with
leveldata1 = split"1,4,2,3"
How do you read the memory though (I already have carts that have the level data)? I've tried many attempts at using PICO-8's code to get the data for me but nothing is working.
Pico’s code?
Pico has a memory and you can use peek() and poke() and some others like peek2/4() and memcpy to manip it.
I've been trying with normal peek but it's not working.
--an example of an attempt that did not work anumber=0x1fff while anumber<0x2fff do anumber+=1 avalue=peek(anumber,8192) add(mapdata,avalue) end --returns 0 as table even with full mapdata in cart ¯\_ (ツ)_/¯ |
peek only takes 1 argument and also it works for me.
cls(1) palt(0,false) pal(0,7) for i=0,130 do poke(0x2000+i,1) end map() ?peek(0x2040),13 |
It's not working for me.
cls(1) palt(0,false) pal(0,7) map() mapthings=peek(0x2040) printh(mapthings,'@clip') --still copies 0 |
At least if we do figure this out then both of us can use it @SmellyFishstiks
I've actually just made a set of functions for compressing both sprite and map data to strings. The decompression function is very small at just 96 tokens, and from comparisons I've done, it's usually fairly close in compression ratio to Zep's PX9 system, which takes up several hundred more bytes in size and uses 254 tokens. It seems to compress dense maps around 2:1, and more sparse ones can compress up to around 6:1 or 7:1.
The functions should be pretty straightforward, you'll just need to make sure you're in puny font mode before you paste the strings the compressor makes, or things won't work right. Also, this will let you use sprites 0-239 in your map, or all but the last row of the spritesheet.
--Specify mget for map or sget for spritesheet, and width and height --of area to compress. Right now it's set up to have the top left --corner of the map or spritesheet as its starting point. function compress(get,w,h) s=chr(w+96) local function fix(v) if(v==18) v=-2 if(v==76) v=-1 return v end local n,len,k=0,1 while n<w*h do local val,nxtval=get(n%w,n\w),get((n+1)%w,(n+1)\w) if nxtval==val and len<159 then len+=1 else if len<=14 and get==sget then s..=chr(fix(val+len*16)+16) k=1 else s..=chr(fix(val)+16)..chr(len+96) k=2 end len=1 end n+=1 end if(k==1) s..="-" printh(s,'@clip') end --specify string name, mset or sset for map or sprite, and location in --map or spritesheet where you want data decompressed to function decompress(s,set,x,y) local i,n,w=2,0,ord(s,1)-96 while i<#s do local v,l=ord(s,i)-16,ord(s,i+1)-96 if(v<0) v=134+v*58 if(set!=mset and v>15) l=v\16 i-=1 for j=1,l do set(n%w+x,n\w+y,v) n+=1 end i+=2 end end |
O_o I now realize that I'm a total noob at PICO-8. Also thanks for the info @JadeLombax!
You're welcome, just let me know if you have any problems using the functions.
Uhh, wait. What is puny font mode and how do you enable it?
(Sorry, specified coding isn't my thing.)
Oh, it's a Pico-8 thing. To enable small capital letter characters to show up in the code editor, you have to press Ctrl+p. If you try to run the decompression function without doing this, large letters will be substituted in the data strings and the sprite or map data will get scrambled.
It's a bit of a pain, but doing things this way let me store almost a whole byte per character and keep the decompression function small and simple.
Cool, also can you explain what each main variable is (the program keeps asking me to add values to nil variables).
Also @JadeLombax, you're like a fountain of knowledge for me. I probably wouldn't have figured out the compression for probably a couple of years! I will make sure that you get credited when I use this in Super Mario Remix (see here: https://www.lexaloffle.com/bbs/?tid=39432). Furthermore, is there any way I can return the favor (sprites or music)?
For the compression function:
get = sget or mget (reads sprite or map data)
w = width of sprite pixels or map tiles to compress (starting at 0)
h = height of sprite pixels of map tiles to compress (starting at 0)
For the decompression function:
s = name of data string to decompress
set = sget or mget (writes sprite or map data)
x = starting x position in in spritesheet or map to decompress to
y= starting y position in in spritesheet or map to decompress to
So to compress the whole 32 screens of the map to a string, you'd enter
compress(mget,128,64) |
Then press Ctrl+P to enter Puny font mode, paste the string, name it (such as mapdata=".....")
and enter the following to decompress it
decompress(mapdata,mset,0,0) |
A HUGE thanks! This will really help me on my journey in game design!
That's alright, I've just noticed you on the boards trying to figure out how to fit more levels in a cart for a while, and my metatile map editor, which you asked about a while ago, has ended up taking longer to finish than expected. I've got it working just fine this point, but I just finished improving the map and sprite compressor here so it would be more capable and versatile, and I'm working on a tutorial to teach people how to use the system, since it's a bit complex.
I think the compression function could be useful to people who want a bit more space but aren't making an NES-length game, or who aren't necessarily ready to switch to a new map editor.
All that said, I haven't really made any music yet, so I might ask you about that sometime ;).
... ok well I'm just going to ignore that and be on my way, poke and peek can take a sec to understand.
If you need a way to make maps in the meantime, you could just create a separate a cart for each map, hand it just a simple line of code to call that compress() function, run it, and then paste the results into your main cart. That way you can still use the map editor as normal for actually creating the maps. As a side benefit, that also would make it less likely that you lose a map because of overwriting or because of a screw-up while using JadeLombax's system.
It’s even possible to have these separate level builder carts write their result not to clipboard but to a file (printh(string, "level1.p8l"
), then use #include level1.p8l
to get the data automatically!
@kimiyoribaka I'm already doing that.
@merwok That would be a good idea if my computer hardware was lacking but it isn't and I'm not too keen on creating a lot of almost empty carts for this project.
JadeLombax published a metatile editor! See their profile for the link.
I think this video by MorphCat games could be really relevant to Pico8 development.
https://www.youtube.com/watch?v=ZWQ0591PAxM
It's not about Pico8, it's about storing levels on a NES cartridge (with no bank-swapping.) But I think a lot of the same techniques could apply.
(Edit : They start talking about compressing levels at about the 6:15 mark.)
Using the extra bit to control the offset is pretty clever.
For the platformer I'm working on, I'm using a slightly simplified version of Metroid's structure system. In my version, a structure is a row length and list of tiles. For example, "3,4,5,5,4,5" would have a row length of 3 and look like this:
455 45 |
The tiles are placed in order until the row length is reached, at which point the next tiles placed drop down to the next row, etc. The same tiles with a row length of 1 instead of 3 ("1,4,5,5,4,5") would look like this:
4 5 5 4 5 |
These are basically metatiles in NES terms, but a bit more flexible because they can easily create non-square structures while using relatively little storage.
[Please log in to post a comment]