Something to help create more complex games in Pico-8 rather than just simple one-off demos. All this code is free to take, use, change, adapt, or whatever. Have fun.
Press Z to cycle through scenes and X to reset back to the default scene.
Scene 0 shows a clock running, scene 1 still shows the clock but doesn't update it, scene 2 rolls a random number every few ticks, and scene 3 just looks pretty. The whole demo weighs in at only 161 lines of code.
This cart contains a simple set of functions used to swap out scenes composed of layers made up of calculations and drawing instructions. For this release there's some basic demo data so you can reverse-engineer the thinking and maybe adapt it to your own projects. I tried to mirror Pico-8's default update/draw style of functionality so you hopefully don't have to change the way you think about writing your games too much.
I made this tool for myself but I wanted to share it because it might help you, if you're like me and struggle with manifesting complex projects. I'm not very experienced as a programmer but I've spent a lot of time using drawing tools like Photoshop. Art tools often use layers to organize complex compositions. So I decided to adapt that way of thinking into Pico-8. I find this helps me keep everything straight in my head as I work. Instead of building monolithic behaviors inside the default _update and _draw functions, this scene manager allows me to create scenes built up of whatever components those scenes should have.
This has the added benefit of making it easier to jump back into projects after not looking at them for a while, because you don't have to completely rediscover your old thought processes in order to start playing around with new ideas. That has saved me a lot of time! I found my code became much more readable and easier to manage after making the switch to using this scene manager. A modified version of this very cart became the core of my Pocket Full of Sand demo.
The simple scene manager is very easy to integrate into Pico-8's core functions:
function _update() -- listen for buttons input() -- update active scene scene.updates() end function _draw() -- draw active scene scene.drawing() end
Here's how it works:
Top level table used to keep all the scene functions tidy and out of the way.
Scenes are made of two functions, assigned to the same index in their respective table. So you end up with something that should look very familiar:
scene.update[0,1,...]() + your code
scene.draw[0,1,...]() + your code
Included functions for the scene manager:
Increments active scene by by one until it reaches the end of the draw table, then it starts over.
The 'cycle' function only looks at the length of the scene.draw table. This means update steps are optional, but draw steps are required. This is personal preference again, and not set in stone.
Now only one critical piece is missing:
Function that runs every update and checks if buttons are down, then sends instructions over to the scene manager to make changes. In the demo, Z (btn 4) fires scene.cycle and X (btn 5) fires scene.reset.
Optional extras to bring everything together:
Table containing functions that hold game logic, behaviors, special sauce, or any step to take before drawing. I give calc functions unique names and add them to my scene.update layers. That way the layers themselves are kept very simple so my most gnarly complex code can live by itself.
This part is entirely optional and just an idea to keep code organized. If the description is confusing, it might make more sense in the way the demo code is structured. In my Pocket Full of Sand demo I ended up changing the name of 'layer' because it's a confusing value to use for just the draw steps. I didn't go back and change it for this release, but you might want to.
As an example of how everything comes together, here's some of the code from the demo.
First the functions for scene 0 are declared:
scene.update = function() calc.clock() end scene.draw = function() cls() rectfill(0,0,128,128,12) layer.clock() end
Then the special sauce for updates and drawings live in their own functions:
-- current time nowish = 0 -- set time to local pico time calc.clock = function() nowish = time() end -- print nowish time at top left layer.clock = function() print('time: '..nowish, 0, 0) end
This example doesn't do much, but I'm sure you can think of ways to extend it for your own ideas!
I tried to keep everything straightforward and well ordered so the code can be easy to grok. I haven't been programming for long so I'm sure there are better ways to do this, but I couldn't find any existing solutions for Pico-8 that fit my needs. In the short time since I wrote this code it's already advanced my Pico-8 skills enormously. Maybe it can do the same for you.
Again, this code is totally free to do with as you please. Best of luck.