Milestone 6 | Light Mechanics & Engine Upgrades


Hey guys, Donald here.

Over the last two months, Comet has made some incredible progress in both game design & tech. In this dev log I want to finally talk to you about some cool stuff we’ve been working on:

  • A peak at Chapter 2’s main light-based puzzle mechanics 
  • Our new crank-to-undo system
  • Some amazing tech upgrades that let us make the game look and feel even better

It’s been so energizing to see some of our oldest and thorniest outstanding tasks on our to-do list get nailed, and then some.

But how were we able to make so much progress in such a short time? Long-time readers will know that Drew Loebach, our Programming Lead, has been helping me out since February 2022 when I started making Comet in Pulp.

Drew has been a great friend who has saved Comet more than once now, and recently Drew had a fantastic idea for a little side project he wanted to work on (I’ll leave the reveal to him).

Comet is a passion project for all of us in Team Comet. Everyone on the team is stealing away time from our busy lives to work on the project. So when life stuff comes up, we encourage each other to take it easy. This might be frustrating to you if you’re reading this and waiting for the game to be done (or if you’re my wife 😅) but it’s what’s kept this project going. 

Drew has impressive design skills and has put them to work in other side quests like his Playdate game Jolly Chimp Champ which came out earlier this year.

So when Drew mentioned his exciting new idea, I checked in with him to see if we can continue to make progress on Comet. Were there other ways I could help support some of the programming tasks in the Comet backlog? Drew felt confident that he could get through most of them quite easily except for one: the wigglers. They were an exciting new mechanic for Chapter 2 but they’re REALLY complicated.

I like to problem-solve and floated the idea that maybe I could ask someone from the Playdate Squad Community to help us with the wigglers and a crank-to-undo system, which would free Drew up to work on more exciting things. He thought that was a wonderful idea.

Energized with a way I could push Comet forward, I went to work on writing up requirements and sharing them with the community. THAT NIGHT, not but a few hours later, Jan said he would be happy to help us out and by the next day it was clear that he was the best person for the job.

You might think I was too quick to make that decision, but sometimes you feel it. Sometimes, you gotta bounce… on these opportunities.

Jan, aka Mouflon Cloud, had JUST finished work on Kye, a port of an old Windows 3.1 puzzle game (which Playdate friend gingerbeardman also worked on).

What’s notable here is that this version of Kye has a CRANK-TO-UNDO SYSTEM (we needed one of those!)

Jan had a proven track record for releasing games and has been a staple member of the community for as long as I’ve been around. And I’ll tell you what, we couldn’t have been luckier getting to work with Jan. And the timing was perfect for him to be distracted by a little side quest. With that set up, let's get into all the things we’ve been working on. 

In the Spotlight

In our Milestone 4 post, I said the following…

When you pull back and look at Comet as a whole, the Core Mechanic that the game was formed around was: using the crank to control the lamp.

The secondary mechanic was: Sokoban puzzles (think of puzzles in games like Chip’s Challenge).

Our core mechanic has always been about interactive dialogue and manipulating the world with the light from your lamp, which is controlled by the Playdate's crank, but you’d be forgiven for not believing me up until now. 

Most of what we’ve shown up to this point has focused on the systems that were needed for Chapter 1. But now, the first set of unique light mechanics are in the game for us to test and tweak. 

The Wigglers


These little guys are something I've had in my mind for well over a year now. Perhaps as far back as the Lua reboot of Comet.

Since we were focused on getting Chapter 1 built, I didn’t talk about them with the team but they often wiggled their way into my mind when I worried about the game being fun.

The wigglers are a set of little creatures who, like Stella, are afraid of the dark but once they’re brought into the light they’re keen to find something to eat.

It's your job to get them to a tasty bush safely and to do that you need to set up a path with your light, which they navigate around the edge of.


In their most basic form, the bushes are locks that block your progress, and the wigglers are your key.

I thought this could be an area of design with a lot of synergy with the systems we already had:

  • Resizing the lamp
  • Changing the environment with our sokoban tool.

About 11 months ago, I wanted to lock down the logic for the wigglers and design a set of rules that were easy for players to understand and possible for us to program.

I had the crux of the idea but I wasn’t exactly sure how to prevent the player from just getting the wigglers stuck in their own light bubble and moving with them.

Inspired by Strupf / Lukas I decided to do some public brainstorming.

It’s often so helpful just to have a sounding board. Maybe I should get a rubber duck.

With help from our community I landed on the following:

  1. Wigglers live in a hole, and one has its head poking out. Just chilling
  2. When that wiggler is within an unmoving light source, it pops out and all the wigglers travels around the light source clockwise
  3. If the player gets within a tile of a wiggler, it runs off. If it’s in the hole, its head hides. If it’s out of the hole, it just runs into the darkness
  4. If the player moves a light source that the wiggler is in, it also runs away.

Soon after, I wanted to find some visual references for the wigglers. I had the idea that each wiggler would be its own independent creature but they would act as a group. I knew I’d seen something like that before but couldn’t quite place the reference, so once again I asked for some help.

Falinks from Pokemon was what I was looking for!


With the logic in place, I spent most of the New Year’s break designing all the wiggler puzzles in Chapter 2 and writing up some detailed design docs for the team.

Then Maria tested out a bunch of designs. We wanted to pack a lot of character into a small amount of pixels.

And as you can see, Maria excels at that!


Drew then got started on the logic.

It's clear that there are many complex factors to consider.

Drew had really only scratched the surface of building out the wigglers before Jan came in and gave us a hand.


And Maria finalized the design of the last few elements.


Here we have our wiggler hole & bush eating animations.

Jan understood the vision for the wigglers perfectly. After we implemented Maria's animations he helped refine their implementation to make the rules more understandable for players and in turn, more fun.

Let's show off a few other examples:

The wigglers will scatter when you spook them

And if they ever get broken up, they slowly realize and start to panic!

Oh yea, and those levels I designed at the start of the year? They basically worked as I intended! I’ve certainly had to make some tweaks, and no doubt I’ll make more, but how cool is that? 

Now we don’t wanna give everything away but here are a couple of gifs of some other creatures you’ll encounter in Chapter 2.


Ctrl+Z Crank

When Comet got rebooted in Lua, we started using Github to manage the project. I knew at the time that an undo system was kind of a must-have for sokoban style games. ‘Crank To Undo’ was issue #16 (now we’re up to issue #261!) but it had always been put aside because it’s a tricky system to build.

There’s a lot to think about:

  • Which actions are recorded?
  • How many actions can we store in allocated memory?
  • How do we capture multiple actions at once or chain reactions?
  • Can we design this in a way that doesn’t impact performance?
  • How do we teach the player?

Jan had all of these front-of-mind and knocked it out of the park. We actually have… unlimited steps recorded? And the game doesn’t miss a beat!

This will have a huge impact on everyone who plays the game. Needing to reset an entire puzzle when you make a simple mistake is just too painful.


Click here If you wanna hear the fun audio feedback we added to this.

Speed of Light

As Jan started working on the crank-to-undo feature, I offhandedly mentioned that he might need to do some optimisations to get the game running well with the new systems he was about to build. 

*Crack knuckles* That was all he needed to hear. Jan went on to change foundational systems which dramatically transformed the experience of playing the game.

Some changes had huge gains of about 30% improvement to CPU & memory usage from about 3 hours of work, other changes took about a week for only 3% or 4% improvements.

But once all is said and done, the experience is night and day. Here’s a demo of a build from June and a build from this week which shows you just how much better it is.

The game now loads new rooms almost as fast as playing in the PC simulator, and due to some major reconstructive surgery, our largest, most complicated rooms now look WAY better and run at smooooooth framerates. 

Let’s dig deeper into what we did:

Collision Clash

Every time something moved in Comet, every object would look at every other object in the game and check if something should happen. As you can imagine, most of these checks don’t matter. A Bear is never going to read a sign (literally or figuratively). So we now have collision groups that limit what is being checked.

Entity/ClassCollision Group
Collides With
PlayerPLAYERObstacles, Lantern, Triggers, Stairs, Buttons, Holes, Collectables, NPCs, Boxes, Animals, Signs, Burnables
BoxBOXESObstacles, Holes, Buttons, Boxes

For example, the player entity needs to check if it’s colliding with a bird, but boxes don’t need to bother.

Instead of creating a sprite for every static obstacle in the game, we now use a few collision tilemaps for the most frequent static tiles: water tiles and fixed obstacles. 

Instead of checking 150 trees, stones and cliffs for collisions with the player and other moving sprites, we look just once with a map that checks if the specific tile the player (or box, wiggler etc.) wants to enter is accessible or not. In some levels, this cut down 50–70% of sprites and more than 10% of CPU calculations.

Crunching Layers

As you guys might know, we use LDtk as our level editor. Up to this point, we’ve been using Nic Magnier's LDtk Importer for Playdate to take the 14 layers of tiles we use to design our rooms, and render them in game.

This worked well for the most part but on larger levels, with a lot of entities, we’ve had to limit the scope of our ideas if we wanted the game to run well.

In fact, the fishing village in Chapter 1 got cut in two a few months back due to issues with loading speeds & framerate. 

But there’s a better way! Expanding on the ‘fast loader’ feature Nic already created when we run the game in the simulator, we now pre-render & crunch a bunch of layers down into a single large image for each room. This was a massive win! 

We also created simple tilemaps for obstacles and water, saving a lot of sprites, which saves a lot of collision checks and makes the levels load faster. We generate them (and calculate the collision maps mentioned before) when building the game so it’s ready when needed in game without slowing down the game itself.

Caching In

We cached image tables and images to prevent multiple loads of the same file (and additional logic for keeping the previous level assets in memory). You can get a good look at this in the above embedded video. Returning to a room that’s still in memory now loads in less than a second, rather than 7 or 8 seconds.

Loading…

By default LDtk levels are loaded all together from the core LDtk file. In our case, the complete set of levels was permanently taking about 7 MB of Playdate’s precious memory. That’s almost half of it and that’s just the static level data, so the game was running pretty close to the 16 MB limit all the time. This made larger file loads dangerous because of possible memory overflow,  which also made garbage collection (the system that removes objects in memory that aren’t needed) run very hot, checking all that data all the time.

But LDtk has an option to save levels to separate files, which the importer respects. Now, when we walk into the fishing village the engine only loads that 73 KB room on the fly, keeping our memory usage low. 

That means, with some work from Jan, we could let our garbage collector to relax a little. Now it runs for 1 millisecond when the load is low, and only ramps up when memory use is above 50%.

Burn Bright

With the new performance gains we’ve been afforded, we’ve upgraded our visuals a bit too.

For a long time now, people have been asking why our lights haven’t been animated. Well now they are!

We also went in and animated all of our rivers and lakes in Chapter 2.

This was actually a very manual process of exporting auto-tile data as PNG images, then hand-placing our water animations for 24 different rooms and rendering them out as single large sprite sheets to be rendered in-game. It was mostly a lot of busy-work but I’m really happy with the results.


Example of exported autotile data, or abstract art?

And finally, you can see the burning bushes on the dark layer of the tilemap.


DevOps

I’ll also give you a look at a couple behind-the-scenes changes of the really nerdy stuff that helps us during development. Things you might not see/experience in-game, but help us make Comet more efficiently.

Automated Builds

Now this is pretty clever.

After helping us out for a while, Jan set up our GitHub instance to automate our builds using Github Actions. Before all of this work, if we wanted to make a build and test it on device, we would:

  1. Save our changes
  2. Compile the game
  3. Run the game in the simulator 
  4. Copy the full folder of LDtk Lua level files from the Sim’s data folder to the source the level folder
  5. Compile again.

As you can imagine, this gets to be a pain in the butt. Drew and I both had some shortcuts to make this easier, but it was never fully automated. Jan developed a system where Github would do steps 2-5 all in the cloud. A virtual computer builds the game, then runs the simulator twice in a headless mode (without a display or a sound card) and then publishes the zipped result.

It took an afternoon filled with dead ends and small breakthroughs because the simulator isn’t designed for that sort of thing so a lot of stuff needed tweaking.

So awesome, we have a good history of builds but the process of sending the game to the Playdate still took a LOOOOONG time.

It turns out most of Comet’s 50 MB file size is music so Jan added some parameters to the automated build process and it now spits out 3 files:

  1. A full dev build as we’ve always used
  2. A mini build that strips out the music
  3. A release-style build that removes unused assets like raw LDtk files.

That mini-build is now down to 3 MB! So much nicer to test with.

Now, when we’re finished working on Comet for the day, the team pushes the change to Github and a few mins later, the change log and build links are posted to the development discord server for everyone to enjoy! (Thanks IFTTT)

Visible Build Numbers

With so many changes going on in the code over the last few months, there has been a lot of testing which can sometimes lead to confusion. When bugs come up is it in the main branch of GitHub? The undo branch? The wiggler branch? 

To keep ontop of things we added a build number to the main menu screen making it easy to track versions.

In our next blog post, we should have hit our Chapter 2 alpha milestone. 

Which means the logic will all be in place and we’ll be able to play through Chapters 1-2 end to end! 🤞 

Thanks for reading, and don’t forget to wishlist Comet on Catalog!

👋

Get Comet

Comments

Log in with itch.io to leave a comment.

(+2)

Wow, those optimizations are crazy, great job!

(+3)

Another awesome update. So proud to be a part of this team.