pyxen.draw

The draw module draws shapes straight to the screen, frame by frame. Call it from update(): what you draw belongs to that frame, and the next frame starts empty, so you draw it again each frame for as long as it should show.

from pyxen import *

def update():
    draw.circle(64, 64, 20, fill=(1, 0.2, 0.3, 1))
    draw.line(0, 0, 128, 128, color=(1, 1, 1, 1))

Nothing is spawned: there is no entity to keep a handle on and nothing to destroy. The time-travel debugger keeps what each frame drew, so when you pause and go back through the frames, every frame shows its shapes as it did when the game ran.


draw or a Shape component?

Pyxen gives you the same primitives twice. They look identical on screen and differ only in lifetime.

Shape componentdraw
LifetimeLives until you destroy the entityEach frame draws it again
PositionEntity position, rotation, scaleWorld coordinates you pass in
CostA full entity: transform, layer, component storageA few quads in the frame buffer
DebuggerListed and inspected as an entityShown in each frame you go back to
Good forGame objects — ships, walls, UI that stays putPictures of your game’s state: trails, overlays, HUD bars

Reach for a Shape when the thing is something in your game. Reach for draw when you are painting a picture of state your entities already hold: keep that state in entities and components, where the debugger and the Inspector see it, and draw from it each frame.


Colours

Every colour argument takes an (r, g, b, a) tuple with components from 0.0 to 1.0, or a Color.

draw.rect(10, 10, 40, 20, fill=(1, 0, 0, 1))        # opaque red
draw.rect(10, 40, 40, 20, fill=(1, 0, 0, 0.35))     # 35% red

The filled shapes draw nothing by default. draw.rect, draw.triangle and draw.circle start with both fill and stroke fully transparent, so a call that passes neither is invisible — that is the single most common surprise with this module. draw.line and draw.polyline default to opaque white instead, so they always show up.


Functions

draw.line(x1, y1, x2, y2, color=(1,1,1,1), width=1)

A straight line between two points.

ArgumentTypeDescription
x1, y1floatStart point, in world coordinates.
x2, y2floatEnd point.
colortupleLine colour. Defaults to opaque white.
widthfloatLine thickness in pixels. Defaults to 1.
def update():
    draw.line(0, 64, 128, 64, color=(0.3, 1, 0.5, 1), width=2)

draw.polyline(points, color=(1,1,1,1), width=1, closed=False)

A run of connected line segments.

ArgumentTypeDescription
pointstuple or FloatBufferFlat sequence (x1, y1, x2, y2, …) — not a list of pairs.
colortupleLine colour. Defaults to opaque white.
widthfloatLine thickness in pixels.
closedboolWhen True, joins the last point back to the first.
def update():
    draw.polyline((10, 10, 40, 60, 80, 20, 110, 70),
                  color=(1, 0.8, 0.2, 1), width=2)

Points are flat on purpose: it is the shape a FloatBuffer already has, so a path you compute once can be handed straight to draw.polyline every frame without rebuilding it.


draw.rect(x, y, width, height, fill=None, stroke=None, stroke_width=1)

An axis-aligned rectangle. x, y is its corner, not its centre.

ArgumentTypeDescription
x, yfloatCorner position.
width, heightfloatSize in pixels.
filltupleInterior colour. Omit for no fill.
stroketupleOutline colour. Omit for no outline.
stroke_widthfloatOutline thickness. Defaults to 1.
def update():
    # A health bar: dark backing, bright fill, thin outline.
    draw.rect(4, 4, 60, 6, fill=(0.1, 0.1, 0.1, 1))
    draw.rect(4, 4, 60 * health / max_health, 6, fill=(0.2, 1, 0.4, 1))
    draw.rect(4, 4, 60, 6, stroke=(1, 1, 1, 0.5))

draw.triangle(x1, y1, x2, y2, x3, y3, fill=None, stroke=None, stroke_width=1)

A triangle through three points.

ArgumentTypeDescription
x1, y1, x2, y2, x3, y3floatThe three corners.
filltupleInterior colour. Omit for no fill.
stroketupleOutline colour. Omit for no outline.
stroke_widthfloatOutline thickness.
def update():
    draw.triangle(64, 20, 84, 60, 44, 60, fill=(1, 1, 0.3, 1))

draw.circle(x, y, radius, fill=None, stroke=None, stroke_width=1, segments=12)

A circle around a centre point.

ArgumentTypeDescription
x, yfloatCentre position.
radiusfloatRadius in pixels.
filltupleInterior colour. Omit for no fill.
stroketupleOutline colour. Omit for no outline.
stroke_widthfloatOutline thickness.
segmentsintStraight 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 when you want a visible polygon.

def update():
    draw.circle(64, 64, 24, fill=(0.2, 0.4, 1, 1), segments=32)
    draw.circle(64, 64, 30, stroke=(1, 1, 1, 0.4), segments=32)

Notes

  • draw calls belong in update(). Calling them from start() draws for a single frame and then vanishes.
  • Drawing happens in world coordinates, so the camera moves it. For a HUD that stays put, draw relative to camera or keep the HUD on an unmoved camera.
  • Everything drawn in a frame lands above the sprites drawn that frame.