Skip to main content

A Python 3D game framework powered by a C++ OpenGL backend.

Project description

speepy3d

speepy3d is a Python 3D game framework with a C++ OpenGL backend.

The backend is currently Windows-only through Win32/WGL. It uses a shader/VBO renderer while keeping simple helpers for debug grid/axes.

Features

  • Native OpenGL window creation.
  • Keyboard and mouse state: is_key_down, is_mouse_down, mouse_delta.
  • Cursor helpers: set_cursor_locked, set_cursor_visible.
  • Timing: delta_time, fps, fpsbar=True, optional set_vsync.
  • Resize and close callbacks.
  • Input bindings: Input.action(), Input.axis(), Input.vec2(), mouse sensitivity and click-to-lock mouse.
  • Input contexts for game/menu/pause bindings.
  • Game layer helpers: GameApp, AssetManager, Timer, Profiler, controllers and particles.
  • Entity/component layer with lifecycle: Component.start(), update(), late_update().
  • Transform hierarchy: parent/children entities.
  • Events/signals: EventBus, app.events, app.on(), app.emit().
  • Prefabs and scene JSON save/load.
  • Collision callbacks and trigger callbacks for entities.
  • Tween and coroutine managers for small animations and timed logic.
  • Render layers for ordered model drawing.
  • Modern OpenGL basics: Shader, Material, Mesh, Texture, Renderer.
  • MeshBatch for building many cubes into one fast mesh.
  • Built-in Shader.basic(), Mesh.cube(), Mesh.plane(), Texture.checkerboard().
  • Mesh normals and basic ambient + directional lighting.
  • Simple OBJ loader: Mesh.from_obj("model.obj").
  • Simple glTF loader: Mesh.from_gltf("model.gltf").
  • Texture loading for .png, .jpg, .jpeg, .bmp and .ppm.
  • Math: Vec2, Vec3, Mat4.
  • Scene objects: Camera3D, Transform, Model, Scene, DirectionalLight.
  • Collision helpers: AABB, PhysicsBody, PhysicsWorld, move_and_collide, SphereCollider, Ray, raycast_aabb.
  • ECS collision callbacks: on_trigger_enter, on_trigger_exit, on_collision_enter, on_collision_exit.
  • Collision layers: scene.ignore_collision(), scene.only_collision().
  • Scene queries: scene.query(), scene.find_all(), scene.find_component().
  • Asset folder loading: assets.load_folder("assets").
  • Debug drawing: DebugDraw.line(), DebugDraw.box(), DebugDraw.ray().
  • WAV sound helpers on Windows: Sound, Music.
  • Immediate UI: Label, Button, Panel, Menu, ContextMenu, UIManager.
  • Game loop helper: run(window, update, render).

Install

python -m pip install speepy3d

For local development from this source folder:

python -m pip install -e .

Quick Start

speepy3d new mygame --template platformer
cd mygame
speepy3d run

Useful CLI commands:

speepy3d new mygame --template empty
speepy3d new mygame --template menu
speepy3d new mygame --template fps
speepy3d info
speepy3d examples
speepy3d --version

Modern Renderer Example

from speepy3d import Camera3D, DirectionalLight, Input, Model, Renderer, Scene, Texture, Vec3, Window

win = Window(800, 600, "speepy3d", fpsbar=True)
win.enable_depth_test(True)

camera = Camera3D(position=Vec3(0.0, 2.0, 6.0), pitch=-15.0)
renderer = Renderer(win)
controls = Input(win, mouse_sensitivity=0.08, mouse_locked_by_click=True)
controls.action("jump", "SPACE")
controls.axis("move_x", negative="A", positive="D")
controls.axis("move_z", negative="S", positive="W")

scene = Scene(
    camera=camera,
    ambient_light=Vec3(0.36, 0.40, 0.48),
    directional_light=DirectionalLight(direction=Vec3(-0.35, -1.0, -0.45), intensity=1.1),
)
cube = scene.add(Model.cube(1.5, texture=Texture.checkerboard()))
scene.add(Model.grid(10.0, 1.0))
scene.add(Model.axes(2.0))

while not win.should_close():
    win.poll_events()
    controls.update()

    dt = win.delta_time or 1.0 / 60.0
    raw_move = controls.vec2("move")
    move = camera.right * raw_move.x + camera.forward * raw_move.y
    if move.length > 0.0:
        camera.position += move.normalized() * camera.move_speed * dt

    controls.apply_look(camera)

    cube.transform.rotation.y += 60.0 * dt
    renderer.render(scene)
    win.swap_buffers()

Game Examples

python examples\snake3d.py
python examples\minicraft3d.py
python examples\platformer3d.py

Input

from speepy3d import Input

controls = Input(win, mouse_sensitivity=0.06, mouse_locked_by_click=True)
controls.action("jump", "SPACE")
controls.axis("move_x", negative="A", positive="D")
controls.axis("move_z", negative="S", positive="W")

while not win.should_close():
    win.poll_events()
    controls.update()

    if controls.action("jump"):
        jump()

    move = controls.vec2("move")

Game Layer

from speepy3d import GameApp, MeshBatch, PhysicsBody, PlatformerController, Vec3

app = GameApp(1280, 720, "short game")
app.set_camera((0, 3, 10), pitch=-15)

app.input.action("jump", "SPACE")
app.input.axis("move_x", negative="A", positive="D")

material = app.assets.material("grass", color=Vec3(0.25, 0.85, 0.35))
cube = app.assets.cube()
player = app.scene.spawn(cube, position=(0, 2, 0), material=material, name="player", tag="actor")

world = MeshBatch()
world.cube((0, 0, 0), size=(12, 1, 4), color=(0.3, 0.8, 0.35))
app.scene.add(world.build_model(material))

body = PhysicsBody.from_values(player.position, (1, 1, 1))
app.physics.static_box((0, 0, 0), (12, 1, 4))
controller = PlatformerController(body, app.input, app.physics)

@app.update
def update(dt):
    controller.update(dt)
    player.position = body.position
    app.debug.box(body.box)

@app.render
def render():
    app.draw_scene()
    app.ui.begin()
    app.ui.panel(20, 20, 220, 90)
    app.ui.label(f"FPS {app.fps:.0f}")
    app.profiler.draw(app.ui, 260, 20)
    app.ui.end()

app.run()

Useful systems:

app.timer.after(2.0, spawn_enemy)
app.timer.every(1.0, add_score)

app.particles.emit((0, 2, 0), count=24)

player = app.scene.find("player")
enemies = app.scene.find_by_tag("enemy")

Entity Components

from speepy3d import Component, GameApp, MeshRenderer, Prefab, Rigidbody, Vec3

app = GameApp(1280, 720, "components")
app.input.context("game").action("jump", "SPACE").axis("move_x", negative="A", positive="D")
app.input.use_context("game")

class Player(Component):
    def start(self):
        self.body = self.entity.get(Rigidbody)

    def update(self, dt):
        self.body.velocity.x = self.input.axis("move_x") * 7.0
        if self.input.action("jump") and self.body.body.on_ground:
            self.body.velocity.y = 10.0

player = app.scene.entity("player", tag="player", position=(0, 2, 0))
player.add(Player)
player.add(MeshRenderer(app.assets.cube(), app.assets.material("player", color=Vec3(0.2, 0.7, 1.0))))
player.add(Rigidbody(size=(1, 1, 1)))

prefab = Prefab.from_entity(player)
clone = prefab.instantiate(app.scene, name="clone", position=(3, 2, 0))

Collision and triggers:

@app.on_trigger_enter("player", "coin")
def collect(player, coin):
    coin.destroy()
    app.emit("score:add", 1)

@player.on_collision("enemy")
def hurt(enemy):
    app.tween(player, "position.y", to=4.0, time=0.25, ease="smooth")

Scene save/load:

app.scene.save("level.json")
app.scene.load("level.json")

Queries:

enemies = app.scene.query(tag="enemy", component=Rigidbody)
body = app.scene.find_component(Rigidbody)
app.scene.destroy("enemy_1")
app.scene.clear()

Collision layers:

player.add(Rigidbody(size=(1, 1, 1), layer="player"))
coin.add(Collider(size=(1, 1, 1), trigger=True, layer="pickup"))
app.scene.ignore_collision("enemy", "enemy")

Coroutines:

def spawn_loop():
    while True:
        spawn_enemy()
        yield app.wait(2.0)

app.start_coroutine(spawn_loop)

Loading Assets

from speepy3d import Material, Mesh, Model, Shader, Texture, Vec3

mesh = Mesh.from_gltf("assets/crate.gltf")
material = Material(
    shader=Shader.basic(),
    texture=Texture.from_file("assets/crate.png"),
    color=Vec3(1.0, 1.0, 1.0),
    roughness=0.5,
)
model = Model(mesh, material=material)

Mesh.from_gltf() supports .gltf JSON files with triangle meshes and external or embedded buffers. .glb, skinning and animation are not implemented yet.

Folder workflow:

app.assets.load_folder("assets")
crate = app.assets.get_material("crate")
player_mesh = app.assets.get_mesh("player")

Collision

from speepy3d import AABB, Vec3, intersects

player = AABB.from_center_size(Vec3(0, 1, 0), Vec3(1, 2, 1))
wall = AABB.from_center_size(Vec3(2, 1, 0), Vec3(1, 2, 4))

if intersects(player, wall):
    print("hit")

UI

from speepy3d import Button, Label, Menu, MenuItem, UIManager

ui = UIManager()
ui.add(Label(20, 20, "SCORE 0"))
ui.add(Button(20, 55, 140, 36, text="START", on_click=start_game))
ui.add(Menu(20, 100, 120, 32, title="GAME", items=[
    MenuItem("RESET", reset_game),
    MenuItem("QUIT", win.close),
]))

while not win.should_close():
    win.poll_events()
    ui.update(win)
    render_3d()
    ui.draw(win)
    win.swap_buffers()

Immediate-mode UI:

ui = UIManager(win)

while not win.should_close():
    win.poll_events()
    render_3d()

    ui.begin()
    ui.panel(20, 20, 250, 180)
    ui.label("Score: 10")
    if ui.button("Restart"):
        restart()
    ui.end()

    win.swap_buffers()

Sound

from speepy3d import Sound

Sound("jump.wav").play()

Sound currently uses Python's Windows winsound, so WAV is the practical target.

Build

python setup.py sdist bdist_wheel

Project details


Download files

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

Source Distribution

speepy3d-0.8.4.tar.gz (71.7 kB view details)

Uploaded Source

Built Distribution

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

speepy3d-0.8.4-cp312-cp312-win_amd64.whl (75.2 kB view details)

Uploaded CPython 3.12Windows x86-64

File details

Details for the file speepy3d-0.8.4.tar.gz.

File metadata

  • Download URL: speepy3d-0.8.4.tar.gz
  • Upload date:
  • Size: 71.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for speepy3d-0.8.4.tar.gz
Algorithm Hash digest
SHA256 65c2b26a1c185af189f937be3e3e9f12fa4c7c2fa6fc29aed98c4e125d1f99d2
MD5 b8164a20d39b43c40529d3bce5232a77
BLAKE2b-256 4c9039e27dea204dcd11e01e235725815523450713d073b454a31835dddd02ce

See more details on using hashes here.

File details

Details for the file speepy3d-0.8.4-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: speepy3d-0.8.4-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 75.2 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for speepy3d-0.8.4-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 2c49e9626b0db64572368584609c0014b45c64b2d532efd57bf3d4fc06bb0eaa
MD5 cd1904261da4da262660c3d903a5481a
BLAKE2b-256 1b6943c60ce19287c3898b92a1d077a269a9cb279c52b31678ab25df3f07e26d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page