Recreating a game for the Playdate console

Stijn Tromp, 500757527 – Gameplay Engineering

Table of contents

  1. Introduction
  2. Methods and techniques
  3. What is a Playdate?
    3.1 Limitations
    3.2 Design and documentation
  4. Recreating an older project for the Playdate
    4.1 The original project
    4.2 Using the Playdate SDK and Lua
    4.3 Additions and miscellaneous topics
  5. Further development
    5.1 Polish and Playdate design
    5.2 Additional features
  6. Conclusion and reflection
  7. Sources

1. Introduction

For this semesters’ Research and Design project I knew I wanted to do a hardware related project. I’ve always been fascinated by new technological advancements and hardware and this steered my interests for this semester. Virtual Reality has been the centre for most of my gaming and school projects but when this tiny less beefy, and very cute, console called Playdate was announced by Panic I knew that I had to make something for this device!

Over the last couple of weeks I’ve looked back at my older work to determine what project I wanted to recreate for the Playdate. Ultimately my choice was led down to my very first game development project “Corengate” a lane runner game originally written in Java using Processing. In this blog post I will walk you through the process of learning Lua, working with the Playdate SDK documentation and recreating the game.

2. Methods and techniques

During this project I’ve used multiple new and old methods and techniques to help me on this journey.

One of the things that I wanted to try for a long time has been the tool HacknPlan. HacknPlan is an agile planning tool like Trello but it’s specifically designed for use during game development project. I’ve never had the chance to try it before cause switching over it with a whole team always seemed like a hassle but with this solo project I’ve finally given it a try and I really liked it! Using HacknPlan I applied a kanban working method and created a backlog to fill a board I’ve created for every week. The card were neatly organized in their respective categories (e.g. Programming, Design, Bug) and all this made this tool something that I will use more of in the future.

A lot of time has been spent on desk research, mainly on the Playdate SDK documentation and the Playdate Design documentation, these came out along with the SDK itself just before the project so not has been made with them yet. I’ve also used Notion to capture my progress and feedback that I gathered weekly from my peers and teachers.

The progress I made was structured and planned using “Think, Make, Check” (TMC) cycles. Research done during this project fits into the multiple strategies and methods of the DOT framework. A few examples of this are the prototyping I did fit into the workshop domain, Peer reviews for showroom and in the lab domain I did tests with the project on an emulator and on actual hardware.

A lot of these TMC cycles consisted of me trying to find out how to use and implement certain game features into the project using the Playdate SDK. An example of this is how to use and add a player with a sprite into the game. I first wanted to know how to do this, then used the SDK documentation to experiment and implement this idea and tested what I’ve made after by using the emulator or Playdate itself. After I made sure my implementation had worked I checked the documentation and my work again to see if anything could be changed or optimized.

3. What is a Playdate

The Playdate is a new pocket-sized console made by the company Panic releasing in 2022. The console draws resemblance with the original Gameboy by Nintendo because of its 1-bit display and button layout. Playdate players collectively share the experience of a curated selection of video games made by independent developers, revealed one at a time on a fixed schedule.

3.1 Limitations

The Playdate might look a very modernised console by the feel of its menus and the implementations of certain newer technologies like Wi-Fi, Bluetooth and a 3-axis accelerometer.

However the rest of the device looks and feels like a blast from the past and might remind a lot of people about the limitations of handheld consoles from the early 90’s. The first thing to notice is the display which has a 400 x 240 resolution and only uses 1-bit, this means that the pixels are either turned on (displaying black) or turned off. There’s not a lot of processing power and RAM on the Playdate either. The CPU only has 180MHz of power and there’s 16 MB of RAM available on the device.

The controls on the Playdate might look rather standard at first sight. The front of the device has a D-Pad and A, B buttons for gameplay and a sleep and menu button. What is more peculiar about the Playdate is the foldable crank located on the side of the device. The crank is used as an input device and can be cranked forward and backward by the user.

The full specifications of the Playdate

3.2 Design and documentation

A lot of newfound creativity must be used to design around all the limitations the Playdate has and this makes for a fun challenge. Luckily Panic has provided some great documentation on Design and their SDK.

The design principles of the Playdate have been influenced and shaped by the limitations of the device. One of the first and most important things developers have to work with is the 1-bit display of the Playdate. Because of this and the small size of the display developers should play around with sizing their graphics and font when it’s unfamiliar territory to them. The documentation also gives several tips around this topic with many examples, they can be found by clicking here.

One of the few techniques that can be used for making 1-bit graphics is called dithering. Dithering is a technique which is used to define gradations of grey by varying the colour or shade of the neighbouring pixels but since we have a 1-bit display the variation is only between pixels that are either on (black) or off. This technique is usually used in combination with repeating patterns and is useful for conveying shades and textures in 1-bit graphics.

An example of how to use dithering to create different perceived colours of greyscale.

For making sprites I used a program called Aseprite. During the project I started experimenting with making my own dithering patterns and this turned out to be a lot of fun. First you make a pattern and then you can make the pattern into a brush to paint with or fill certain areas of your sprite

A few dithering patterns made into brushes

When using Aseprite to make your own dithering patterns you can create your own brushes out of your patterns. To do this make a selection of your pattern and then go to edit and “New Brush” (or use the Ctrl + B shortcut.) Now that you have your brush you can use it to draw or to fill shapes with the bucket option.

Video showcasing how to turn your own dithering pattern into a brush using Aseprite

Panic provides multiple fonts for you to use with your Playdate game. Something that I found very useful is that they also provide glyphs for all the buttons found on the Playdate. Using these glyphs makes it very clear for a player what button has be pressed when prompted by text. These glyphs can be accessed using the following characters: ” Ⓐ Ⓑ 🟨 ⊙ 🔒 🎣 ✛ ⬆️ ➡️ ⬇️ ⬅️ “

4. Recreating an older project for the Playdate

I’ve chosen to recreate an older project on which I worked because this is my first experience with developing a game for the Playdate including using their SDK and learning the programming language Lua.

4.1 The original project

The project I’ve recreated for the Playdate is called Corengate. Corengate is a simple lane runner game made during the first semester of my study. There is only a single type of enemy that you have to dodge and some power-ups that you can pick up. The power-ups made you go faster or slower and gave you extra points. When a player had died during a run of the game they would have their score placed on a highscore board if the score was sufficient enough. The original project was made using Java in Processing.

Screenshot of the original project

4.2 Using the Playdate SDK and Lua

At the start of this project Panic had just released their first public build of the Playdate SDK. The Playdate SDK is made for the programming language Lua but also includes an API to use native C code in your game or program. Panic recommends sticking with Lua in most cases and only switch to C if you can’t get the performance you want out of your game while using Lua. This is mainly because Lua is garbage collected. Its garbage collector’s performance varies wildly from game-to-game and even frame-to-frame within the same game. The collector’s performance is dependent on the amount of garbage generated per frame and, perhaps counter-intuitively, the amount of persistent, non-garbage present in a frame (which it has to crawl to discover new garbage).

Before porting to C Panic recommends that you squeeze every last bit of performance out of your Lua code before refactoring or completely rewriting your game in C. (Some suggestions found here.) The game I recreated wasn’t very big and heavy for the Playdate so I opted to start with Lua and stay with it for the rest of the project.

For an easy way to gauge performance you can use the drawFPS function within your update function to display the FPS within your game when testing it on the emulator or the physical console.

The “30” in this image displays the current FPS

Lua is a lightweight scripting language that supports many programming methods such as object-oriented and data-driven programming. The primary focus of Lua is for scripting and it’s rarely used as a standalone programming language. The scripts made with Lua are most of the time integrated into other programs or games, a few popular games that use Lua are for example: Garry’s Mod, Roblox or World of Warcraft.

Because this project is one of the first where I will be leaving Unity and C# behind for a long time I wanted to get a good work environment set up first. I Installed Visual Studio Code with the Lua extension and used an example project found on GitHub to work out how to use the PlayDate SDK in a working manner. This also included a quick way to test the games in the included emulator. With all of this in place I started working on recreating the game for the Playdate.

To get started I wanted to draw my player sprite on the Playdate’s screen. To do this make sure you first import the graphics and sprites libraries. The other libraries imported during the project add functionalities within the SDK related to their names such as reading out the crank position and input for the crank library. The ui library enables functionalities for using the Playdate’s built-in menu but also for the crank indicator which is used in this project.

After importing the needed libraries I added my player image to a “images” folder in the root of my project and first loaded the image onto a playerImage variable. Our playerSprite will then get the image assigned. For the last two steps I move the player to our starting position and then “add” the sprite, this adds the given sprite to the display list, so that it is drawn in the current scene. I also made “gfx” a constant for “playdate.graphics” to make the code less cluttered and easier to type for the rest of the project.

The code snippet above also showcases how to add a collider to a sprite. The coordinates are relative to the sprites own internal coordinate system and by using the getSize function we set the size of collider to the size of the sprite. This has to be done for all sprites you want to participate in the collision system.
I used the moveWithCollisions function to move the sprites with collisions on the screen, this seemed to be simplest implementation for my use case but for more advanced games there seems to be a lot possible with the sprite collision detection system in the API.

For getting input from the Playdate’s Crank I used the getCrankTicks function. There are also other ways to get input from the crank but this function is the easiest way to find out if the crank has made a full rotation. Make sure you import the “CoreLibs/crank” library or else this function won’t work. The getCrankTicks function will give you a return value of 1 as the crank turns past each increment of the number you pass through the function. For instance if you use “getCrankTicks(6)” you will get a return value of 1 for every 60 degrees you turn the crank (360 ÷ 6 = 60.) I used 1 because I wanted to use full 360 degree turns.

4.3 Additions and miscellaneous topics

Working with the Playdate SDK and Lua was a very new and refreshing experience for me. It made me realize that programming using another language can be a lot of fun but can also be exhausting. The main logic behind programming usually stays the same but the execution and small details differ a lot from what you might be used to. Small things such as if-statements and loops couldn’t be written from memory but I had to look up a lot of these things to get them correct and to get used to the way of writing them.

Other things next to programming where also needed during this project. I mainly wanted to import the audio used in the original project and have 1-bit sprites to represent the objects in the game. Importing the audio as mp3 files is possible however playing these mp3 files would be very CPU-intensive with only 180MHz of power. Thus we had to encode our existing audio into the ADPCM format which is less CPU-intensive to decode but still gives us a smaller file size than uncompressed audio files.
To encode our existing audio files I used a free, open source tool called Audacity. This process is rather simple, you load your desired sound into Audacity and export the audio to WAV with “IMA ADPCM” encoding. These are the recommended settings by Panic in their documentation. When your sound is exported it is ready to be used in your project.

Steps taken inside Audacity to convert existing audio to the correct format for the Playdate

To make the 1-bit sprites for the project I used a commercial program called Aseprite. I had previously touched the program before but only for a couple of times. Only using black pixels to create the sprites was quite challenging and confirmed me once again that I’m not a great artist but I still managed to create a few sprites to add to the game.

Example 1-bit sprites made using Aseprite

The game also features a high-score screen. This screen displays the top three scores and is saved and stored in a json file in the Playdate’s memory so the scores are still there when restarting the game application. To do this I used the playdate.datastore API, this allows for a simple way to serialize Lua tables and images. After a new score had been set I write the high-score data and whenever the game is initialized I read out this data.

Using the datastore.write function.

I also added the star power-up that makes use of the Playdate’s crank. When a player pick up the star power-up they can use the crank to gain extra points. An indicator will also appear on the screen to give a heads up to the player that they can use the crank.

After developing the project I made sure the file size of the game wasn’t exceeding the 40MB indicated in the SDK docs. The Playdate only has 4GB of storage so games should stay as small as possible. The game ended up to be 6,40MB, 6MB of that is taken up by the background music! Panic states that the biggest culprit for bigger game size is audio which seems to be true.

After I received my Playdate I also tried the game on the device itself. And it seemed to run just as smooth as on the emulator and gave me a chance to try out the crank with the game. You can see a video of the game on the right, the recording is made using the Mirror software from Panic which shows the Playdate screen and input on a computer when connecting using a USB C cable.

Picture of the project running on a Playdate console

5. Further development

During this project there wasn’t enough time and resources to add everything I wanted and more that could make the project better. So in the next paragraphs I will talk about further development that could be done for the project.

5.1 Polish and Playdate design

There are multiple subjects the Playdate SDK and design docs touches on that I wasn’t able to implement into the project such as a launcher card, a custom font for the game and Playdate menu integration.

Every game on the Playdate is launched from their own launcher card on the home screen of the Playdate. These launcher cards fill up most of the screen and can feature animations that will play when the player has the launcher card selected. When you boot up a game the launcher card will go full-screen and can make for a nice transition to your game. A few ideas I have for a potential launcher card could include an aeroplane ascending from an airport when booting the game or the player character that starts running when the game boots.

Example of how launcher cards work

When pressing the menu button on the playdate you get a menu where you can go back to the homescreen, change volume settings and it pauses your game. Any game can this menu to add their own entries that work with the game.

The way text looks in the game is defined by the font used. Making your own or picking an applicable font can make a big impact on the game-feel and first impressions. Different kind of fonts might be used for different types of texts in your game such as titles, dialogue, labels, or status information. If my project were to use more text or if this would be added by implementing a story element or other game elements, I would opt to make my own font. While I’ve never dabbled into making a font I think this would be a great and fun new experience. Panic has released a piece of online software called Caps (Account needed for access), with Caps users can drawn their own typographic characters, edit existing fonts and preview them.

Screenshot of PlaydateCaps

If the size of the audio would’ve been a problem I could’ve used Synthesized audio instead of encoding in ADPCM format. Synthesized audio is mostly known by people because of the synthesizer and the MIDI format, learning to use and compose this method is a big process but very unique. The audio used during the project doesn’t fit with the Playdate’s style so changing it so sound more synthesized and “old-school” would probably fit the game better.

5.2 Additional features and gameplay

In it’s current state a lot can be changed or be added to the game to make expand it. To verify what would work best and what would benefit the project most some research and testing must be done.

Currently the project uses most input methods of the Playdate but not all. The before mentioned menu button is not used in the game neither are the microphone and 3-Axis accelerometer. Not all input methods need to be used to make this project more complete but it could make the game feel more unique and it would be a good exercise on how to use and get more potential out of the Playdate and it’s SDK.

More power-ups or unique interactions could also be added. A power-up or maybe a threshold on the score that opens up more lanes for the player to walk on might give the game more depth and opens up more opportunities for other improvements and additions. The same can be said for adding other dangers in the game the player has to avoid or traverse. Other small improvements could include animations and visual guidance and response to the game.

6. Conclusion and reflection

During this project I learned to work with a lot of new tools and techniques used within the game development industry; planning with HacknPlan, Lua, 1-bit assets and not to forget developing for a completely new piece of hardware with it’s own just released SDK. All of this was a bit challenging but I learned a lot from it and looking back at my first game I made was a fun experience.

Planning and scoping is always challenging for me, and this project has proven it again. Even though not everything went the way I wanted I had fun and I will be definitely be working more with the Playdate in the future. Recreating a smaller and existing project was definitely a good choice and brought enough hurdles on it’s own. However for a next project with the Playdate I’m looking forward to design and develop a game with the uniqueness of the Playdate itself in mind and gather more feedback about iterations to benefit the end product. Now that the SDK is out for a while finding more resources and help with developing for the Playdate might be easier available cause during the biggest part of this project I could only mostly rely on the SDK and it’s documentation with barely any other sources available to be used.

All in all this project was a great start and introduction to using Lua, developing for the Playdate and so much more.

7. Sources

HBO-i. (2021, 8 juli). The DOT Framework – ICT research methods. HBO-i-Methoden-Toolkit. Geraadpleegd op 22 juni 2022, van https://ictresearchmethods.nl/The_DOT_Framework

James Greer, B. (2019, 30 november). 1-Bit Pixel Art Techniques (Tutorial + Timelapse). YouTube. Geraadpleegd op 7 maart 2022, van https://www.youtube.com/watch?v=0BZwEoj50uw

M. (2019, 12 november). Speed up Dithering painting in Aseprite. #PixelArt. YouTube. Geraadpleegd op 17 maart 2022, van https://www.youtube.com/watch?v=ozbSCI7iuOI

Panic Inc. (z.d.-a). Designing for Playdate. Playdate Design Documentation. Geraadpleegd op 14 maart 2022, van https://sdk.play.date/1.9.3/Designing%20for%20Playdate.html

Panic Inc. (z.d.-b). Inside Playdate. Playdate SDK Documentation. Geraadpleegd op 7 maart 2022, van https://sdk.play.date/

Panic Inc. (z.d.-c). Is there a media kit? – Playdate Help. Playdate Help. Geraadpleegd op 11 april 2022, van https://help.play.date/press/is-there-a-media-kit/

S. (2022a, maart 14). Everything About Playdate SDK Collisions in 7 Minutes. YouTube. Geraadpleegd op 15 maart 2022, van https://www.youtube.com/watch?v=27vCPiwgWzQ

Lua Orginazation. (2022, 26 januari). Lua: about. Lua. Geraadpleegd op 22 juni 2022, van https://www.lua.org/about.html

W. (2022b, maart 2). GitHub – Whitebrim/VSCode-PlaydateTemplate. GitHub. Geraadpleegd op 11 maart 2022, van https://github.com/Whitebrim/VSCode-PlaydateTemplate

Related Posts