Formen
Rect, Triangle, Circle, Line und Polyline sind Form-Komponenten. Du gibst einer Entität beim Erzeugen eine davon mit, und die Entität zeichnet diese Form in jedem Frame, bis du sie zerstörst.
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))
Eine Form ist eine echte Entität, also gilt alles, was Entitäten können: bewege sie mit
entity.x und entity.y, drehe sie mit entity.rotation, gib ihr ein Elternobjekt, animiere
sie per Tween, leg sie auf eine Ebene, blende sie mit entity.visible aus.
Formen oder draw?
Pyxen bietet dieselben fünf Primitive zweimal an. Auf dem Bildschirm sind sie identisch und unterscheiden sich nur in ihrer Lebensdauer. Die andere Hälfte steht unter draw.
| Form-Komponente | draw | |
|---|---|---|
| Lebensdauer | Lebt, bis du die Entität zerstörst | Ein Frame |
| Position | Position, Rotation und Skalierung der Entität | Weltkoordinaten, die du übergibst |
| Kosten | Eine vollständige Entität: Transform, Ebene, Komponentenspeicher | Ein paar Quads im Frame-Puffer |
| Gut für | Spielobjekte — Schiffe, Wände, feste Oberfläche | Prozedurale Geometrie, Debug-Anzeigen, Balken, Spuren |
Nimm eine Form, wenn das Ding etwas in deinem Spiel ist. Nimm draw, wenn du ein Bild von
etwas malst, das du ohnehin schon selbst verwaltest.
Drei Stolperfallen
Eine Form wird beim Erzeugen festgelegt und lässt sich danach nicht mehr ändern. Es gibt
kein entity.shape — der Zugriff wirft ValueError: component schema does not exist. Anders
als sprite und text, die sehr wohl zuweisbar sind. Um eine Form zu ändern, zerstöre die
Entität und erzeuge eine neue.
entity.color färbt eine Form nicht ein. Bei Sprites funktioniert es; eine Form zeichnet
mit ihrem eigenen fill und stroke und ignoriert es. Setze die Farbe an der Form selbst.
Die Umrissstärke hat zwei Namen. Rect, Triangle und Circle nehmen stroke_width;
Line und Polyline nehmen width. Gemeint ist dasselbe.
Farben
Jedes Farbargument nimmt ein (r, g, b, a)-Tupel mit Komponenten von 0.0 bis 1.0 oder ein
Color.
Rect(40, 20, fill=(1, 0, 0, 1)) # deckendes Rot
Rect(40, 20, fill=(1, 0, 0, 0.35)) # Rot mit 35 %
Eine Form ohne fill und ohne stroke zeichnet nichts. Beide starten vollständig
transparent, Rect(40, 40) ist also eine gültige, unsichtbare Entität — die häufigste
Überraschung hier. Du kannst beide angeben und bekommst eine gefüllte Form mit Umriss.
Typen
Rect(width, height, fill=None, stroke=None, stroke_width=1, pivot=None)
Ein Rechteck.
| Argument | Typ | Beschreibung |
|---|---|---|
width, height | float | Größe in Pixeln. |
fill | Tupel | Innenfarbe. Weglassen für keine Füllung. |
stroke | Tupel | Umrissfarbe. Weglassen für keinen Umriss. |
stroke_width | float | Stärke des Umrisses. Standard ist 1. |
pivot | Tupel | Der Punkt, auf den sich die Position der Entität bezieht, in Form-Pixeln. Standard ist die Ecke; (width/2, height/2) zentriert sie. |
Rect ist die einzige Form mit pivot — und das ist zugleich der Punkt, um den sich die
Rotation dreht.
def start():
world.spawn(x=110, y=90, shape=Rect(48, 32, fill=(0.4, 0.7, 1.0, 1.0)))
# Auf der eigenen Position zentriert, dreht sich also auf der Stelle.
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)
Ein Dreieck durch drei Punkte, angegeben relativ zur Position der Entität.
| Argument | Typ | Beschreibung |
|---|---|---|
x1, y1, x2, y2, x3, y3 | float | Die drei Ecken, als Versatz zur Entität. |
fill | Tupel | Innenfarbe. Weglassen für keine Füllung. |
stroke | Tupel | Umrissfarbe. Weglassen für keinen Umriss. |
stroke_width | float | Stärke des Umrisses. |
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)
Ein Kreis, zentriert auf der Entität.
| Argument | Typ | Beschreibung |
|---|---|---|
radius | float | Radius in Pixeln. |
fill | Tupel | Innenfarbe. Weglassen für keine Füllung. |
stroke | Tupel | Umrissfarbe. Weglassen für keinen Umriss. |
stroke_width | float | Stärke des Umrisses. |
segments | int | Gerade Segmente, die die Kurve annähern. Standard ist 12. |
Ein Kreis ist in Wahrheit ein Vieleck. Zwölf Segmente wirken bei kleinen Größen rund; erhöhe
den Wert für große Kreise und senke ihn bewusst, wenn du ein sichtbares Vieleck willst —
segments=6 ist ein Sechseck.
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)
Eine gerade Linie zwischen zwei Punkten, relativ zur Entität.
| Argument | Typ | Beschreibung |
|---|---|---|
x1, y1 | float | Anfang, als Versatz zur Entität. |
x2, y2 | float | Ende. |
stroke | Tupel | Linienfarbe. |
width | float | Linienstärke. Achtung: width, nicht 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)
Eine Folge verbundener Segmente.
| Argument | Typ | Beschreibung |
|---|---|---|
points | Tupel oder FloatBuffer | Flache Folge (x1, y1, x2, y2, …) — keine Liste von Paaren. |
stroke | Tupel | Linienfarbe. |
width | float | Linienstärke. Achtung: width, nicht stroke_width. |
closed | bool | Bei True wird der letzte Punkt mit dem ersten verbunden. |
fill | Tupel | Füllt die eingeschlossene Fläche. Vor allem mit closed=True sinnvoll. |
Die Punkte liegen bewusst flach: genau diese Form hat ein FloatBuffer bereits, sodass ein
einmal berechneter Pfad unverändert an Polyline übergeben werden kann.
world.spawn(x=250, y=90,
shape=Polyline((0, 0, 30, -25, 60, 0), stroke=(1, 0.9, 0.2, 1), width=3))