<- /posts

learning pico-8, part 1

Hands-on experience

As many programmers, I always wanted to try out game development. It was always a process that I mistified a lot since most of the things that I experienced about video games were quite complex. I do remember that back in the days of the GameBoy Advance (I’m old but not that old), I would boot up The Legend of Zelda: A Link to the Past and end up marveled at how someone could create such a thing.

Well, here I am finally getting into the gist of it and trying to learn game development the old fashioned way. By this I mean that I would be creating and learning 2D game development, not trying to do RollerCoaster Tycoon in assembly.

I wanted to start things easily with not too much to worry about, I spend some time in my career doing research as to what programming languages and tools were used to create videogames. Well, to my surprise I ended up finding PICO-8, a minimalistic game engine that you can interact with via Lua.

I had a little bit of experience writing lua code since I use Neovim as my primary editor, so figured it would be a nice way to program games since I was already familiar with the language.

First steps

Now, after downloading (and purchasing) PICO-8’s engine, I was presented with a black screen that seemed to be a… terminal? Splendid! There was a message that instructed me to type HELP, and so I did. A bit more information popped up that prompted me to press Escape, and again, I did.

I was now inside some sort of editor that had a really ugly font. No offense to the people behind PICO-8, you’ve created an amazing little piece of software, but you could’ve chosen a more readable font, right? I know it fits into the aesthetic so nothing to be too worried about. If things were not going as expected I figured I could always write the code elsewhere.

Now, after purchasing PICO-8 I was presented with some “guides” on how to interact with the software, and I decided to go over Game Development with PICO-8, written by Dylan Bennet. It is a very straightforward way of starting developing games in PICO-8, containing a walkthrough about the features of the engine and editor, as well as some game examples to sift through, nice!

The engine

PICO-8 has three core functions to interact with the engine, _INIT, _UPDATE, and _DRAW. Each does what you’d expect: initialize some things, update the canvas, and draw stuff on the canvas.

But, it does not stop there. PICO-8 also comes with a few built in tools that simplify stuff a lot when creating games. It comes with a sprite editor, tile editor, sound editor, and a sound pattern creator. Amazing, amazing lexaloffle. You created a thing of beauty.

So, the core functionality essentially revolves around updating or “checking” the current state of the engine on each frame while running games, or cartridges as PICO-8 likes to call them.

The learning project

Now, what to do. How to learn. What to build. The possibilities are endless. I decide to recreate the simplest game ever craeted, in fact, it was the first video game, ever. Pong.

Yes, as simplistic as that sounds, I need to learn how this thing works, and for that I decided to start small to just… start. Here’s my plan and the things I want to achieve with this project for me to learn stuff. I will only call Pong succssfully recreated whenever I achieve the following things:

  • Create sprites
  • Create and trigger sounds
  • Render and animate sprites
  • Read user inputs
  • Handle collissions between sprites
  • Handle state for score keeping

Simple enough, right? Then let’s start coding pong.

Core elements

I want to start by doing everything in a very “dirty” way so then I can clean up code and do everything in a “proper” way. You get me? Ok, so, to start things off, I want to create the sprites that put everything together. In pong, we have two core sprites: a ball and a block, a rectangle of sorts. Each rectangle represents a player and the ball represents, well, the ball.

Using PICO-8’s sprite editor I am able to create two sprites, each sprite will only use the color white to mimic the original game color coding. Now, how do we render these sprites. The engine let’s us reference sprites by running the SPR function which accepts an integer, referencing the sprite tile we just created. The ball holds the sprite 0, and the rectangles hold the sprite 1. Let’s try rendering these sprites on screen now:

FUNCTION _INIT()
END

FUNCTION _UPDATE()
END

FUNCTION _DRAW()
  SPR(0, 10, 10) -- Draw the ball at an x and y coordinate
  SPR(1, 20, 20) -- Draw the rectangle at an x and y coordinate
END

Amazing! Now we are able to see on-screen the ball we just created and a rectangle just to the right of it.

For our next trick, let us handle user inputs. For this, we can call the BTN function. As its name implies it reads out if a key was pressed on our keyboard. PICO-8 is able to read six keys as inputs:

For movement:

  • Button 0 represents Left
  • Button 1 represents Right
  • Button 2 represents Up
  • Button 3 represents Down

Our action keys:

  • Button 4 represents “O”
  • Button 5 represents “X”

Amazing, we’ll check for two inputs, 2 and 3 since in Pong, the users can only move their “players” up and down. First off, I want to check that our input recognition works, just because, well, I’m learning. I will create a global variable called “message” that will be set to an empty string for now. Then, after we register either up or down, I will change this variable into “up” or “down”, respectively. Our code looks like this:

FUNCTION _INIT()
  MESSAGE=""
END

FUNCTION _UPDATE()
  IF BTN(2) THEN
    MESSAGE="UP"
  END

  IF BTN(3) THEN
    MESSAGE="DOWN"
  END
END

FUNCTION _DRAW()
  SPR(0, 10, 10)
  SPR(1, 20, 20)
  PRINT(MESSAGE, 30, 30) -- Draw the message at an x and y coordinate
END

Now we have a way of telling if the user is intending to move up or down! Let’s now bind this into the sprite itself. We first need to create a variable to hold the y value of the player. This will allow us to change its value whenever we want to go up or down.

FUNCTION _INIT()
  Y=50 -- A y starting value for the rectangle
END

FUNCTION _UPDATE()
  IF BTN(2) THEN
    Y=Y-1
  END

  IF BTN(3) THEN
    Y=Y+1
  END
END

FUNCTION _DRAW()
  SPR(0, 10, 10)
  SPR(1, 20, Y)
END

User inputs now are reflected in the rectangle sprite by moving it up and down. In the next part we will be handling more things regarding the movement of the user and the ball itself. Onto the next one…