A lightweight 2D runtime built on top of pygame-ce.
Project description
Guigine
Guigine is a lightweight 2D runtime built on top of pygame-ce.
It is designed for small and medium 2D games, gameplay prototypes, menu-heavy applications, and projects that benefit from scenes, ECS, resource loading, and simple rendering without carrying a large engine framework.
Install
If you want the library and CLI together:
pip install guigine
If you mainly want the CLI in an isolated environment:
pipx install guigine
Both installation modes expose the guigine command on Windows, Linux, and macOS.
CLI Quick Start
Create a new starter project:
guigine new my_game
Backward-compatible shorthand:
guigine my_game
Remove a generated project:
guigine remove my_game
The generated project includes:
- a local
guigine/engine copy - an
assets/folder - a Python-focused
.gitignore - a
src/<project_name>/main.pyentry point - a starter
README.md Poetryandtaskipycommands for running and testing
Contributor Setup
Clone the repository and install dependencies with Poetry:
poetry install
Run the full test suite:
poetry run task test
Run only the CLI-focused tests:
poetry run task test-cli
Run the example showcase from the repository:
poetry run task run-examples
What Guigine Includes
EngineAppruntime bootstrap- scene management with fade transitions
- shared
SceneContext - lightweight ECS primitives
- 2D camera support
- physics and collision helpers
- resource loading for images, fonts, and sounds
- audio and music managers
- reusable widgets
- optional TMX map loading
- TMX entity spawning helpers
- automated tests for core runtime systems
Package Layout
Repository structure:
Guigine/
|- guigine/
|- examples/
|- tests/
|- pyproject.toml
\- README.md
guigine/contains the reusable runtime package published to PyPI.examples/contains repository-only examples and assets.tests/contains the automated suite used during development.
The bash/ folder is kept only for local compatibility wrappers and is excluded from published package artifacts.
Minimal Example
from pathlib import Path
from guigine.app.app import EngineApp
from guigine.app.config import EngineConfig
from guigine.scene.base_scene import BaseScene
class DemoScene(BaseScene):
def __init__(self, context):
super().__init__(context, world_width=1280, world_height=720)
def process_input(self, events):
_ = events
def update(self, dt):
_ = dt
def render(self):
self.screen.fill((20, 24, 30))
def main() -> None:
app = EngineApp(
EngineConfig(
title="My Game",
width=1280,
height=720,
asset_root=Path("assets"),
)
)
app.register_scene("demo", lambda context: DemoScene(context))
app.start("demo")
app.run()
if __name__ == "__main__":
main()
ECS Basics
Guigine uses a small ECS architecture:
- An
Entityis an ID plus a bag of components. - A
Componentstores data. - A
Systemreads entities that have a specific set of components and updates them every frame.
This model keeps behavior composable. Instead of creating a deep inheritance tree like PlayerWithPhysicsAndHealthAndSprite, you build an entity from small parts and let systems operate on those parts.
Entity
An entity is just a container for components.
from guigine.ecs.entity_manager import EntityManager
from guigine.components.base import Position, Velocity
from guigine.components.collider import Collider
entity_manager = EntityManager()
player = entity_manager.create(
Position(100, 100),
Velocity(180, 0),
Collider(16, 16),
)
In this example, the entity itself does not know how to move or collide. It only owns the data required by other systems.
Component
Components are focused data objects. A good component should answer "what data does this entity have?" rather than "what complex behavior does this entity execute?"
Basic Components
Position
Position stores world coordinates.
from guigine.components.base import Position
position = Position(120, 64)
print(position.xy) # (120.0, 64.0)
Use it for anything that exists in world space.
Velocity
Velocity stores movement speed and direction.
from guigine.components.base import Velocity
velocity = Velocity(150, -40)
print(velocity.vx, velocity.vy)
When an entity has both Position and Velocity, systems such as PhysicsSystem can move it every frame.
Collider
Collider defines a collision rectangle and optional offsets relative to the entity position.
from guigine.components.collider import Collider
collider = Collider(width=16, height=16, offset_x=0, offset_y=0)
This is the basic piece used by physics and collision logic.
Freeze
Freeze is a small control component that allows systems to temporarily stop movement or logic.
from guigine.components.base import Freeze
freeze = Freeze(active=True)
This is useful for pause states, cutscenes, hit-stop, and scripted interactions.
Sprite
Sprite is the visual component used by rendering systems. It stores the image/surface and drawing rectangle for an entity.
Typical pattern:
from guigine.components.base import Position
from guigine.components.sprite import Sprite
enemy = self.entity_manager.create(
Position(200, 120),
Sprite(self.resources.load_image("actors/enemy.png")),
)
Health
Health is a simple gameplay-friendly component that stores hit points.
from guigine.components.base import Health
health = Health(max_hp=100)
health.take_damage(20)
print(health.current_hp) # 80.0
This is a good example of a component that still stays small and data-focused.
Basic Systems
Systems are where frame-by-frame behavior happens.
PhysicsSystem
PhysicsSystem looks for entities with Position and Velocity and moves them using dt. If an entity also has Collider, the system resolves collisions against static and dynamic colliders.
from guigine.systems.physics import PhysicsSystem
class GameplayScene(BaseScene):
def __init__(self, context):
super().__init__(context, world_width=2000, world_height=1200)
self.physics = PhysicsSystem()
self.add_system(self.physics)
def update(self, dt):
self.physics.cache_static_colliders(self.entity_manager)
self.update_systems(dt)
This is the most common system to start with when building actors that move.
RenderSystem
RenderSystem is responsible for drawing sprite-based entities visible to the camera.
It mainly works with entities that have:
PositionSprite- optionally
Collider,RenderLayer,DepthAnchor, orAlwaysOnTop
Example:
from guigine.systems.render import RenderSystem
self.render_system = RenderSystem(
screen=self.screen,
camera=self.camera,
entity_manager=self.entity_manager,
)
The render system sorts visible entities and draws them in a stable order, which helps a lot in top-down and layered scenes.
AnimationSystem
AnimationSystem updates animation state over time for entities that expose animation-related components.
Use it when your scene has animated actors, props, or effects and you want that timing logic separated from scene orchestration.
AreaTriggerSystem
AreaTriggerSystem is useful when regions in the world should react to entities entering them.
Common uses:
- dialogue triggers
- zone transitions
- interaction hotspots
- scripted events
PathFollowingSystem
PathFollowingSystem is useful for moving entities along a computed route.
This becomes especially helpful when combined with navigation/pathfinding helpers from guigine.utils.navigation.
ECS Example In A Scene
This example shows a small ECS-driven scene with a moving player.
from guigine.components.base import Position, Velocity
from guigine.components.collider import Collider
from guigine.scene.base_scene import BaseScene
from guigine.systems.physics import PhysicsSystem
class GameplayScene(BaseScene):
def __init__(self, context):
super().__init__(context, world_width=2000, world_height=1200)
self.physics = PhysicsSystem()
self.add_system(self.physics)
self.player = self.entity_manager.create(
Position(100, 100),
Velocity(120, 0),
Collider(16, 16),
)
def update(self, dt):
self.physics.cache_static_colliders(self.entity_manager)
self.update_systems(dt)
The entity contains state. The scene owns the orchestration. The system owns the frame update logic.
Resource Loading Example
button_image = self.resources.load_image("ui/button.png")
font = self.resources.load_font("fonts/MyFont.ttf", 16)
click_sound = self.resources.load_sound("audio/click.wav")
Widget Example
from guigine.widgets.button import Button
button = Button(
init_surface=self.resources.load_image("ui/button.png"),
surface_pressed=self.resources.load_image("ui/button_pressed.png"),
pos_center=(640, 360),
action=lambda: self.scene_manager.start_fade("gameplay"),
text="Start",
font_size=16,
font="fonts/MyFont.ttf",
resource_manager=self.resources,
)
self.context.ui.clear()
self.context.ui.add(button)
TMX Example
from guigine.map.tile_map_loader import TileMapLoader
loader = TileMapLoader(self.resources, walk_layer_name="ground2")
tilemap = loader.load("maps/level_01.tmx")
Practical Guidance
When building with Guigine, a good default approach is:
- Keep scenes responsible for flow and orchestration.
- Keep components small and data-oriented.
- Keep systems narrow and reusable.
- Move project-specific game rules outside the engine package whenever possible.
That keeps the runtime easy to reason about and easier to reuse across multiple projects.
Notes For Publishing
- The PyPI package exposes the
guiginecommand through a Python entry point. - The local
bash/wrappers are not part of the published package. - The repository examples are documentation/reference material and are not installed as part of the runtime package.
Current Status
Guigine is currently best treated as an early reusable runtime for experimentation, tooling, and game prototypes. The public API is intentionally small and should evolve carefully as more real projects adopt it.
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 guigine-0.1.1.tar.gz.
File metadata
- Download URL: guigine-0.1.1.tar.gz
- Upload date:
- Size: 27.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.4.1 CPython/3.12.0rc2 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f8d13d3df29fca2fb9e5d25e238abdf4da7feb5a766c9c21fada20d3f5eb805c
|
|
| MD5 |
02d8ac79703b3813eb028f316625a9da
|
|
| BLAKE2b-256 |
2dedaff0a79ec224a5ca52a92df8cf9a2365ca4dbd38d08ddb2bcf94927383bd
|
File details
Details for the file guigine-0.1.1-py3-none-any.whl.
File metadata
- Download URL: guigine-0.1.1-py3-none-any.whl
- Upload date:
- Size: 48.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.4.1 CPython/3.12.0rc2 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd7c28fa9df037405fe78140ab288f5fb2e15c580be06cae32e443433e514739
|
|
| MD5 |
4681b0b0990740350c2165097e1b1b86
|
|
| BLAKE2b-256 |
03d77903edc91442cb50db81bd4f2c1e0247eace4c333bcd5bacf99ed46315ea
|