Special Functions

In part 1, we wrote some commands called function calls to tell Pico-8 to draw text and shapes on the screen. But we can write our own functions too. And there are 3 special functions that, if you include them in your code, will be called by Pico-8 itself while your program is running! This forms what’s known in game programming as a game loop. Games must run code constantly to calculate game logic, move characters around on the screen, respond to the user pressing controller buttons and so on. And then it must display what’s happening on the screen many times per second (the rate of this is called frames per second or FPS). This means there is a constant ’loop’ that consists of: updating the game’s state, drawing it to the screen, and repeating.

In Pico-8, the game loop is implemented by making use of special functions called _update and _draw. A third special function, _init, is simply called when the program starts up. Let’s explore each of these functions now. You can remove the existing code from part 1 (but keep the sprite).

_init

_init is simply an initialization function which is called only once, when the program starts up. This can be used to set the initial state of the program so that _update and _draw have something to do.

In my games I always like to have a way to print some message on the screen to help me understand what’s going on as I’m making changes. This won’t be displayed when the game is released, but will come in handy while we’re developing the game. This kind of message is called a debug message. When the game first starts up, we won’t have anything to debug, so the message will be empty (blank).

1
2
3
4
5
function _init()
 _debug=""
 player_x=0
 player_y=64
end

In this _init function, we create a new variable called _debug. A variable in a program is something that has a name and a value that can change. Variables can be named anything, but I’ve used the convention of prefixing this variable with an underscore (_) to show that it’s a special kind of variable that’s not related to the game itself. It’s important to give variables and functions meaningful names so that you know what they do and you can find them again later. Using conventions like this help to differentiate variables so you can easily tell at a glance the kind of thing it’s going to do.

We assign a value of "" to the _debug variable. Assignment is done using the equals (=) operator (symbol). The double quotes ("") make a new string (text) value with nothing inside, so the string is empty.

player_x and player_y are coordinates that we’ll use in a moment. Note that coordinates in Pico-8 start from 0 and go up to 127.

_update

_update is meant for handling game logic and user input. It is usually called at a rate of 30 frames per second, which allows for smooth movement of graphics on screen even on old or less powerful computers.

1
2
3
function _update()
	player_x = player_x+1
end

In this function we assign a new value to player_x using the addition operator to increase the value by 1. This means that the player_x variable will increase by 1 each update, so it will usually increase by 30 each second.

Since increasing (incrementing) a variable’s value like this is a very common thing to do, there’s a shorthand way of writing it:

1
player_x += 1

This does the exact same thing as above with less code, which is very important in Pico-8. However, you might find the first version easier to read, so use whichever you like for now.

_draw

_draw, like _update, is usually called at 30 frames per second and is used to draw things on the screen.

1
2
3
4
function _draw()
	cls(2)
	spr(1,player_x,player_y)
end

Now in our _draw function, we use two functions that we’ve already seen, cls() and spr(). Except now instead of using fixed values for the sprite’s coordinates, we’re using the player_x and player_y variables. This technique is essentially how we can automatically move things around on the screen. Imagine if we had to write the new coordinates by hand every frame - we’d need hundreds of lines of code!

Running the Program

Run the program with Ctrl-R. You’ll see the sprite move across the screen and disappear. In fact it is still moving (our _update function is still running), but we can’t see it anymore because the coordinates are off the screen.

_update vs _draw

At this point, you might wonder why _update and _draw are two separate functions. They are both called at 30fps, so could we put all the code together in _draw()? Yes we could, but it’s a bad idea. Earlier I said that _update and _draw are usually called at 30fps, but what if the program or computer is very slow to run? In that case, Pico-8 can’t call both functions at 30fps. So instead, it will try to catch up by calling _draw at a lower framerate (eg. 15fps) instead, and call _update two times per frame. This approach ensures that even if Pico-8 is running slowly, the game’s logic will still run at 30fps so the player is less likely to notice anything strange in the game. Running at a lower framerate to catch up is called dropping frames. Now, imagine if we used _draw() for everything. This would mean that sometimes, our code that calculates game logic wouldn’t run at all! This could be disastrous as it might mean that the game doesn’t respond to use input, or the player doesn’t collide with walls and appears to jump through them, or other strange effects. This is why game logic should go in _update and _draw should only be used for drawing functions like cls() and spr().

Interesting note: instead of _update, you can write a function called _update60 which has the same role but it runs at 60fps. This can result in smoother gameplay, but requires a faster computer to run.

Next Steps

Save the program with Ctrl-S and try to experiment. Can you make the sprite move in different directions? How about adding a second sprite? Try moving shapes around too (hint: we saw the rect function for drawing rectangles in part 1).

Now is a good time to introduce the Pico-8 cheat sheet. This is a handy reference to all the built-in functions in Pico-8, and I refer to it all the time when making my games. You can find out more about each function and examples of how to use them on the Pico-8 wiki.

In the next part of the tutorial, we’ll create some helper functions that will be used many times in our program, and after that we’ll get started on the actual game!