PIZZA-8 - Simulating PICO-8 on a modern game engine
Introduction
If you've seen my Twitter recently (Right 'ere), you might've seen I've been working on Jefftastic's Pizza Delivery 2, a sequel to a game I keep close to my heart, as stupid as that sounds taking in count that it's a straight forward platformer made in two days, Why? Well, to me it's basically a Hello World of sorts! It's such a simple game that it's helped me understand a lot of game engines and languages, from PICO-8, to it's TIC-80 port to finally going all round and making an accurate Godot port, the latter one is important for us today.
If you've ever played games like Donkey Kong 64, the Yakuza games or Animal Crossing, you might've found that they hide extra content in the form of classic games, most of the time when a developer wants to do this they write an emulator to get as accurate as they can get to the original game, since it plays the original Binary data, now this is pretty straight forward when you have the knowledge to reverse engineer the original hardware and simulate it on modern hardware, but my own case is uh... Pretty special.
Donkey Kong's original arcade game was added to Donkey Kong 64 for example
My approach to get all existing Pizza Delivery games running inside Pizza Delivery 2 was porting, since PD2 runs on the Godot engine, I can't just put an executable to the original games there, so I took all the original assets, referenced the source code a little and did everything by eye, this process worked pretty fine for Pizza Delivery 1 since it's a simple scratch platformer and it ended up with what I think is the best way to play that game, but since Pizza Delivery Demastered is a PICO-8 game, it was a lot more complicated to just, eyeball.
My first attempt at this was less than accurate, it was done in about 3 days and there were a lot of parts where I just had to accept that it wasn't gonna be accurate, PICO-8 is very hardware-eske, with this I mean that you can do a lot of tricks to get some visual effects, the best example of this is the fade-in and fade-out on the game intro, this just, wasn't doable on a modern engine like Godot, atleast with how I made the first attempt. So after this, I remembered something I was told about months ago. If you've ever played Celeste and looked hard enough through the levels, you might have unlocked a little computer on a corner with Celeste Classic, the original game made by the creators in 3 days for PICO-8.
Well, since PICO-8 is closed source, how did they get the game running on not only the PC, but also Nintendo Switch, PS4 and Xbox? Luckily we have the answer to that, turns out the devs shared a few files from Celeste's source code as a learning resource, and within those is their PICO-8 "Simulator", what this actually is is a collection of functions from the original system recreated in C# (Same language as the full game) and the original game source translated from LUA to C#, so what the main game does is just run a "Scene" with Celeste Classic on it. This was a really clean and smart way of doing this, since PICO-8 emulators that try to emulate the system down to it's poke's and quirks fail to be accurate, So what if I did this? I figured out it would be a pretty fun challenge and if done well, could make something impressive.
Pizza-porting goals
My only goals for this project were:
- Getting PDD running all withing once scene, meaning that I could not use things like autoload scripts or well, multiple scenes, all of the PICO-8 related code must be inside one scene, to make it as easy to manage as possible.
- Be as accurate as possible, emulate every PICO-8 specific function PDD uses and transcribe as much of the original source code as possible, we'll later see why I couldn't just transcribe everything.
The big caveat here is how PICO-8 and Godot work. Although they're both game engines/frameworks, under the hood there's very little that's the same, the best way I can categorize them is that PICO-8 is bare-bones, which you may think is a bad thing but it has it's ups and downs:
- Everything must be called by-the-frame, meaning that if you wanna draw text or a circle, you must call it every frame, otherwise there'll be nothing to see.
- It doesn't have an object system in place, meaning that if you wanna do object instancing, you will need to roll out your own object system.
- It's all one script, this means that any function or running instance can call any variable as long as it's not local to another function.
Now, Godot on the other hand:
- It's all based on instances (Called nodes by the engine), meaning that out of the box you can, for example, instance a player node as many times as you want.
- Variables are private to each script/node, sure, you can find your desired node on the node tree and look for a function/variable inside it, but if that node disappears, changes name or gets deleted, you will probably end up with a game crash.
- Since everything (and I do mean everything) is node based, things like drawing shapes or text cannot be done in code, you could, say, create a text node in-code and just do that every frame to simulate PICO-8, but this will do exactly that, create a new node, every, single, frame.
Pizza time
My first step was to well, write a PICO-8 like environment, on my first attempt I left things like the pause menu for last which ended up on a buggy but fine menu, this time, I wrote a menu system with things like the Menuitem function in mind, and it payed off! I ended up recreating all of the pause menu functions excluding the Favorite one since, well, there's not much you can do with it.
After this, I opened up the last version of the PDD source code and went function by function, thinking about how I'd need to go around recreating them, in the end I had about 20~ functions from PICO-8 working less-or-so the same as their original counterparts, plus a few more I added due to this new environment's short comings, another project that helped around this time was pico2tic, an attempt to make PICO-8 games run on the TIC-80 system, another LUA based fantasy console, I took functions like cos and sin from there since I noticed PICO-8 outputs something different on those.
I lifted some code I already had made from my first attempt to fasten up the making of this, including the startup screen and the music code, also, Why PIZZA-8? Well, not only to avoid legal problems with Zep (Just kidding, he's an awesome guy) but also because it fits onto PD2's canon, the idea being that Stanley (The main character) is too broke to get a PICO-8 so he got this bootleg version.
Now, about those short comings, probably the biggest is the fact that since this isn't a single-script game anymore, global variables don't exist, to fix this, the emulator node (Basically what keeps all the emulator code) has 2 extra functions to read and write to a "User defined array", so anytime I wanna define a global variable, I can call it and any time another script wants to read that variable, I call it too.
Example of how this works, you can see that even if it's not the best solution, it works
There also is the problem of the palette, PICO-8 has 16 colors normally, and with this comes a function called Pal, it lets you replace a color with another color, which allows you to do things like sprite color swaps (Like in WWEM) or in PDD's case, fade the screen in and out, for this, due to my lacking knowledge of Godot's shader scripts, I made a small script that reads all 16 colors on screen, and replaces them with another array of colors, due to reasons I do not comprehend, this doesn't work totally as it should, but after the day or two that it took me to get it working I just, I won't spend any more time fixing it, it does the job.
Early gif - look at this awesome pain yeaaaa
Another problem was the map, for this I created a tilemap node and luckily, I can read and write to it just like in PICO-8, but I didn't think about how reading tiles worked on this before I made the map, so after finding out my map was totally wrong, I had to redo it with correct tiles and create an array that has all PICO-8 "flags" for sprites, which are a feature I used a lot in PDD.
I also get to show off my amazing commenting code skills oH yEA baby
The biggest problem with the map was reloading it, Godot doesn't have a built-in function for reloading nodes to their default state, so what I had to do was quickly delete the map node and instance the original map at the same position, this was harder than it sounds since if the game ever calls the map and the game doesn't have the map node, it'll cause trouble, so I had to do some "dirty" work to get it to be bullet-proof.
Then came the task of taking as much as I could of the original source code and transcribing it to GDscript, Godot's python-eske language, fortunately the syntax between LUA and GDscript are fairly similar but figuring out where to put what was a mess, this was also when I noticed just how bad PDD was written, it was my first experience with LUA and game logic programming at that scale (Both of my previous projects Blob and Repressed Memories being less programming-heavy), so although I kept most of the (embarrassingly awful) coding to make it accurate, I did optimize where I could:
Top is the transcribed version, bottom is original, you can see some of the same code still there
Also this is where I excuse my awful coding from above as to "Keep it accurate"
Around this time, I also added a couple of nice things to the port itself, inspired by this post and Celeste Classic itself, I modeled a little 3D room and after messing around with 3D sprites and filters, I got the game to run inside a little TV! How cool is that!
I based most of the room on some art I made for the first port attempt
I also made the sound that comes out of the game less bass-ee and added reverb to be more akin to again, Celeste Classic.
Conclusion
All of this was, although really fun, kinda slow to do, it took me about 4 days to make the emulator itself and some 5-6 days to port the game itself, so about a week and half worth of work.
Was it worth it in the end? I think so! Not only did I learn how to make an interpreter in this manner, but now I have a framework I can keep around if I ever wanna port more of my PICO-8 games to Godot, maybe for a new game as an extra (Jetpico-8 DX... WWEM 2... Ah the ideas...).
Although as I'm writing this, the code for this simulator will be kept private since again, the point of it is to be unlocked in Pizza Delivery 2 and besides, there's not much point to playing PDD on this since it's 1:1 to the PICO-8 version, although If there's interest I could release the framework by itself to help others interested in porting PICO-8 games to Godot.
I love porting games and going into dumb projects like this, it's part of my passion of making games, so I'll probably look into what other games I can port to Godot or simulate in Godot, maybe in a better language like C# or C++ this time.
Now, where can you keep yourself updated with PD2 and other pizza-tastic projects? Well you can follow Love Pizza Inc., a Software house-like account we made on twitter or you can join the Pizza Delivery Community server!
Thanks for reading and have a nice day ❤️
Get Pizza Delivery Demastered
Pizza Delivery Demastered
Status | Released |
Author | Coffee 'Valen' Bat |
Genre | Platformer |
Tags | 2D, PICO-8, pizza |
Languages | English |
Leave a comment
Log in with itch.io to leave a comment.