The game loop is the mechanism that drives a game's logic and rendering processes. Because each cycle of the loop updates the display, it is important for the loop to run at regular intervals to keep the animation smooth and the game feeling responsive to user input.
PICO-8 has a built-in game loop that runs 30 times per second. You use this in your code by defining three functions: _init(), _update()
, and _draw()
.
Both _update()
and _draw()
must be defined for PICO-8's game loop to execute. _init()
is optional.
Since version 0.1.4 only _update()
or _draw()
needs to exist to enable the game loop.
You can define _update60()
instead of _update()
to have your code run at 60 times per second.
-- initialize the position to be the center of the screen
function _init()
xpos = 64
ypos = 64
end
-- update the position based on the player pressing buttons
function _update()
if (btn(0) and xpos > 0) xpos -= 1
if (btn(1) and xpos < 127) xpos += 1
if (btn(2) and ypos > 0) ypos -= 1
if (btn(3) and ypos < 127) ypos += 1
end
-- clear the screen, then draw a circle at the current position
function _draw()
cls()
circfill(xpos, ypos, 10, 8)
end
Init[]
The _init()
function is called once when the cartridge is initialized. It is not part of the main game loop. However, it is a good practice when using the game loop to put all statements that initialize the cartridge in this function, instead of using statements at the top level of your code.
PICO-8 calls _init()
automatically before starting the game loop if the game loop is being used.
Update[]
The _update()
function is where the game performs all of its logic for the next frame. This is where you read user input, update the game world simulation, and start (asynchronous) music and sound events.
The _update60()
function serves the same purpose but changes your cart to run 60 times per second instead of 30.
Draw[]
The _draw()
function is where the game does all of the work to draw the current state of the game to the screen.
PICO-8 attempts to call both _update()
and _draw()
30 times per second. If the code takes longer than 1/30th of a second to execute, PICO-8 will skip the occasional call to _draw()
so that the game world simulation has a chance to "catch up" to real time. The player is less likely to notice a missing _draw()
call than slowness in the game simulation.
Writing your own game loop[]
If for some reason you want different behavior for your game loop than is provided by _update()
and _draw()
, you can implement a custom loop in your own code.
At the end of each frame, the PICO-8 game loop copies the graphics buffer to the screen, then waits until the next tick of the frame timer. To do this in your code, call the flip()
function.
You can use the stat()
function to measure the amount of time between ticks of the frame timer is spent executing your code. If stat(1)
returns a value greater than 1, that means you passed a frame deadline without calling flip()
, and you might need to do less work during the next frame (such as skipping draw calls) to catch up. This is how the built-in game loop determines whether to skip a call to _draw()
.