It's time again for PiCoSteveMo
It's November again, which can mean only one thing. That's right, it's time to make a janky PICO-8 game with a tenuous connection to a Stephen King work.
https://itch.io/jam/picostevemo-4
My idea this year is to make a truck game around the unusual spelling in the title of the novel Pet Sematary.
I haven't touched PICO-8 in this time so I'll try to make a summary of the tricks I learned last year.
Starting a new project
If you want your cart project to live somewhere other than in PICO-8's memory files, you can make a symlink to some other part of your hard drive.
On linux, pico-8 carts live in ~/.lexaloffle/pico-8/carts and I want to my cart to live in ~/src/wet-semantary.
First in the PICO-8 console, create a new cart
-- in PICO-8 save a fresh new cart
save wet-semantary
Then in a bash terminal we can make a link
# in a bash shell move the cart to the repo
mkdir ~/src/wet-sementary
mv ~/.lexaloffle/pico-8/carts/wet-semantary.p8 ~/src/wet-semantary/
# set up a symlink to PICO-8 carts directory
ln -s ~/src/wet-semantary ~/.lexaloffle/pico-8/carts/wet-semantary
# verify that it worked
ls -al ~/.lexaloffle/pico-8/carts
Then you can access this new folder from PICO-8.
-- in PICO-8 again load the cart
load wet-semantary/wet-semantary.p8
Once the cart is loaded you're ready to start jamming. Run the cart with RUN or ctrl+R.
Using a separate lua file for source
If you're like me and not hardcore enough to use the PICO-8 editor, you also may want to have a separate lua file for your source code.
If it's not there already, add a __lua__ block like this between the pico-8 cartridge\\version 43 section and the __gfx__ section.
__lua__
#include main.lua
This will import source code from the main.lua file.
Adding text the top of your cartridge file
Whilst we're talking about that lua block, you can add comments to the top of it and they will appear on your cartridge png when you export it
__lua__
--wet semantary
--by beho ♥ https://beho.dev
#include main.lua
PICO-8 methods
You mean you don't have this PICO-8 cheat sheet as your desktop background already?
If you get lost, you can also search the manual.
Here are more or less all the methods I used last year:
Drawing
-- clear the screen with colour n
cls(n)
-- outline of rectangle (x, y, w, y) with border radius r and colour n
rrect(x,y,w,h,r,n)
-- fills a rectangle (x, y, w, y) with border radius r and colour n
rrectfill(x,y,w,h,r,n)
-- draw sprite number n to (x,y), has optional scaling parameters
spr(n, x, y)
-- draw from sprite map source rectangle (sx, sy, sw, sh) to (x, y)
sspr(sx, sy, sw, sh, x, y)
-- draw an image to (x, y) from tile map source rectangle (sx, sy, sw, sh)
map(x, y, sx, sy, sw, sh)
-- draw text on the screen at position (x, y) with colour n
print("⬅️", x, y, n)
-- change the transparency colour in the sprite editor
-- helpful if you want to paint things black
palt(11, true); -- 11=extremely bright green
palt(0, false); -- 0 =black
Input
-- if button 4 is pressed (and released, fires once)
if btnp(4) then
-- do things
end
Sounds
-- make crazy little noises
ErrorSound = "\as6x1g0g-0"
print(ErrorSound)
-- play music track n
music(n)
-- fade music track n in with over 6,000ms
music(n, 6000)
Iteration
-- iterate over a range
for i = 1, 4 do CloneGuest(Options[i])); end -- four guests
-- iterate over a list
for option in all(Options) do
-- option.things
end
Time
-- time since the game started
t()
-- check how much time has happened since a previous measurement
if t() - PartyStart < 3 then
end
Scene management
You don't need scene management to have a good time now, but I know I will want it eventually.
Inspired by system for managing states you can assign your update and draw hooks to variables:
-- empty global variables which will be populated with
-- update and draw methods for scenes
Update = nil
Draw = nil
function _init()
-- the initial state
PartyInit()
end
function _update()
Update()
end
function _draw()
Draw()
end
(The methods that start with an _ here are the entrypoints that PICO-8 uses to control your game.)
Then you can switch to other scenes by updating these global variables.
function TitleSceneInit()
Update = TitleSceneUpdate
Draw = TitleSceneDraw
end
function TitleSceneUpdate()
-- update logic
end
function TitleSceneDraw()
-- draw logic
end
Centered text
I made this helper function:
-- print text (somewhat) centered at (x,y)
function PrintCentered(text, x, y, color)
-- print offscreen so we can measure the width
local w = print(text, 0, -8)
print(text, x - ceil(w / 2), y, color)
end
Animation
I used this implementation of smooth step.
-- (time, start, end)
function SmoothStep(t, s, e)
s = s or 0
e = e or 1
t = mid(1, t)
return s + (e - s) * t * t * (3 - 2 * t)
end
This will return a value between 0 and 1 that represents how far time has progressed from value start to end.
I used this for example to animate the logo sliding down from the top of the screen:
logoOffset = SmoothStep((t() - 3 - TitleStart) / 3, -8 * 9, 0)
map(16, 0, 8 * 3 + 6, 8 * 4 + logoOffset, 8, 5)
OK that's enough yapping, I need to start working on my new game now.