Entities & Components
Pyxen uses an Entity-Component System (ECS). If you’ve used game engines with class hierarchies — where a Player extends Character extends GameObject — this is different. In Pyxen, there’s no inheritance. Everything is composition.
Entities
An entity is a game object. It could be a player, a wall, a bullet, a particle — anything. Under the hood, an entity is just an ID. It holds no data on its own. Instead, you attach components to it.
player = world.spawn(name="player", x=100, y=50)
This creates an entity at position (100, 50) with a name. The entity exists in the world and will be rendered if it has a sprite.
Components
Components are pieces of data attached to an entity. Pyxen has two kinds:
Built-in components
These are provided by the engine:
| Component | Purpose |
|---|---|
| Transform | Position (x, y), rotation, scale |
| Sprite | Visual appearance — image, tile, pivot, color |
| Camera | Viewport with zoom |
| GridBody | Collision body on the grid |
| GridMap | Tilemap with rows, columns, and tile data |
| Sound | Sound effect playback |
| Music | Music stream playback |
| Name | String identifier for lookup |
| Layer | Rendering layer (0–255) |
| Parent / Children | Scene hierarchy |
You set built-in components through entity properties or at spawn time:
e = world.spawn(
x=50, y=80,
sprite=Sprite("hero"),
name="player"
)
# later
e.x = 100
e.sprite = Sprite("hero", tile=(16, 0, 16, 16))
Custom components
You can attach your own components to any entity. A custom component is a dictionary with typed fields:
player.health = {"value": 100, "max": 100}
player.speed = {"value": 2.5}
Once attached, you access fields directly:
player.health.value -= 10
You can also use marker components — components with no data that act as tags:
player.alive = True
enemy.boss = True
This is useful for queries: you can find all entities with a given marker.
Field types
Component fields can be:
| Type | Example | Notes |
|---|---|---|
int | 42 | 32-bit integer |
float | 3.14 | 32-bit float |
bool | True | Boolean |
str | "hello" | Max 31 characters |
| Entity | player | Reference to another entity |
These constraints exist because components are stored in a compact, cache-friendly layout inside the C++ engine. This is what makes queries fast.
Querying entities
The world lets you find entities by their components:
# all entities with a "health" component
for e in world.all("health"):
if e.health.value <= 0:
world.destroy(e)
# combine: has "enemy", does not have "dead"
for e in world.all("enemy", without=("dead",)):
e.x += e.speed.value
You can also query by built-in components like "sprite", "body", "camera", "sound", "music", and "map":
# all entities with a sprite
for e in world.all("sprite"):
e.color = (1, 1, 1, 1)
# entities with both a sprite and the "enemy" custom component
for e in world.all("sprite", "enemy"):
e.color = (1, 0, 0, 1)
Queries are the main way you write game logic in Pyxen. Instead of each object updating itself, you write systems that iterate over entities with specific components.
See the full API: world.all(), Entity
Why ECS?
If you’re used to object-oriented game code, ECS might feel unusual at first. The benefits:
- No class hierarchies — you don’t need to decide upfront if a PowerUp is-a Collectible is-a Entity. Just attach the components you need.
- Easy to combine behaviors — an entity can have
health,enemy,flying, andbosscomponents all at once, without multiple inheritance. - Fast queries — the engine stores components in contiguous memory, so iterating over all entities with a given component is very efficient.
- Easy to debug — every entity is just a bag of data. The frame inspector shows you exactly what’s attached to each one.