PICO-8 is a great tool to use if you are looking to learn how to develop games. You can create 8-bit retro style games and distribute them online for anyone to play.
PICO-8 offers its own sprite editor and music composer for creating complete standalone games. To become familiar with how PICO-8 works, it is best to start simple.
Running the PICO-8 console
PICO-8 is a virtual console and game development tool. You can use it to create and distribute simple retro-style games.
Start by getting the PICO-8 application and running it:
- Purchase and download the software from your itch.io home page. You can download PICO-8 for Windows, macOS, or Linux.
- Run the app and you should see a splash screen like this:
This is the PICO-8 command line that you can use to load and run cartridges, along with various other actions, documented on the PICO-8 Wiki.
Basic use of the code editor
You can get an idea of how PICO-8 programming works by using the code editor to create a simple program and then run it.
- On the home screen, touch ESC to enter the code editor. You should see a mostly blank screen:
- The code editor is basic but easy to use. For now, start with the simplest program:
PRINT("HELLO, WORLD")
- When you have entered the above code, press ESC to return to the home screen.
- now you can write RUN to run your program. It should display the text “HELLO WORLD”.
write a simple game
Once you’re comfortable writing PICO-8 code and running it, it’s time to try out a simple game. Tic-tac-toe is a great choice for a starter game because:
- It’s turn-based, so you don’t have to worry about timing anything.
- This is very little logic, essentially consisting of just two rules.
- It has very simple graphical requirements.
The complete code for this simple game is available in a GitHub repository. You can download this file, save it to your local cartridge folder, and open it with PICO-8.
To open your local cartridge folder in your computer’s file explorer, type BINDER on the PICO-8 command line.
Game Loop Intro
Whatever language you use, game development typically centers around a “game loop.” It consists of the following process:
- Get user input
- Update game status
- draw game
PICO-8 has excellent built-in support for a game loop through its functions. _in that(), _to update()Y _draw(). These functions are special because PICO-8 calls them automatically, when necessary.
You can see how they work by creating the skeleton of a game. This first review will show a cursor on the screen that moves as you move the mouse.
Start by configuring the required actions in _in that(). This function is not strictly part of the game loop as it only runs once, at startup. For a game that requires mouse input, you’ll need to make a special call to enable it:
function _init()
poke(0x5f2d, 1)
end
the _to update The function handles user input and updates to the state of your game. To support mouse input, this is the place to keep track of the pointer’s position.
function _update()
mousex = stat(32)
mousey = stat(33)
end
Note that variables in PICO-8 are global by default, so any function in your program will have access to them. mouse x Y brownish. PICO-8 includes stat as a built-in function. Provides various details about the current environment, including mouse, keyboard, and date/time.
Finally, define a _draw function to display a pointer on the screen. For a crosshair pointer, you can draw a vertical line and a horizontal line at the current mouse position:
function _draw()
cls()
line(mousex, mousey - 4, mousex, mousey + 4)
line(mousex - 4, mousey, mousex + 4, mousey)
end
the line() The function is another one that is built into PICO-8. Four arguments are needed to draw a line between two points: the x and y coordinates of point a, and the x and y coordinates of point b.
the cls() function clears the screen. Try removing this line to see what happens when you don’t clear the screen.
Draw the remaining elements of the game
Tic-tac-toe takes place on a grid of nine squares. You can recreate this using four lines: two horizontal and two vertical. Since a PICO-8 screen is 128×128 pixels, each square can be 42 pixels long, leaving 1 pixel for the grid lines:
function draw_grid()
line(42, 0, 42, 127)
line(85, 0, 85, 127)line(0, 42, 127, 42)
line(0, 85, 127, 85)
end
The next task is to draw a symbol, a zero or a cross, in a given square. You can use this to display both the current “board” and a “preview” symbol when the user hovers over an empty square.
Drawing a “zero” is simply a case of calling PICO-8 circuit() function:
function draw_nought(x, y)
circ(x, y, 10)
end
Drawing a “cross” is a bit more complicated. You will need to draw two intersecting diagonal lines:
function draw_cross(x, y)
line(x - 10, y - 10, x + 10, y + 10)
line(x + 10, y - 10, x - 10, y + 10)
end
The remaining drawing function is draw_square. Draws the symbol (cross or zero) on a given grid square, if any. However, to complete that feature, you will need to access the game state.
Game Status Tracking
To play a meaningful game, you’ll need a way to track which symbol, if any, is on which space. While PICO-8 supports multidimensional arrays, it’s just as easy to use a one-dimensional array for simple use cases like this. You only need to keep track of nine different locations on the grid:
p = {"", "", "", "", "", "", "", "", ""}
You can access the elements of this array using familiar syntax from other languages. For example:
p[1] = "0"
if (p[9] == "x") then
PICO-8 arrays start at index 1, not index 0 as is typical in many languages.
You can translate from a column and row reference to an index on this array and vice versa. Using the fact that the screen is 128×128 and the grid is 3×3, you can convert the x and y coordinates to grid references like this:
mousecol=flr(mousex/42.666)
mouserow=flr((mousey/42.666) % 3)
Handling user input
You can track mouse button presses in a similar way to mouse position. In _to updateuse a global variable to keep track of state and call statistics(34) To check if the left mouse button is pressed:
if (down and stat(34) == 0) then
down = false
endif ((not down) and stat(34) == 1) then
down = true
end
keeping track of down allows you to identify when the button is first clicked, since stat() only tells you if the button is pressed when it runs.
Win condition detection
A game of tic tac toe ends when:
- There are three instances of the same symbol in the same row, column, or diagonal.
- All seats are full.
the check_for_win The function determines if the current state of the game satisfies either of the two conditions. There are several ways to handle this, but for a simple game like tic tac toe, a brute force approach is usually the easiest:
for a = 1,3 do
if p[a] != "" and p[a] == p[a + 3] and p[a] == p[a + 6] then
finished = true
winner = p[a]
end
end
This code excerpt shows the column check. Find matching nonempty values at the appropriate positions in the main grid array. If it discovers a winning line, this code sets two global variables which then drive the display of the winning message in _display().
packaging your game
There is no point in creating a game unless others can play it. Fortunately, PICO-8 has you covered. You can create an executable by running the following at the PICO-8 command line:
- Write RUN to start your program.
- While running, press F2 to take a screenshot. PICO-8 will use this as a thumbnail for your game.
- Write EXPORT TACTACT.HTML. HTML is the fastest format to generate and the most portable.
PICO-8 also supports native binary executables for Windows, macOS, and Linux. Use EXPORT TICTACTOE.BIN to create them. Whichever format you choose, PICO-8 will export your files in the cartridge folder.
PICO-8 could be just the beginning
This simple game has a lot of potential for follow-up work. You can make graphics enhancements or add some logic to CPU movements. You can also make a two player version and let the player choose their symbol.
Most PICO-8 game developers make their creations available for free. In the world of professional game development, tools like Unreal Engine, GameMaker, and Unity are more common.