Python RPG engine with I2C hardware abstraction for magmacrunch game systems
Run the real engine in your browser under Pyodide. Four examples, playable with the keyboard, no install required.
Game, entities, tile maps, camera, input, and the UI widgets, plus the magmascript domain table.
A tkinter-based 2D game engine by magmacrunch media, inspired by adenosine and built for the same kind of small RPGs, but in Python, and able to read real controllers over I2C when it runs on a Raspberry Pi. Game logic never depends on tkinter, and hardware degrades gracefully to the keyboard when it is absent.
It also ships a hardware dev kit: a bus-level simulator that runs the real I2C stack against imaginary controllers, a live test bench for bringing real ones up, and session record/replay that turns a captured session into a regression test. You can build for the Magma Hub before the Magma Hub exists.
pip:
pip install texastoast
optional extras: [sprites] for Pillow sprite sheets, [hardware] for I2C,
[audio] for a real sound mixer
from texastoast import CanvasRenderer, Entity, Game, KeyboardInput, TileMap
game = Game(title="my game", width=320, height=240, fps=30)
renderer = CanvasRenderer(game.canvas, 320, 240)
keyboard = KeyboardInput(game.root)
tilemap = TileMap([[1, 1, 1], [1, 0, 1], [1, 1, 1]], tile_size=16, solid_tiles={1})
player = Entity(x=20, y=20, width=12, height=12, speed=100)
def update(dt):
state = keyboard.poll()
player.move(state.dx, state.dy, dt, tilemap)
def render():
renderer.clear()
renderer.draw_tilemap(tilemap, {0: "#7cb342", 1: "#5d4037"})
renderer.draw_rect(player.x, player.y, player.width, player.height, "#e94560")
game.set_update(update)
game.set_render(render)
game.start()
Once a game has more than one screen, hand the loop to a scene stack. Pushing a
scene freezes the ones below it, so a pause menu needs no paused flag
and no early-return chain, the modality is the stack.
from texastoast import SceneStack
stack = SceneStack()
class PauseScene:
render_below = True # the frozen world stays visible underneath
def on_enter(self): menu.show(["Resume", "Quit"], on_cancel=stack.pop)
def on_exit(self): menu.hide()
def update(self, dt): pass
def render(self): menu.render()
class WorldScene:
def update(self, dt): ...
def render(self): ...
def handle_key(self, event):
if event.keysym == "Escape":
stack.push(PauseScene()) # world freezes; no flag exists
stack.push(WorldScene())
game.set_update(stack.update)
game.set_render(stack.render)
game.bind_key("<Key>", stack.dispatch_key)
A scene is anything with update(dt) and render(), there is no base class to subclass.
texastoast registers itself as the texastoast domain (aliased tt),
so a .mgs script can drive the engine with no glue code. Needs magmascript 3.2+;
neither package depends on the other.
g = tt.game({"title": "hello", "width": 400, "height": 300, "fps": 30})
r = tt.renderer(g, 400, 300)
kb = tt.keyboard(g)
world = tt.tilemap([[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1]], 20, [1])
player = tt.entity({"x": 40, "y": 30, "width": 14, "height": 14, "speed": 100})
update = fn(dt) {
s = kb.poll()
player.move(s.dx, s.dy, dt, world)
if s.a { player.speed = 220 } else { player.speed = 100 }
}
Apache-2.0 · Copyright 2026 magmacrunch media