Archive:

Subscribe:Atom Feed

"Summer Side Quest"

Posted: 08 August, 2025

I’ve been in Finland for the last couple of weeks, having a bit of a summer holiday, and spending a few days at Assembly, in Pasila.

If you’ve not heard of Assembly, it’s an annual gaming / e-sport / demoscene event, that started in the early 90s. I’ve been going since 2011, as it’s a great way to hook up with game industry folks (there’re a lot of overlap with the demoscene), and catch up with chums. I missed last year (partner was ill), so it was great to be back in the hall, catching the memes and watching the demos on the big screen.

As counter-intuitive as it might seem, being in the hall with several thousand other people, in a massive LAN party, is a scarily productive environment. I love coding in there, so I normally spend my time doing something fun. A bit of a side-quest, so to speak. This year, I fired up the Playdate SDK and had a mess around.

Yup, it’s a spin (ha!) on Nebulus.

Rather than hand-animate the tower’s rotation, in something like Aesprite, I’ve done it procedurally.

The brick pattern is 16x16 pixels, so I need to generate 16 frames of animation to scroll it fully, from left to right. But… the tower is 200 pixels across, and the pattern needs to repeat, with some curvature, across those 200 pixels, which is where high-school trigonometry comes in: cosine

We need to make a choice about how many pixels map to one degree of rotation around the tower – which I just eyeballed until I got something I liked – and once that’s done, we can use the cosine of the angle to get an index into the source pattern, (and the destination frame) and then copy a line of pixels from one to the other. There’s one downside to this, though: unless you take tiny steps, you might leave some holes in the destination frames, which complicates the maths a bit. To keep things simple, I do it in passes:

  1. From the middle of the animation frame to the left edge
  2. From the middle of the animation frame to the right edge
  3. From middle to left, filling any holes with the pixel data from the previous position
  4. Same from middle to right

The Playdate has a 1 bit screen, meaning pixels are on or off. It’s a fancy Kindle, basically, so there’s no shading unless you do some sort of dithering. The tower looked nice without it, but it lacked depth. My first attempt to fix this was by baking the dithering into the pre-calculated animation frames, but it didn’t look all that great, so I borrowed a technique from the demo “Crank The World”. Aras used an 8bit per pixel backbuffer, which is dithered against a bluenoise texture. The big plus with this trick is that the dithering remains static. Flickering pixels on the Playdate look horrible, and kinda flash in a weird way, so you need to be super careful. This method totally avoids that, and has a few additional benefits: once you’re working on an 8bit per pixel back buffer, you can do additive effects, like partial transparency, and everything comes out in the wash without any extra effort.

With the tower looking groovy, I needed to add the platforms. For this, I wrote a simple Python script to parse TileD .tmx files into C headers, so my level data can be compiled in. If a tile is “filled” then I assume there’s a platform there.

The platform rendering is a very similar process to the tower’s pre-calculated animations: I’m wrapping the platforms around a cylinder of known radius, with an offset to add some depth.

  1. Where is the platform, around the tower? This is the centre point of the platform.
  2. Using the cosine, work out how far the left and right edges of the platform are, from the centre point
  3. If the platform is “behind” the tower (ie: any cosine value < 0 would mean a portion of the platform’s behind the tower) then work out how if the left and right side need to be clipped

To add depth, I’m using the cosine to set an 8 bit colour value for the platform, and then I’m blitting the appropriately sized rectangle of this into the back buffer. It works surprisingly well!

This is the end of my Playdate shenanigans for the foreseeable. I did add a nice gradient copper-list background while I was on the plane, but after updating my laptop to Debian 13, I discovered that the Playdate simulator is basically broken (libweb2gtk version is missing) and I can’t be arsed to patch that.

Besides, I’ve not got any other holidays booked, so no more side-quests for me… Until Xmas.

I have done some more work on Gilby, though! :D

Previous Post: "Lumo 2 Key Art"

Friends:

If you like any of my work, please consider checking out some of the fantastic games made by the following super talented people: