โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ gameloop API Contents โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ ๐ฆ gameloop โโ ๐ Overview โโ ๐งฑ Constructor โโ โถ๏ธ Main control methods โโ โฑ๏ธ FPS control โโ ๐ Lifecycle callbacks โโ ๐๏ธ Scheduled callbacks โโ ๐งน Garbage collection helpers โโ ๐ Frame order โโ ๐ช Nested loops โโ ๐ Notes โโ โถ๏ธ Example
๐ฆ API Reference: gameloop
gameloop is the core run loop class for retroPy.
It handles:
- input polling
- delta time updates
- timer updates
- calling your
Update()and optionalDraw()functions - presenting the framebuffer
- optional FPS limiting
- lifecycle callbacks
- one-shot and repeating scheduled callbacks
๐ Overview
gameloop is the standard way to run a retroPy game.
It centralizes:
- per-frame input polling
- gameplay timing state
- scheduled callbacks
- draw presentation
๐งฑ Constructor
gameloop(update, draw=None, on_start=None, on_run=None, on_exit=None)
Creates a new game loop object.
updatemust be callabledrawmay be callable orNoneon_start,on_run, andon_exitmay be callable orNone
loop = gameloop(Update, Draw)
The constructor validates that the provided callbacks are callable.
โถ๏ธ Main control methods
run()
Starts the loop.
On native builds, this blocks until the loop exits. On WASM builds, the browser drives frames using the platform main loop.
loop.run()
exit()
Requests loop termination.
loop.exit()
restart()
Requests the loop to restart.
loop.restart()
pause()
Pauses gameplay progression for this loop.
When paused:
update()is not calledtimer(...)objects owned by this loop are not advanced- callbacks scheduled by
run_after()andevery_ms()are not advanced dt()anddt_ms()return0gTime()does not advancedraw()can still run if it was providedon_paused()can run if it was set
loop.pause()
resume()
Resumes a paused loop.
loop.resume()
โฑ๏ธ FPS control
set_target_fps(fps)
Sets the target frame rate.
fps > 0enables frame limitingfps <= 0disables the cap
loop.set_target_fps(30)
๐ Lifecycle callbacks
set_on_start(callback)
Called once when the loop starts.
set_on_run(callback)
Called every frame before input, timers, update, and draw.
set_on_exit(callback)
Called once when the loop exits.
def on_start():
print("start")
def on_exit():
print("exit")
loop.set_on_start(on_start)
loop.set_on_exit(on_exit)
You can also pass these callbacks directly to the constructor.
๐๏ธ Scheduled callbacks
The game loop includes a small built-in scheduler for millisecond-based callbacks.
run_after(delay_ms, callback)
Schedules a one-shot callback.
def later():
print("hello later")
loop.run_after(500, later)
every_ms(interval_ms, callback)
Schedules a repeating callback.
def blink():
print("tick")
loop.every_ms(250, blink)
These callbacks use the same underlying timer backend as timer(...).
Pause behavior:
- scheduled callbacks freeze while the loop is paused
Scheduler capacity
The internal scheduler has a fixed number of slots. If too many scheduled callbacks are added, the loop raises an error.
๐งน Garbage collection helpers
set_gc_frames(n)
Runs gc.collect() every n frames.
n <= 0disables the automatic collection schedule
set_gc_budget(ms)
Sets a minimum spare time budget before the automatic GC is allowed to run.
gc_now()
Runs gc.collect() immediately.
loop.set_gc_frames(30)
loop.set_gc_budget(2)
๐ Frame order
Each active frame follows this general order:
- call
on_runif set - poll input
- measure wall-clock frame time
- if not paused, update gameplay time and loop-owned scheduled work, then call
update() - if paused, keep gameplay time frozen and call
on_paused()if it was set - call
draw()if provided - handle pending runtime work
- present the framebuffer
- optionally perform GC and FPS delay
This explains why kb.*_down() works naturally inside Update(), and why timer objects advance automatically while the loop is running.
๐ช Nested loops
The implementation maintains a small loop stack, allowing nested loops up to a fixed depth. This is useful for modal subloops such as menus or dialogs, but it is still best to keep nesting intentional and limited.
Notes
- The default target FPS is initialized to
33. - On WASM builds, display presentation is routed through the browser-facing framebuffer present path instead of the normal hardware show path.
pause()pauses timer advancement as well asupdate()calls.
Example
from retroPy import *
blink_on = True
def toggle():
global blink_on
blink_on = not blink_on
def Update():
if kb.A_down():
loop.exit()
def Draw():
draw.fill(0)
if blink_on:
draw.text("Blink", 20, 20, 15)
loop = gameloop(Update, Draw)
loop.set_target_fps(30)
loop.every_ms(300, toggle)
loop.run()