pyxen.draw

Das Modul draw zeichnet Formen Frame für Frame direkt auf den Bildschirm. Rufe es aus update() auf: Was du zeichnest, gehört zu diesem Frame, und der nächste beginnt leer — du zeichnest es also in jedem Frame erneut, solange es zu sehen sein soll.

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))

Es entsteht nichts: keine Entität, die du behalten, und nichts, das du zerstören müsstest. Der Zeitreise-Debugger behält, was jeder Frame gezeichnet hat: Wenn du pausierst und durch die Frames zurückgehst, zeigt jeder Frame seine Formen so wie im laufenden Spiel.


draw oder eine Shape-Komponente?

Pyxen bietet dieselben Primitive zweimal an. Auf dem Bildschirm sind sie identisch und unterscheiden sich nur in ihrer Lebensdauer.

Shape-Komponentedraw
LebensdauerLebt, bis du die Entität zerstörstJeder Frame zeichnet es neu
PositionPosition, Rotation und Skalierung der EntitätWeltkoordinaten, die du übergibst
KostenEine vollständige Entität: Transform, Ebene, KomponentenspeicherEin paar Quads im Frame-Puffer
DebuggerAls Entität aufgelistet und inspiziertIn jedem Frame zu sehen, zu dem du zurückgehst
Gut fürSpielobjekte — Schiffe, Wände, feste OberflächeBilder vom Zustand deines Spiels: Spuren, Overlays, Balken

Nimm Shape, wenn das Ding etwas in deinem Spiel ist. Nimm draw, wenn du ein Bild von einem Zustand malst, den deine Entitäten schon halten: Behalte diesen Zustand in Entitäten und Komponenten, wo der Debugger und der Inspektor ihn sehen, und zeichne ihn in jedem Frame.


Farben

Jedes Farbargument nimmt ein (r, g, b, a)-Tupel mit Komponenten von 0.0 bis 1.0 oder ein Color.

draw.rect(10, 10, 40, 20, fill=(1, 0, 0, 1))        # deckendes Rot
draw.rect(10, 40, 40, 20, fill=(1, 0, 0, 0.35))     # Rot mit 35 %

Die gefüllten Formen zeichnen standardmäßig nichts. draw.rect, draw.triangle und draw.circle starten mit vollständig transparentem fill und stroke, ein Aufruf ohne beides ist also unsichtbar — das ist die häufigste Überraschung bei diesem Modul. draw.line und draw.polyline verwenden dagegen deckendes Weiß und sind immer zu sehen.


Funktionen

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

Eine gerade Linie zwischen zwei Punkten.

ArgumentTypBeschreibung
x1, y1floatStartpunkt, in Weltkoordinaten.
x2, y2floatEndpunkt.
colorTupelLinienfarbe. Standard ist deckendes Weiß.
widthfloatLinienstärke in Pixeln. Standard ist 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)

Eine Folge verbundener Liniensegmente.

ArgumentTypBeschreibung
pointsTupel oder FloatBufferFlache Folge (x1, y1, x2, y2, …) — keine Liste von Paaren.
colorTupelLinienfarbe. Standard ist deckendes Weiß.
widthfloatLinienstärke in Pixeln.
closedboolBei True wird der letzte Punkt mit dem ersten verbunden.
def update():
    draw.polyline((10, 10, 40, 60, 80, 20, 110, 70),
                  color=(1, 0.8, 0.2, 1), width=2)

Die Punkte liegen bewusst flach: genau diese Form hat ein FloatBuffer bereits, sodass ein einmal berechneter Pfad in jedem Frame unverändert an draw.polyline übergeben werden kann, ohne ihn neu aufzubauen.


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

Ein achsenparalleles Rechteck. x, y ist seine Ecke, nicht sein Mittelpunkt.

ArgumentTypBeschreibung
x, yfloatPosition der Ecke.
width, heightfloatGröße in Pixeln.
fillTupelInnenfarbe. Weglassen für keine Füllung.
strokeTupelUmrissfarbe. Weglassen für keinen Umriss.
stroke_widthfloatStärke des Umrisses. Standard ist 1.
def update():
    # Eine Lebensleiste: dunkler Hintergrund, kräftige Füllung, dünner Umriss.
    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)

Ein Dreieck durch drei Punkte.

ArgumentTypBeschreibung
x1, y1, x2, y2, x3, y3floatDie drei Ecken.
fillTupelInnenfarbe. Weglassen für keine Füllung.
strokeTupelUmrissfarbe. Weglassen für keinen Umriss.
stroke_widthfloatStärke des Umrisses.
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)

Ein Kreis um einen Mittelpunkt.

ArgumentTypBeschreibung
x, yfloatPosition des Mittelpunkts.
radiusfloatRadius in Pixeln.
fillTupelInnenfarbe. Weglassen für keine Füllung.
strokeTupelUmrissfarbe. Weglassen für keinen Umriss.
stroke_widthfloatStärke des Umrisses.
segmentsintGerade 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, wenn du ein sichtbares Vieleck willst.

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)

Hinweise

  • draw-Aufrufe gehören in update(). Aus start() zeichnen sie ein einziges Frame und sind dann weg.
  • Gezeichnet wird in Weltkoordinaten, die Kamera bewegt das Gezeichnete also mit. Für eine Oberfläche, die stehen bleibt, zeichne relativ zu camera oder lass sie auf einer unbewegten Kamera.
  • Alles, was in einem Frame gezeichnet wird, liegt über den Sprites dieses Frames.