Skip to content

Migrating to 0.2.0

magmacrunchmedia edited this page Aug 24, 2026 · 1 revision

Migrating to 0.2.0

0.2.0 is a correctness release. Most of it is invisible, but three changes will touch your code. The whole list is in the CHANGELOG.

1. Entity.move() takes dt

This is the one that matters.

player.move(state.dx, state.dy, tilemap)       # 0.1.x
player.move(state.dx, state.dy, dt, tilemap)   # 0.2.0

dt is already in scope — it is the argument your update function receives.

Why

In 0.1.x, speed was applied per frame, not per second. speed=100 — the value in the README quickstart and every example — moved the entity 100 px every frame: 3000 px/s at 30 fps, enough to cross a small map between two frames. The docs described speed as a walking pace; the code did something else.

In 0.2.0 speed is pixels per second and movement is frame-rate independent: a game at 30 fps and the same game at 60 fps now move at the same speed.

What this means for your numbers

If you tuned a speed by trial and error against the old behavior, it was per-frame. To keep the same feel, multiply by your frame rate:

Entity(speed=3)     # 0.1.x: 3 px/frame at 30 fps
Entity(speed=90)    # 0.2.0: 90 px/second — the same motion

Realistically, most 0.1.x games used values like speed=100 that were far too fast to play, so re-tune rather than convert. Something in the 60–150 px/s range is a comfortable walk.

Missing the argument

Forgetting dt raises immediately rather than misbehaving quietly:

TypeError: Entity.move() missing 1 required positional argument: 'dt'

Passing a tile map positionally where dt belongs also fails loudly, with a TypeError: unsupported operand type(s) for *: 'int' and 'TileMap'. Use the keyword form if you want to be certain:

player.move(state.dx, state.dy, dt, tilemap=tilemap)

2. Tile id 0 now renders

draw_tilemap used to skip tile id 0 unconditionally, so the {0: "#7cb342"} grass color that every example passed was silently discarded and the window background showed through instead.

Now a tile is drawn when its id has an entry in tile_colors, and skipped when it does not. If you were relying on 0 being invisible, either leave it out of the color map or say so explicitly:

renderer.draw_tilemap(tilemap, {1: "#5d4037"})              # 0 has no color
renderer.draw_tilemap(tilemap, colors, skip_tiles={0})      # or skip it

Ids with no color no longer fall back to white either, so stray white squares from unmapped ids are gone.

3. I2C reads report failure

I2CBus.read_byte_data() and read_i2c_block_data() return None when the read fails or the bus is in mock mode. They used to return 0x00 bytes, which was indistinguishable from a controller reporting "nothing pressed".

The consequence was worse than it sounds: MagmaHub.connected became True after the first poll even with no hardware attached, so CompositeInput latched onto the dead hub and keyboard input stopped reaching the game entirely.

If you call the bus directly, handle None:

data = bus.read_i2c_block_data(0x08, 0x00, 2)
if data is None:
    ...  # no device

If you use MagmaHub or CompositeInput, this now simply works the way it was documented to.

Also worth knowing

These are not breaking, but they change how things behave:

  • Diagonals are normalized. Holding two directions used to move 1.41× faster than one. Diagonal movement is now the same speed as straight, which is a little slower than before.
  • Walls are solid at speed. Movement is sub-stepped, so a fast entity can no longer pass through a wall. If something relied on that, it will now stop.
  • Entities stop flush against walls. A blocked entity used to revert to its previous position, leaving a gap of up to a full tile. It now touches.
  • Check your spawn points. Because collision actually works, an entity placed inside a solid tile is genuinely stuck. Two of the shipped examples had this bug and were fixed in 0.2.0 — check yours.
  • Maps from the tile editor. The editor used to mark every non-zero tile id as solid, so saved maps had grass and NPCs as walls. Re-open old maps in the editor and set the solid checkboxes, or edit solid_tiles in the JSON.

New in 0.2.0

  • Game(root=...) to embed the game in an existing tkinter app or a test.
  • Game.on_close(fn) for teardown, run on quit() and on the window's X button.
  • draw_tilemap(..., skip_tiles=...).
  • py.typed — type checkers now see the annotations.

Clone this wiki locally