Game Loop

Pyxen runs your game at a fixed 30 frames per second. You don’t write a main loop — you define two functions and Pyxen calls them for you.

start() and update()

def start():
    # called once, at the beginning
    pass

def update():
    # called every frame, 30 times per second
    pass

start() is where you set up your world: spawn entities, place them, configure the scene.

update() is where your game logic runs: read input, move entities, check conditions, update state. After each call to update(), the engine renders the frame.

Fixed frame rate

Every frame takes exactly the same amount of time: 1/30th of a second (~33ms). This means:

You can still use time.dt (delta time) if you prefer time-based movement, but it will always be 1/30.

Frame order

Each frame, Pyxen runs these steps in order:

  1. Input — read keyboard, mouse, touch, and gamepad state
  2. update() — your code runs
  3. Collision — the engine resolves grid bodies and calls collision callbacks
  4. Render — the engine draws all visible entities

This means when you read input in update(), the values are fresh. When you move an entity, collision resolution happens right after.

Collision callbacks

If you define on_collision or on_tile_collision at the top level, the engine calls them during the collision step:

def on_collision(entity, other):
    # two grid bodies overlapped
    pass

def on_tile_collision(entity, tile):
    # a grid body hit a tilemap tile
    pass

These run after update() but before rendering.

Timing

The pyxen.time module gives you:

PropertyValueUse
time.frame0, 1, 2, ...Frame counter since start
time.dt0.0333...Delta time (always 1/30)
time.t0.0, 0.033, 0.066, ...Elapsed time in seconds
time.fps30Frames per second

For timed events, you can use either approach:

# Frame-based: fire every 60 frames (2 seconds)
if time.frame % 60 == 0:
    fire()

# Time-based: same thing
if int(time.t * 30) % 60 == 0:
    fire()

Frame counting is usually simpler and more predictable.