Here's my first game for Pico-8 and it's a Pole Position/Enduro/OutRun derivative. You might have already played it on my itch.io page.
- Day and night
- Three landscapes
- Obstacles and jumps
- A strict time limit
- See how well you did in the course map overview
- Really messy source code
- Terrible sound
I wrote something about it in my blog and if you have something to ask about it I will gladly answer here.
Edit: I'd like to mention I used the ord() function found here, it was a lifesaver.
Update: Now saves high score only when returning to title screen, would continuously write on disk otherwise (due to changes in 0.1.6)
This is terrific! It's really polished and you packed in an impressive amount of visual variety. The little touches make a big difference, like the custom fonts and the scanline dithering. (And the football. Of course.)
Thanks too for the detailed writeup, those were great insights into the design. Interesting what you noted about pico-8 calculating sprite rendering time based on area, could you expand on that? Is that just a consequence of the internal scaling operations, or does there seem to be some arbitrary delay being applied there?
I'm glad that the article had some value. Thank you. :)
Well, I might be talking out of my ass here, but different API functions have different "execution time" and for sspr() it varies based on the draw area. IIRC spr() has a bit faster exec time for similarly sized sprites, not sure how much of that difference comes from having to use more Lua tokens for the extra arguments and so on and how much comes from the actual sspr() call itself.
I doubt sspr() and spr() do anything differently internally (i.e. an additional layer of Lua code doing the scaling and drawing), I would guess it's just one SDL_RenderCopy() call in both cases plus a delay based on how many pixels were drawn.
But that's just my guess, the truth might be more complex.
I'm just speculating here myself, but I'm doubtful that the pico-8 draw APIs use SDL operations directly - both because they need to handle index-based transparency and recoloring, and because pico-8 exposes the framebuffer as directly addressable memory in its own tightly-packed indexed format.
That would otherwise involve frequent translation to and from hardware texture formats - which seems like a lot of decidedly un-retro hassle for something that would have more 'authentic' performance quirks if it were handled in software, with a final translation from framebuffer memory into an SDL texture for rendering to the screen.
My own guess would be that pico-8 does API draws in software and imposes a delay on each framebuffer pixel write to make larger coverage take longer. There's bound to be more going on than just that though, since it's still slower to draw a 16x16 sprite than to draw a 16x16 color rect.
Yes I did play it on itch.io and thought it was very slick!
Actually the part I'm most curious about is the engine sound - how does it scale up and down dynamically with the speed of the car? I expected you to have a different sound effect for each pitch of the engine but I didn't find any such sound effects... so is it possible to send data directly to the Pico-8 sound device??
You can poke the memory at 0x3200-0x42ff to modify the parameter of a sound effect. You can see this in the following lines:
poke(0x3200+68*2,tach) poke(0x3200+68*2+1,3+0x60) poke(0x3200+68*2+2,tach/1.5) poke(0x3200+68*2+3,1+0x60)
This modifies the sound effect at index 2 according to the speed of the car.
There are indeed 'artificial' costs assigned to the internal draw operations. For spr() it's around 1 Lua vm instruction per drawn pixel, and sspr() it's 2.
@kometbomb thanks for the excellent write-up. Apart from being an entertaining read, it also revealed a bug in the draw cost calculations that is now fixed in 0.1.6 -- as you pointed out, sspr() wasn't properly taking clipping into account, so drawing would cost the same even if they were mostly clipped.
@Zep: Nice to hear that I could help improving this awesome toy. :)
My frequency setting code actually overwrites data in the instrument (i.e. other than the frequency/note param) because the different parameters (frequency, waveform etc.) share the same bytes. I ended up just fiddling the numbers until it sounds at least somewhat like an engine.
I vary the frequency so that it's basically doing a really fast arpeggio, as you can see the above code. The 1.5 multiplier is there so that both notes don't change at the same time when tach goes up a notch, giving an illusion of more than 32(?) different frequencies.
Log in to post a comment