Skip to main content

texastoast

PyPI Python versions CI License

Python RPG engine with I2C hardware abstraction for magmacrunch game systems.

A tkinter-based 2D game engine inspired by adenosine, with optional I2C support for Raspberry Pi hardware.

Install

pip install texastoast

Optional extras:

pip install "texastoast[sprites]"   # Pillow, for sprite sheet cropping
pip install "texastoast[hardware]"  # smbus2, for I2C controllers on Raspberry Pi

Neither is required — the engine runs on keyboard input with no extras installed.

From source

git clone https://github.com/magmacrunchmedia/texastoast.git
cd texastoast
pip install -e ".[dev]"

Quick Start

from texastoast import Game, CanvasRenderer, TileMap, Entity, KeyboardInput

game = Game(title="My Game", width=400, height=300, fps=30)
renderer = CanvasRenderer(game.canvas, 400, 300)
keyboard = KeyboardInput(game.root)

tilemap = TileMap([
    [1, 1, 1, 1, 1],
    [1, 0, 0, 0, 1],
    [1, 1, 1, 1, 1],
], tile_size=20, solid_tiles={1})

player = Entity(x=40, y=24, width=14, height=14, speed=100)  # 100 px/second

def update(dt):
    state = keyboard.poll()
    player.move(state.dx, state.dy, dt, tilemap)
    renderer.camera.follow(player.center_x, player.center_y,
                           map_width=tilemap.width, map_height=tilemap.height)

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()

Movement contract

  • speed is in pixels per second, not per frame.
  • move() takes the frame's dt, so movement is frame-rate independent.
  • Diagonals are normalized: holding two directions is the same speed as one.

Upgrading from 0.1.x

Entity.move() gained a required dt argument, and a few defaults changed. See CHANGELOG.md or the migration guide.

player.move(state.dx, state.dy, tilemap)      # 0.1.x — px per frame
player.move(state.dx, state.dy, dt, tilemap)  # 0.2.0 — px per second

Examples

Examples and the tile editor live in the repository, not in the installed package — clone the repo to run them.

Example Description
examples/hello_world.py Minimal movement demo
examples/tilemap_demo.py Walk around a larger map
examples/sprite_demo.py Animated character sprites
examples/rpg_demo.py NPCs, dialogue, menus, HUD
examples/game_template.py Full game starting point
examples/magma_hub_demo.py I2C controller input
tools/tile_editor.py Tile map editor GUI

Documentation

Full guides live in the wiki. The reference below covers the whole public API.

API Reference

Core

from texastoast import Game, Config, GameLoop

game = Game(title="My Game", width=640, height=480, fps=30)
game.set_update(update_fn)  # def update(dt: float): ...
game.set_render(render_fn)  # def render(): ...
game.on_close(cleanup_fn)   # runs on quit(), including the window's X button
game.start()

# Embed in an existing tkinter app (the caller keeps ownership of the root
# and runs its own mainloop):
game = Game(width=640, height=480, root=my_frame)

Rendering

from texastoast import CanvasRenderer, Camera

renderer = CanvasRenderer(game.canvas, 640, 480)

# A tile is drawn when its id has a color; ids you leave out stay transparent.
renderer.draw_tilemap(tilemap, {0: "#7cb342", 1: "#5d4037"})
renderer.draw_tilemap(tilemap, colors, skip_tiles={0})  # or skip explicitly

renderer.draw_rect(x, y, w, h, color)
renderer.draw_image(x, y, photo_image)
renderer.draw_text(x, y, text)                  # world space, follows the camera
renderer.draw_hud_text(x, y, text, fill="#fff") # screen space, ignores the camera

# Camera
renderer.camera.follow(target_x, target_y, map_width=800, map_height=600)
renderer.camera.set_position(x, y)
renderer.camera.world_to_screen(wx, wy)
renderer.camera.is_visible(x, y, w, h)

World

from texastoast import TileMap, Entity, AABB

# Tile map
tilemap = TileMap(grid_data, tile_size=16, solid_tiles={1, 2})
tilemap = TileMap.from_file("map.json", tile_size=16)
tilemap.save("map.json")
tilemap.get(col, row)          # -> tile_id, or -1 out of bounds
tilemap.is_solid(col, row)     # -> bool (out of bounds counts as solid)
tilemap.is_solid_at(world_x, world_y)

# Entity — speed is px/second, move() takes dt
player = Entity(x=0, y=0, width=16, height=16, speed=100)
player.move(dx, dy, dt, tilemap)  # with collision; omit tilemap to skip it
player.vel_x, player.vel_y        # px/second
player.aabb                       # -> AABB for overlap checks
player.collides_with(other_entity)

Collision resolves each axis separately, so entities slide along walls rather than sticking. A blocked entity stops flush against the wall, and fast movement is sub-stepped so nothing tunnels through a tile.

Input

from texastoast import KeyboardInput, InputState

keyboard = KeyboardInput(game.root)
game.on_close(keyboard.destroy)  # release the key bindings on exit
state = keyboard.poll()

state.up, state.down, state.left, state.right  # bool
state.a, state.b, state.start, state.select     # bool
state.dx, state.dy                               # float (-1, 0, 1), raw axes
state.is_any_direction()                         # bool

dx/dy are raw axis reads and are not normalized — Entity.move does that for you. If you integrate position yourself, normalize before scaling by speed.

I2C

Optional I2C support for connecting hardware controllers via Raspberry Pi.

from texastoast import I2CBus, MagmaHub, MagmaHubInput, CompositeInput

# Direct I2C
bus = I2CBus(1)
hubs = MagmaHub.scan_buses(bus_numbers=[1])
hub = hubs[0]
hub.poll()       # -> [ControllerState, ...]
hub.connected    # -> True only while reads are actually succeeding

# Input adapter (same interface as KeyboardInput)
hub_input = MagmaHubInput(hub, controller_index=0)
state = hub_input.poll()

# Auto-fallback composite
controls = CompositeInput(keyboard, hub_input)
state = controls.poll()  # uses hub if connected, else keyboard

Without smbus2, or with no bus present, I2CBus runs in mock mode: reads return None rather than fabricated zeros, hub.connected stays False, and CompositeInput falls through to the keyboard.

UI

from texastoast.ui import DialogueBox, Menu, HUD

# Dialogue
dialogue = DialogueBox(game.canvas, 640, 480)
dialogue.show("Hello, world!", speaker="NPC", on_complete=callback)
dialogue.dismiss()

# Menu
menu = Menu(game.canvas, 640, 480)
menu.show(["Play", "Settings", "Quit"],
          on_select=lambda i, label: print(label),
          on_cancel=lambda: menu.hide())
menu.move_up()
menu.move_down()
menu.confirm()

# HUD
hud = HUD(game.canvas, 640, 480)
hud.add_stat("hp", "HP", value=100, max_value=100, color="#e94560")
hud.set_stat("hp", 75)
hud.add_text("score", "Score: 0", 10, 10, fill="#fdd835")
hud.set_text("score", "Score: 100")
hud.render()

Design Philosophy

  • No opinions — engines provide systems, you wire them together
  • Configurable — pass callbacks and data, don't inherit from base classes
  • Tiny — small, focused modules with minimal dependencies
  • Graceful fallback — I2C hardware is optional, keyboard always works
  • Testable — game logic doesn't depend on tkinter

Contributing

See CONTRIBUTING.md for setup, tests, and the release process.

License

Apache-2.0. Copyright 2026 magmacrunch media.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

texastoast-0.2.0.tar.gz (43.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

texastoast-0.2.0-py3-none-any.whl (31.4 kB view details)

Uploaded Python 3

File details

Details for the file texastoast-0.2.0.tar.gz.

File metadata

  • Download URL: texastoast-0.2.0.tar.gz
  • Upload date:
  • Size: 43.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for texastoast-0.2.0.tar.gz
Algorithm Hash digest
SHA256 2c30d590126b72fa6b98214ab7aca7581372ee014e50bb6b3c0e15e09a88cd2a
MD5 ba1de708ca2461073aea3d884e900db5
BLAKE2b-256 2a1553c49df0ff8118062910b3ee4fa53dd66d18817ea16330e7155af900bf32

See more details on using hashes here.

File details

Details for the file texastoast-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: texastoast-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 31.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for texastoast-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8ac52a20304b4b37be8a87dddc34c29b1e907594ee5c062b2e06730c690fa422
MD5 5fa50bb760d2f55d98c6617687ad3180
BLAKE2b-256 a85726b87ab78c6f2a424d6dd27ac8bb77e033dd8517d4aff92da57bcd38c5aa

See more details on using hashes here.

Release history Release notifications | RSS feed

0.11.2

2 files

0.11.1

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

This release

0.2.0 This release

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page