Saga2D
A small Python framework for 2D games where the developer writes game logic, not engine plumbing. It renders sprites and simple shapes on the GPU through pyglet, lays out a small UI toolkit, runs a scene stack, hosts online matches and packages standalone builds. Game code never touches the backend.
Three games prove it, each in its own repository: Tribes (turn-based, Polytopia-style), Warband (real-time strategy) and Shardbound (an Eador-inspired campaign). Procedural assets — sound synthesis and a software low-poly renderer — live in sagaforge; the hosted server and website in saga-online.
Install
Requires Python 3.12 or newer. Pin the engine in a game so engine development cannot change that game's build:
uv add 'saga2d==0.2.0'
# or: python -m pip install 'saga2d==0.2.0'
Commit the game's pyproject.toml and lockfile. Upgrade the pin deliberately,
then run that game's tests before committing the upgrade. See the
release guide
for compatibility policy, publishing and testing a local engine checkout.
Develop the engine
uv sync --extra dev
uv run pytest -q # headless suite on the mock backend
Set SAGA2D_SILENT=1 to keep any pyglet-backed script off the speakers
(SAGA2D_HEADLESS=1 implies it and also hides windows).
A complete game
This example needs no image or sound files:
from saga2d import Anchor, Button, Column, Game, Label, Scene
class World(Scene):
background_color = (18, 20, 30, 255)
controls = {"escape": "close"}
def on_enter(self):
self.gold = 0
self.ui.add(Column(
Label(lambda: f"Gold: {self.gold}", text_style="heading"),
Button("Collect a coin", shortcut="Space", on_click=self.collect, width=220),
Label("Esc closes the window.", text_style="caption"),
spacing=16, anchor=Anchor.TOP_LEFT, margin=24,
))
def draw(self):
self.draw_circle(360, 220, 32, (230, 190, 90, 255))
def collect(self):
self.gold += 1
def close(self):
self.game.quit()
Game("My Game", resolution=(640, 400)).run(World())
What you get
- Two coordinate spaces.
"world"is transformed by the scene'sCamera(pan, zoom, shake) on the GPU;"screen"is for UI. Sprites default to world space;draw_rect,draw_circle,draw_line,draw_polygon,draw_textanddraw_imagetakespace=. - Measured paragraphs.
Scene.draw_paragraph(text, x, y, width)wraps against the actual font and returns the height for subsequent layout. - Hex boards.
HexGridsupplies centers, corners, picking, neighbours, weighted movement ranges and shortest paths; the game supplies terrain costs. See the cookbook. - Sprites with a logical size.
Sprite("name", size=(64, 64))draws any texture at that size, so procedural textures rendered at the display's pixel density stay crisp on HiDPI screens. - Declarative input.
controlsmaps keys and chords to methods;bind_keydoes the same at runtime; mouse events carry world coordinates and modifier keys. - A scene stack with transparent overlays, deferred push/pop, and per-scene ownership of sprites, timers and particle emitters.
- UI: reactive
Label,Buttonwith keycap shortcuts,KeyHints,Panel,Row,Column,ProgressBar,Minimap, anchors, flow layout, wrapped text and aTheme. Text is measured by the backend, so layout fits. See wrapped labels, measuring a tree and button shortcuts. - Actions, tweens, timers, particle emitters, frame animation, transient
effects, audio with
master/music/sfxchannels and a silent driver, JSON save slots and persisted settings. - Multiplayer.
MatchHost/MatchClientfor LAN,OnlineClientand the sharedMatchMenu/MatchLobbyfor hosted rooms, andsaga2d.server, the authoritative room server any game registers with through aGameSpec. Follow the counter-room tutorial and the transport notes. - Packaging.
saga2d.packagingbuilds, verifies and installs standalone games with PyInstaller from a ten-linetools/package.py. - Testing. A mock backend that records every draw call,
render_scenefor offscreen screenshots you can look at, a CPU budget for long checks, paced native frames, and a counter test game for the server.
See DESIGN.md for the architecture and the reasoning behind the cuts.
Release files for saga2d 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| saga2d-0.2.0.tar.gz | 419.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| saga2d-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 740.9 kB
Release files / saga2d-0.2.0.tar.gz
| Download URL | saga2d-0.2.0.tar.gz |
|---|---|
| Size | 419.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f2998d1f0e6c99766efa5a41aa485929d25f746c93f978da26875f8641a74c1a
|
|
BLAKE2b-256 checksum How to use checksums |
2a5c438202920ce7f52c445fa918bfbfa74f98f914a1a7484fbd469e4c622203
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.10
|
Release files / saga2d-0.2.0-py3-none-any.whl
| Download URL | saga2d-0.2.0-py3-none-any.whl |
|---|---|
| Size | 321.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6256579c26616cfa6815d1be492ab1d01c6661fb186cb13e9b220dc1af0a929b
|
|
BLAKE2b-256 checksum How to use checksums |
5deff976d33480e16461eb7c8b466724297748e76e5ccd2727f8f627848391a2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.10
|