Milestone 2 | Performance, Key Art & level design.


In our last devlog, I talked about Chapter 1 being playable. Sadly, that was in reference to an internal milestone, not something people could download (yet 👀). Thank you to everyone who reached out and asked how they could get their hands on it, and forgive me for the confusion. 

There are a few systems I’d like to work on before we put the game into the hands of people outside the team, so it might take a little more time until folks get to play it. 

Plus, there are a few developments outside Comet that I’m dedicating time to… 

Life Milestone

Pretty soon after we last caught up, my wife and I had our first child!

She’s had a number of health issues which have affected her growth and sleep. This has been really rough on us. Thankfully we have family that live close by which has made all the difference. Our little girl is back on track now which should hopefully result in a more stable schedule for everyone. 

Comet Milestone 2

For a long time now, Comet hasn’t been very performant on Playdate hardware. The team was so focused on building the content creation pipeline and game foundations that performance took a back seat.

Comet quietly has a lot going on and it’s really important to Drew that the game runs well and loads assets quickly. He’s done a lot of work already to load assets on the fly when they’re needed rather than all at once, but while I was on Dad duty, he wanted to take the time to focus on getting our framerate up and with a bit of head room.

Here’s what he found…

Performance

Hey guys, Drew here. A little context before I start, Comet’s levels are built in a tool called Level Design Tool Kit or LDtk. This is an easy option for us because the great Nic Magnier (creator of Pick Pack Pup) built an importer for Playdate and shared it with the world. 


When I started working on performance, the game was running somewhere between 20-26 fps in the larger outdoor areas. The goal was: get this up to a smooth 30 fps. 

There’s lots of stuff going on here that could be slowing our game down: We have large areas with tons of sprites, lots of different layers that need to be rendered with lots of invisible collision rects that prevent the player from walking through solid objects, and lots of animated sprites with timers that are updated every frame.

I did my testing in the Fishing village at night time, since this is the level with the most going on (size, number of entities, animated tiles, etc.)

Baseline: 20 fps

Walking to the neighboring area, we saw that it already ran at a noticeably zippier 26 fps, but still too far from our goal of 30fps for me to be happy with. This screen also has entities to load, all of the layers, and the water that we need to animate, but just having less.... stuff to animate and update already meant that it ran faster.

Guard encounter: 25/26 fps

Check Lantern UI

On the left side of the screenshot above, you can see Stella’s hand holding the lantern. Under the hood, the game was checking a bunch of stuff every frame to see if it needed to update the animation frame based on how strong the lamp was. We should really be conserving the operations that we do every frame, so instead we can make this check only when the lamp radius changes.

While we're at it, the code checked the crank every frame using “playdate.getCrankTicks(4)”. Instead, we can try using a callback function which is only called when the crank is turned. Let’s see if that was more performant…

...

...

... 

it wasn't.

So... no luck there. I guess it's time to look elsewhere for some performance savings.

🩑Squid's Performance Recommendation | Cache Animated Tilemap

This was suggested by Squidgod, an absolute bro in the Playdate community who was nice enough to take a look and give us some suggestions on how to improve our game's performance.

For our animated layers such as the water, we had a single 10x20 animation that we were tiling over whatever size we needed to get the overall effect, such as the ocean.


Every animation frame, we're grabbing one 10x20 piece from this spritesheet and tiling it over a large area to make our ocean move.

Squid's suggestion was that we pre-bake a big sprite sheet by doing the tiling operation one time upfront for each animation frame, and saving one big spritesheet of the animated ocean to memory. That way the process-intensive part only has to be done once when we load the level. Let's give that one a try...

BINGO!!!

…and... we're up to a solid 29/30 fps, even with larger levels with multiple layers and animations!

...BUT... the new method takes up lots of memory... causing the game to crash :-{

It turns out we traded one problem for another. Pre-rendering the different versions of the animated background offloads the processor, but the tradeoff is that it fills up the Playdate's memory which has to hold a large image for each frame of the background animation (like those beautiful ocean waves.)

In those cases the game still runs ok (until it crashes), so the tradeoff was worth it, but I need to keep a close eye on the memory usage from here on out!

Time to see how I could free up more memory. The playdate has 16MB of RAM, and we were using about 6MB of it in the Fishing Village before, so plenty of headroom! After caching the tilemaps though, we were closer to 12-13MB. It was still enough in theory, but it seemed that when we change levels, if there's not enough headroom then the game generates all of the tilemaps and stores them in memory before Lua's garbage collection can kick in, causing a crash. Blargh.

I decided to try and move on from drawing the animated tiles across an entire layer (with a mask). Instead, I drew them onto some smaller-sized sprites. Rather than each animated entity having its own layer in LDtK, I created a special entity where you can select the animation and the size it should be tiled over.


The game's code will then pick up this entity, and when the level loads, it’ll pre-bake the animation for the exact size we need it for.

Once I got that working, it drastically reduced the amount of memory that I needed. It went from hitting the 16MB limit down to having the memory usage consistently at ~9MB. Also, the loading times for the large Fishing Village were down from 8 seconds to around 6. BONUS!  

Now we're at a smooth 30 fps! Time to sit down, relax and enjoy the fruits of my labor.

Back to you Donald.

Book Intro

In previous blogs, I showed off some book animations and a cool concept for how to transition from menu into game.

Well, that’s no longer just a concept!

We went back and forth about what the book is. Is it a story that Stella is writing in her diary? Or is it a story the player’s reading about Stella? In the end we felt the latter was more appropriate.

Before we built it, we sat down and came up with ideas of how we wanted the intro to look.

Will, Drew and I all pulled our pen and paper out to come up with ideas.

  • How many fades should we have?
  • Should we have some sort of team logo then the game logo?
  • How should we transition from the card nicely?
  • How long do we want it to be?
  • Should we have a unique sequence the first time the player loads, then a quicker one next time?

From the brain storming, I made this mock up.



And Drew got it working!

Around the same time, Rae on the Playdate Discord was talking about using a little trick to display an exit animation.

After seeing that, Drew couldn’t help but work on it real quick.


To save time in testing, it’s been turned off (for now) but all in all, I think these sequences look great.

Team Logo

After a while, that left blank page started to bug me.


We had an idea earlier in development that this might be a book found in Stella’s room.

 

But I couldn’t get it to look good with my poor art skills, so we moved away from that concept and left it blank.

Some time later, I realized we could stick our team logo there.For a while, I’d been collecting examples of logos that would work well in 1-bit.


With some ideas in mind, I asked if anyone on Discord would want to make something for us.

Choosh was keen to give it a crack. Given that Comet is half the Playdate’s resolution at 2x the size and our logo needed to sit inside the left page of the book, I didn’t give him a whole lot of room to play with, but after some back and forth I was really happy with the result.


Seeing as it will only be seen in game, I think it reads well.

We opted for a Comet that points up to represent our team making progress, and to avoid something that looks too much like our game logo, or FF7 (best not to invoke the wrath of Square Enix’s lawyers). Slap a bit of detail into the background and I was able to get it in game pretty easily.


Key Art

After finishing the sprite art for the NPCs in the fishing village, I asked Maria if she could work on some key art. We’d need it for store pages, and it’s helpful on social media.

I gave Maria the guidance that it should show Stella with her lamp going on an adventure. Although she might be in the woods at night, the art shouldn’t invoke a feeling of fear. And she nailed it!


Maria went through quite a process to get to the final result.

Here are some WIP shots.

Full Art

Banners

The result is something that blends in well for the itch page, and pops in a thumbnail.

Card Art

With the key art for the store page done, we went about adapting it to 1-bit for the game’s card art.

First, a scuffed mock up was made by me using Daniel kaasiand’s dithering oven.

After that, Steph took over. He did a wonderful job converting Maria’s work down to 350x155, by hand.


Then I took the torch back to finish it off with animations, a border & logo.


Collectables / Menu Image.

Chapter 1 of Comet has big hangout energy, but we also have some puzzles with a blend of difficulty levels to change things up.

As a reward for completing them, we introduced a few collectables: shells (+ a few animal treats).

Collectables gif

We also needed a way for the player to track what they’ve collected, and perhaps what’s missing. So we decided to use the Playdate’s menu image system. At the same time, we could have a little flavor text to remind the player what their goal is.

What better way to show it off: Stella giving Nat the Cat a fish.


Half Lit Indoor Areas

In a 1-bit game, it’s not easy to show a scene going from day to night. We get around that by having a transitional scene in Stella’s house where she goes to bed then wakes up.

But when she wakes up, we found it wasn’t obvious to the player that it’s night time inside. So I played around with having a black/transparent checkerboard pattern inside that implies some low-light lamps being left on overnight for Stella (seeing as she’s afraid of the dark and all).


In testing it reads really well.  

But I was worried about how it might look when testing on device…

On device it looks great! 

I gotta say, I love testing ☄Comet on device. It just comes to life! It’s a very special feeling.


NPC Portraits

During the milestone push, we also finished the portraits for all other characters in Chapter 1.

I really like how these turned out.


Thought Bubble

Will and I found it tricky to tell when Stella was thinking to herself vs talking. So Steph made this new dialog box we can swap in where needed, and Drew was kind enough to come back to the dialog system to put it in.


Early Level Design of Chapter 2

And finally, during this time, I began blocking out Chapter 2.

Chapter 2 opens up and allows the player to explore different areas of the woods.

The plan for now is to block up the basic shapes of the woods as a whole level. Then break it apart into smaller more manageable rooms.


Each main area will have different types of puzzles, most of which will require the player to make clever use of the lamp’s light.

The two areas I’ve been able to work on without programming support have been the rocky area with a lot of cliff puzzles.

And the lost woods-style section which I won’t show off just yet.

What’s next?

The team has scattered recently but we plan to make a push before Christmas & New Years. There are a handful of polishing tasks for Chapter 1, then I think we’ll all be focused on developing Chapter 2. Look out for it next time we talk!

Get Comet

Comments

Log in with itch.io to leave a comment.

(+1)

excellent update. loved reading the 'behind the music' optimization story.

(+2)

Thanks for the interesting write-up! The part about prerendering animated tiles for an area to improve the rendering performance will definitely be used in my games as well when I run into performance issues 😊

Looking forward to trying out the game when it’s ready! ✹

(1 edit) (+3)

I'm glad you liked it! The general takeaway here is that for any process-intensive operations, always ask yourself if it's possible to pre-render or pre-compute so you don't have that in your main game loop which can affect performance!


Another good example of this was the static for my TV Tuner minigame from Mobware Minigames. At first i was rendering the static every frame, which performed horribly on device. I started pre-rendering all the frames of the random static shown on the TV, which worked, but added a noticeable load time. Finally I added a feature where the pre-rendered "static" is saved to an image on the Playdate disc, so the next time the player plays the minigame it can grab the pre-rendered static and eliminate the load time altogether!

 

(you could also pre-render the static noise and include it in the game as a png file, but i took the trade-off of the small one-off load time in exchange for not making the .pdx any larger than it needs to be. )

(+2)

What a great read! Thank you for sharing all this process. I can't wait to play the game.