A single-file importable 3D game engine for Python built on Pygame and OpenGL.
Project description
PyGine3D
A single-file importable 3D game engine for Python built on Pygame and OpenGL 3.3 Core.
Created by Somting — MIT License
What is it?
PyGine3D is a lightweight 3D game engine that lives in a single .py file. Drop it into your project, import it, and you have a full Unity-style game engine with GameObjects, Components, Scenes, cameras, lighting, collision, a 2D HUD overlay, and a Minecraft-style inventory — no framework setup, no project structure required.
Installation
Option 1 — pip
pip install pygine3d
Option 2 — manual
Download PyGine3D.py and drop it next to your game file. Then at the top of your script force Python to load the local file:
import sys
sys.path.insert(0, r"path/to/your/project")
import PyGine3D
Dependencies
pip install pygame PyOpenGL PyOpenGL_accelerate numpy pillow
Quick Start
import sys
sys.path.insert(0, r"C:/your/project/folder")
import PyGine3D
engine = PyGine3D.Engine(800, 600, "My Game")
scene = engine.scene
# Add a cube
cube = scene.spawn("Cube")
cube.add_component(PyGine3D.MeshRenderer(PyGine3D.Mesh.cube()))
cube.transform.position = PyGine3D.Vec3(0, 0, -5)
# Lighting
scene.ambient_light = PyGine3D.Color(0.2, 0.2, 0.2)
scene.directional_light = PyGine3D.DirectionalLight(
direction=PyGine3D.Vec3(-1, -2, -1),
color=PyGine3D.Color(1.0, 0.95, 0.85)
)
# Camera
engine.camera = PyGine3D.FlyCamera(speed=5.0)
engine.run()
Core Concepts
PyGine3D uses a Unity-style Entity-Component architecture.
- Engine — creates the window, runs the game loop
- Scene — holds all GameObjects
- GameObject — an entity in the world, has a Transform and Components
- Component — behaviour attached to a GameObject, subclass to add logic
- Transform — position, rotation (Euler degrees), scale as Vec3
API Reference
Engine
engine = PyGine3D.Engine(width, height, title, fps=60, clear_color=None)
| Property / Method | Description |
|---|---|
engine.scene |
The active Scene |
engine.camera |
The active Camera |
engine.input |
The Input object |
engine.hud |
The 2D HUD overlay |
engine.time |
Total elapsed seconds |
engine.frame |
Current frame number |
engine.on_start |
Callback — called once before the loop |
engine.on_update |
Callback — called every frame with dt |
engine.on_gui |
Callback — called every frame for HUD drawing |
engine.on_quit |
Callback — called when the engine stops |
engine.set_icon(path_or_surface) |
Set a custom window icon |
engine.quit() |
Stop the engine from anywhere |
engine.run() |
Start the game loop |
Scene
scene = engine.scene
| Method | Description |
|---|---|
scene.spawn(name) |
Create and return a new GameObject |
scene.destroy(go) |
Remove a GameObject from the scene |
scene.find(name) |
Find a GameObject by name |
scene.find_by_tag(tag) |
Find all GameObjects with a tag |
scene.check_collision(collider) |
Returns list of overlapping BoxColliders |
scene.ambient_light |
Color — ambient light applied to all objects |
scene.directional_light |
DirectionalLight — main directional light |
GameObject
go = scene.spawn("MyObject")
| Property / Method | Description |
|---|---|
go.name |
Name string |
go.active |
Bool — if False, object is skipped |
go.transform |
The Transform |
go.tags |
List of tag strings |
go.add_component(comp) |
Attach a component, returns it |
go.get_component(cls) |
Get first component of type |
Transform
go.transform.position = PyGine3D.Vec3(0, 1, -5)
go.transform.rotation = PyGine3D.Vec3(0, 45, 0) # Euler degrees
go.transform.scale = PyGine3D.Vec3(1, 1, 1)
Component
Subclass Component to add custom behaviour to any GameObject.
class Spinner(PyGine3D.Component):
def on_start(self):
pass # called once when the scene starts
def on_update(self, dt):
self.owner.transform.rotation.y += 60 * dt # spin 60 deg/sec
def on_destroy(self):
pass # called when the GameObject is destroyed
cube.add_component(Spinner())
Vec3
v = PyGine3D.Vec3(x, y, z)
| Method / Property | Description |
|---|---|
v + v2, v - v2, v * scalar |
Arithmetic |
v.dot(v2) |
Dot product |
v.cross(v2) |
Cross product |
v.length() |
Magnitude |
v.normalized() |
Unit vector |
Vec3.zero() |
(0, 0, 0) |
Vec3.one() |
(1, 1, 1) |
Vec3.up() |
(0, 1, 0) |
Vec3.forward() |
(0, 0, -1) |
Vec3.right() |
(1, 0, 0) |
Color
c = PyGine3D.Color(r, g, b, a=1.0) # values 0.0 to 1.0
Static helpers: Color.white(), Color.black(), Color.red(), Color.green(), Color.blue()
Mesh
PyGine3D.Mesh.cube()
PyGine3D.Mesh.sphere(radius=0.5, rings=16, sectors=16)
PyGine3D.Mesh.plane(size=10.0, divisions=4)
PyGine3D.Mesh.from_obj("path/to/model.obj")
Texture
tex = PyGine3D.Texture("path/to/image.png")
tex = PyGine3D.Texture.solid_color(r, g, b, a) # 1x1 colour texture
Material
mat = PyGine3D.Material(texture=tex, color=PyGine3D.Color(1, 0.5, 0.2))
Both texture and color are optional. Color acts as a tint multiplied over the texture.
MeshRenderer
go.add_component(PyGine3D.MeshRenderer(mesh, material))
Renders a Mesh with a Material. Uses a built-in Phong shader automatically.
Lighting
scene.ambient_light = PyGine3D.Color(0.2, 0.2, 0.25)
scene.directional_light = PyGine3D.DirectionalLight(
direction = PyGine3D.Vec3(-1, -2, -1),
color = PyGine3D.Color(1.0, 0.95, 0.85),
intensity = 1.0
)
BoxCollider
go.add_component(PyGine3D.BoxCollider(PyGine3D.Vec3(0.5, 0.5, 0.5)))
# Check for overlaps
col = go.get_component(PyGine3D.BoxCollider)
hits = scene.check_collision(col) # returns list of BoxColliders
for h in hits:
print(h.owner.name)
auto_collision=True is the default on quick_cube, quick_plane, and quick_box — they add a BoxCollider automatically.
Cameras
FlyCamera
First-person free-fly camera. Right-click + drag to look, WASD to move.
engine.camera = PyGine3D.FlyCamera(speed=5.0, sensitivity=0.15, fov=75)
Controls: WASD / arrow keys — move, Q/E — down/up, right-click drag — look
OrbitCamera
Orbits around a target point.
engine.camera = PyGine3D.OrbitCamera(target=PyGine3D.Vec3(0,0,0), distance=8.0)
Controls: right-click drag — orbit, scroll wheel — zoom
Input
engine.input.get_key(pygame.K_w) # held down
engine.input.get_key_down(pygame.K_space) # just pressed this frame
engine.input.get_mouse_button(1) # 1=left, 2=middle, 3=right
engine.input.mouse_pos # (x, y) tuple
engine.input.mouse_delta # (dx, dy) tuple
Key constants are also available directly: PyGine3D.K_W, PyGine3D.K_SPACE, PyGine3D.K_ESC, etc.
Convenience Spawners
# Uniform scale cube with optional texture/color and auto BoxCollider
quick_cube(scene, position, color, texture, scale, name, auto_collision=True)
# Flat plane
quick_plane(scene, position, size, color, texture, name, auto_collision=True)
# Non-uniform box (width/height/depth independently)
quick_box(scene, position, size=Vec3(1,1,1), color, texture, name, auto_collision=True)
Set auto_collision=False for purely decorative objects that the player should walk through.
HUD (2D Overlay)
Draw 2D elements on top of the 3D scene. Call inside engine.on_gui.
def on_gui():
hud = engine.hud
hud.rect(x, y, w, h, color=(0,0,0,180), border_radius=6)
hud.text("Hello!", x, y, size=20, color=(255,255,255), shadow=True)
hud.text_centered("Centred", x, y, w, h, size=18)
hud.image("icon.png", x, y, w, h)
hud.line(x1, y1, x2, y2, color=(255,255,255), width=1)
hud.circle(cx, cy, radius, color=(255,255,255,255))
hud.crosshair(size=10, thickness=2, color=(255,255,255,200))
hud.panel(x, y, w, h) # dark semi-transparent panel
engine.on_gui = on_gui
Inventory
Minecraft-style hotbar + grid inventory.
inv = PyGine3D.Inventory(engine, cols=9, rows=4)
# Add items to hotbar
inv.hotbar[0] = PyGine3D.InventoryItem("Sword", color=(180, 180, 200))
inv.hotbar[1] = PyGine3D.InventoryItem("Torch", count=8, color=(255, 180, 40))
# Add items to grid
inv.add_item(PyGine3D.InventoryItem("Stone", count=32, color=(120,120,120)))
# Remove item from grid
inv.remove_item("Stone")
# Get selected hotbar item
item = inv.selected_item()
def on_update(dt):
inv.update() # handles E to open/close, 1-9 keys, scroll wheel
def on_gui():
inv.draw() # renders hotbar + grid if open
engine.on_update = on_update
engine.on_gui = on_gui
E — open/close inventory grid
1-9 — select hotbar slot
Scroll wheel — cycle hotbar selection
InventoryItem
item = PyGine3D.InventoryItem(
name = "Pickaxe",
count = 1,
icon = "pickaxe.png", # optional — file path or pygame.Surface
color = (140, 120, 100) # fallback color if no icon
)
Full Example
import sys
sys.path.insert(0, r"C:/your/project")
import PyGine3D
import pygame
engine = PyGine3D.Engine(1280, 720, "My Game", fps=60)
scene = engine.scene
# Lighting
scene.ambient_light = PyGine3D.Color(0.2, 0.2, 0.25)
scene.directional_light = PyGine3D.DirectionalLight(
direction=PyGine3D.Vec3(-1, -2, -1),
color=PyGine3D.Color(1.0, 0.95, 0.85),
intensity=1.0
)
# Ground
floor = PyGine3D.quick_plane(scene, position=PyGine3D.Vec3(0,-0.5,0),
size=20, color=PyGine3D.Color(0.3,0.5,0.3))
# Cube with custom component
cube = PyGine3D.quick_cube(scene, position=PyGine3D.Vec3(0,0,-5),
color=PyGine3D.Color(0.8,0.3,0.2))
class Spinner(PyGine3D.Component):
def on_update(self, dt):
self.owner.transform.rotation.y += 45 * dt
cube.add_component(Spinner())
# Camera
engine.camera = PyGine3D.FlyCamera(speed=6.0)
# HUD
def on_gui():
engine.hud.crosshair()
engine.hud.text(f"Time: {engine.time:.1f}s", 10, 10, size=18)
engine.on_gui = on_gui
engine.run()
Version History
| Version | Notes |
|---|---|
| 1.1.2 | Fixed OpenGL wildcard import clobbering class names |
| 1.1.1 | Added 2D HUD overlay and Inventory system |
| 1.1.0 | Added auto_collision, quick_box, startup banner, custom icon support |
| 1.0.0 | Initial release |
License
MIT License — Copyright (c) 2026 Somting
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pygine3d-1.1.2.tar.gz.
File metadata
- Download URL: pygine3d-1.1.2.tar.gz
- Upload date:
- Size: 20.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
801df5edc7a90271e48536056e5bcab11b5acdb9669482ae5df3c7ca760efd08
|
|
| MD5 |
fd01e4477e9eb97e3dde6582fa28a098
|
|
| BLAKE2b-256 |
0e68f01504ae4cc221bd51f76c3d131e76e6ea3d92d7053f5c883ba76db50117
|
File details
Details for the file pygine3d-1.1.2-py3-none-any.whl.
File metadata
- Download URL: pygine3d-1.1.2-py3-none-any.whl
- Upload date:
- Size: 21.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6db68506bc35bb26d0fa629cb77429d7bde1af81f0331a1987282d3c367cad28
|
|
| MD5 |
eda2df43ecf68884ff182f281c29de95
|
|
| BLAKE2b-256 |
1d09a0ca1b71f8cedd1468d9676d9070fa3bd2b81100f3ff607b5f110900e9a9
|