I'm currently attempting to port a game from a very silly game jam to pico-8, since I feel like it'd fit better for the most part. However, while debugging I've discovered that all my images are being handled very slightly wrong.
The images in question are black and white, using every 4 bits to store 4 pixels, and they're loaded into data carts by copying strings of hexadecimal into pico-8 (the idea being to simplify the entire process by focusing on 1 hexadecimal digit as the base block of data). To then reduce the data size, any characters that are "0" or "F" (all white or a black pixels) is encoded with a runtime length using the next 8 bits.
The problem is that both the parts that draw the images and the parts that sample the images for use in collision detection are shifted 4 pixels to the left (wrapping).
This is the function that decodes them:
function image_load(addr, len) local im = {} local s = "" for i=addr,addr+len-1 do s=s..sub(tostr(@i,0x1),5,6) end local i=1 local step = 1>>16 local n=0 while i<#s do local v=tonum(sub(s,i,i),0x1) if v==0 then i+=1 local r=tonum(sub(s,i,i),0x1)*16 i+=1 r+=tonum(sub(s,i,i),0x1) for ii=1,r do n+=step*4 end elseif v==15 then i+=1 local r=tonum(sub(s,i,i),0x1)*16 i+=1 r+=tonum(sub(s,i,i),0x1) for ii=1,r do im[n]=true n+=step im[n]=true n+=step im[n]=true n+=step im[n]=true n+=step end else local b=1 for ii=1,4 do if v&b>0 then im[n] = true end n+=step b=b<<1 end end i+=1 end return im end |
The return value is thus a 1d array of true/nil values. nil is used for white pixels to save memory. The step of 1>>16
is used because the main map image used for most of the gameplay is too big to index with 16-bits, so 32 bits are used instead. The reason I repeated the interpretation of all black pixels instead of another loop is because this function is used in spots where speed somewhat matters, and I've noticed too many nested loops can quickly cause slowdown even with lua's optimization of for loops.
This is the simplest of the functions that uses the images:
function map_check(x,y) return currmap[(y>>16)*currmap.w+(x>>16)] end |
This is used for collision detection in the parts of the game where most of the black pixels will be walls (with the ones that aren't being stored separately rather than being part of the map).
I've spent quite a bit of time staring at this code, and I can't tell why the pixel data would be shifted 4 pixels. I've checked the cart data in the spots where the data is stored, and it matches original data perfectly. Can anyone else tell what's going wrong?
To be clear, I'm asking because I might use this again in the future, so I'd like to know what the error is. I can easily work around this for this game but just adding +(4>>16)
to all the indices that sample the images, but that's not guaranteed to work in a future game.
cannot run code atm but exploding bits into a true table is certainly not saving memory!
why not use bit testing to detect pixels?
you can pack 32bits per table entry.
have you tried printing values ? not sure to understand where are the bogus value in above code.
do you sample data showing off the bug?
@freds72
I considered using bits, but I figured it'd be better to try solutions with simpler code first. pico-8 is so memory sparse compared to anything it might run on that it feels pointless to save more memory than is needed to satisfy the requirements.
Also, before criticizing, perhaps read more carefully. I said I was saving memory by using nil. I initially filled the table with true/false. Replacing false with nil reduced the memory usage down to about 1/3 in the case of the largest image.
it’s not a critique, and I understand that pico has enough memory to use simple approach first - wanted to say such options exists (as not always obvious)
test data to help would be good tho!
Providing test data is a bit awkward, given that it's meant to read it from the cartridge. You could try this putting this at the beginning of the gfx data though:
5013103410741074107410741074107410c307317e10f31007310f7110820f1110420f1110c20f1110c20f1110c20f7110c20f0221fc2010820f1210f2200121 0f1210e20f7110e20f7110f2200711f83010e10f03110f04110f04110f14008f0f14008ffd3001f0f84010800f0401fc4010c00f1310c00f7210c10f7210c10f 7210c10f7210c10f7210c10f0311fc3010e10f03110e |
in that case the address and len would be 0 and 150. then the width and height would be 84 and 48. If it shows up correctly, it should be a picture of some silhouette of people. (the odd size is because the game is a port from a game jam that was about mimicking a phone)
not sure what I am looking at, did a fix in map_check to switch x/y
Ah...I guess I should have just posted the png version.
It's this:
I'm used to just assuming that things listed in the same order will be interpreted as such, but reading my previous post again I can see why w and h of 84 and 48 could be confusing when I also say it's mimicking a phone. The phone in question wasn't a smart phone.
The single pixel gaps in your cartridge are correct. The image is rotated, but that's not really relevant. However, the pixels being shifted and wrapping around is the issue I've been trying to figure out.
As a side note, this code (y*currmap.w+x)>>>16
is not equivalent to this code (y>>16)*w + (x>>16)
. They both work for images of a reasonable size, but the former completely misses the point of using 32 bit indices. If y
gets multiplied by currmap.w
before being shifted, then any overflow will still cause a massive difference which will still be there after the shifting.
can you post the target image? that would help!!
my best guess is that encoding is incorrect - I got something by changing indices for v=0 and v=15
(and yes, I am perfectly aware that 128*128 will overflow)
[Please log in to post a comment]