Spaceship Engine is a small Python engine for real-time ASCII/terminal games. It provides a fixed-timestep game loop, entity + sprite rendering with z-order, a basic HUD system, and keyboard input via pynput.
From a checkout of this repo:
python -m pip install -e .Or, if published to PyPI:
python -m pip install spaceship-engineRequires Python 3.8+.
Create a game, add an entity, and run:
from spaceship.game import Game
from spaceship.render.entity import Entity
from spaceship.utils.math import Vector
class Player(Entity):
def __init__(self, game: Game):
super().__init__(game, Vector(0, 0))
self.sprite.load(
" O \n"
"/|\\\n"
"/ \\",
priority=10,
)
def update(self, dt: float):
speed = 30
if self.game.input.is_char_held("w"):
self.position += Vector(0, -speed) * dt
if self.game.input.is_char_held("s"):
self.position += Vector(0, speed) * dt
if self.game.input.is_char_held("a"):
self.position += Vector(-speed, 0) * dt
if self.game.input.is_char_held("d"):
self.position += Vector(speed, 0) * dt
if self.game.input.is_char_held("q"):
raise SystemExit(0)
def init():
game.add_entity(Player(game))
def update(dt: float):
pass
game = Game(init_hook=init, update_hook=update, border=True)
game.run()- Fixed-timestep updates at 60 Hz (
fixed_dt = 1/60), with a cap to avoid runaway catch-up after long stalls. - Rendering runs as fast as possible and uses a terminal diff to redraw only changed cells.
- Add/remove world objects with
game.add_entity(entity)/entity.kill().
- Subclass
Entityand implementupdate(self, dt: float). - Use
self.position(aVector) to move in world space. - Each entity owns a
Sprite(self.sprite) that is rendered by theCamera.
- Load ASCII art via
sprite.load(raw_string, priority=1). - Z-order:
sprite.priority(higher numbers render on top). - Center marker: include exactly one
\tin the raw art to mark the sprite center (the tab is removed). - Transparency: the engine treats the bell character
\aas transparent (that cell is skipped during rendering).
The camera transforms world positions into screen positions. Available modes:
CameraMode.CENTERCameraMode.TOP_LEFT,CameraMode.TOP_RIGHTCameraMode.BOT_LEFT,CameraMode.BOT_RIGHT
HUD elements are templated strings with backtick-delimited placeholders:
from spaceship.render.hud import HUDElement, HUDAlignment
score_hud = HUDElement(
template="Score: `score`",
values={"score": "0"},
align=HUDAlignment.RIGHT,
)
game.hud.add_bottom_hud(score_hud)
# Later:
score_hud.set_value("score", "123")Top HUD height is computed automatically based on alignment groups; bottom HUD renders one line per element.
- Check held keys:
game.input.is_char_held("w"), orgame.input.is_key_held(key)for special keys. - Register callbacks:
hook_to_keypress(fn)/hook_to_keyrelease(fn)(and unhook variants).
The grid size and margins live in spaceship.utils.constants:
SIZE_X,SIZE_Y: logical grid dimensions (characters)LEFT_MARGIN,RIGHT_MARGIN,TOP_MARGINCELL_WIDTH: terminal columns per cellCHAR_ASPECT: used by camera Y scaling
There is also spaceship.utils.constants.configure(...), but note that many engine modules import these constants at import time. For best results, set constants before importing/constructing the engine (or edit constants.py in a fork).
After pip install -e .:
python -m spaceship.demo.demoControls: WASD to move the rock, Q to quit.
- Keyboard input uses
pynput, which may require accessibility permissions (macOS) or an active desktop session (some Linux setups). - ANSI escape codes are used for rendering; use a modern terminal (Windows Terminal, iTerm2, etc.).