Shapes
Rect, Triangle, Circle, Line and Polyline are shape components. You give one to an entity when you spawn it, and the entity draws that shape every frame until you destroy it.
from pyxen import *
def start():
world.spawn(x=110, y=90, shape=Rect(48, 32, fill=(0.4, 0.7, 1.0, 1.0)))
world.spawn(x=210, y=90, shape=Circle(24, stroke=(1.0, 0.6, 0.3, 1.0), stroke_width=2))
A shape is a real entity, so everything entities can do applies: move it with entity.x and
entity.y, spin it with entity.rotation, parent it, tween it, put it on a layer, hide it
with entity.visible.
Shapes or draw?
Pyxen gives you the same five primitives twice. They look identical on screen and differ only in lifetime. See draw for the other half.
| Shape component | draw | |
|---|---|---|
| Lifetime | Lives until you destroy the entity | One frame |
| Position | Entity position, rotation, scale | World coordinates you pass in |
| Cost | A full entity: transform, layer, component storage | A few quads in the frame buffer |
| Good for | Game objects — ships, walls, UI that stays put | Procedural geometry, debug overlays, HUD bars, trails |
Reach for a shape when the thing is something in your game. Reach for draw when you are
painting a picture of something you already track yourself.
Three things that catch people out
A shape is set at spawn and cannot be changed afterwards. There is no entity.shape —
reading it raises ValueError: component schema does not exist. This is unlike sprite and
text, which are assignable. To change a shape, destroy the entity and spawn a new one.
entity.color does not tint a shape. It works for sprites; a shape draws in its own fill
and stroke and ignores it. Set the colour on the shape itself.
The stroke width argument has two names. Rect, Triangle and Circle take
stroke_width; Line and Polyline take width. They mean the same thing.
Colours
Every colour argument takes an (r, g, b, a) tuple with components from 0.0 to 1.0, or a
Color.
Rect(40, 20, fill=(1, 0, 0, 1)) # opaque red
Rect(40, 20, fill=(1, 0, 0, 0.35)) # 35% red
A shape with neither fill nor stroke draws nothing. Both start fully transparent, so
Rect(40, 40) is a valid, invisible entity — the most common surprise here. You can pass both
to get a filled shape with an outline.
Types
Rect(width, height, fill=None, stroke=None, stroke_width=1, pivot=None)
A rectangle.
| Argument | Type | Description |
|---|---|---|
width, height | float | Size in pixels. |
fill | tuple | Interior colour. Omit for no fill. |
stroke | tuple | Outline colour. Omit for no outline. |
stroke_width | float | Outline thickness. Defaults to 1. |
pivot | tuple | The point the entity’s position refers to, in shape pixels. Defaults to the corner; (width/2, height/2) centres it. |
Rect is the only shape with a pivot — it is also what rotation turns around.
def start():
world.spawn(x=110, y=90, shape=Rect(48, 32, fill=(0.4, 0.7, 1.0, 1.0)))
# Centred on its own position, so rotation spins in place.
world.spawn(name="spinner", x=210, y=90,
shape=Rect(48, 32, stroke=(1.0, 0.6, 0.3, 1.0), stroke_width=2,
pivot=(24, 16)))
def update():
world.get(name="spinner").rotation += 0.02
Triangle(x1, y1, x2, y2, x3, y3, fill=None, stroke=None, stroke_width=1)
A triangle through three points, given relative to the entity’s position.
| Argument | Type | Description |
|---|---|---|
x1, y1, x2, y2, x3, y3 | float | The three corners, as offsets from the entity. |
fill | tuple | Interior colour. Omit for no fill. |
stroke | tuple | Outline colour. Omit for no outline. |
stroke_width | float | Outline thickness. |
world.spawn(x=160, y=90, shape=Triangle(0, -20, 18, 14, -18, 14, fill=(1, 1, 0.3, 1)))
Circle(radius, fill=None, stroke=None, stroke_width=1, segments=12)
A circle centred on the entity.
| Argument | Type | Description |
|---|---|---|
radius | float | Radius in pixels. |
fill | tuple | Interior colour. Omit for no fill. |
stroke | tuple | Outline colour. Omit for no outline. |
stroke_width | float | Outline thickness. |
segments | int | Straight segments approximating the curve. Defaults to 12. |
A circle is really a polygon. Twelve segments reads as round at small sizes; raise it for large
circles, and lower it on purpose when you want a visible polygon — segments=6 is a hexagon.
world.spawn(x=110, y=90, shape=Circle(28, fill=(0.5, 1.0, 0.7, 1.0), segments=6))
world.spawn(x=210, y=90, shape=Circle(28, fill=(1.0, 0.5, 0.7, 1.0), segments=32))
Line(x1, y1, x2, y2, stroke=None, width=1)
A straight line between two points, relative to the entity.
| Argument | Type | Description |
|---|---|---|
x1, y1 | float | Start, as an offset from the entity. |
x2, y2 | float | End. |
stroke | tuple | Line colour. |
width | float | Line thickness. Note: width, not stroke_width. |
world.spawn(x=60, y=90, shape=Line(0, 0, 80, -40, stroke=(0.3, 1, 0.5, 1), width=2))
Polyline(points, stroke=None, width=1, closed=False, fill=None)
A run of connected segments.
| Argument | Type | Description |
|---|---|---|
points | tuple or FloatBuffer | Flat sequence (x1, y1, x2, y2, …) — not a list of pairs. |
stroke | tuple | Line colour. |
width | float | Line thickness. Note: width, not stroke_width. |
closed | bool | When True, joins the last point back to the first. |
fill | tuple | Fills the enclosed area. Most useful with closed=True. |
Points are flat on purpose: it is the shape a FloatBuffer already has, so a path computed
once can be handed straight to Polyline without rebuilding it.
world.spawn(x=250, y=90,
shape=Polyline((0, 0, 30, -25, 60, 0), stroke=(1, 0.9, 0.2, 1), width=3))