🦅 Pyglet 2.1 – Vollständiger Guide
Was ist Pyglet?
Pyglet (aktuell v2.1.x) ist eine leistungsstarke, plattformübergreifende Python-Bibliothek für Spiele und multimediale Anwendungen. Sie ist die Basis unter Python Arcade – Arcade baut intern auf Pyglet auf! Wer Pyglet versteht, versteht auch Arcades Innenleben.
🎮 Für Spiele
Window-Management, Input-Handling, Sprites, Sound – alles für 2D-Spiele inklusive.
🖥️ OpenGL-Basis
Direkter OpenGL-Zugriff. Für Custom-Shader und Effekte unschlagbar flexibel.
📦 Keine Abhängigkeiten
Nur Python Standard Library + OpenGL. Kein SDL, kein externe C-Lib nötig.
🔊 Audio
Media-Player, Streaming, 3D-Sound via OpenAL. Vollständige Audio-Pipeline.
# Pyglet 2.1 installieren
pip install pyglet
# Version prüfen
python -c "import pyglet; print(pyglet.version)"
# Optionale Erweiterungen
pip install pyglet[image] # Erweiterte Bildformate
pip install pyglet[audio] # Erweiterte Audioformate via FFmpeg
Fenster & Events
Pyglet-Fenster werden als pyglet.window.Window-Objekte erstellt. Events werden via Dekorator oder Subklasse registriert.
import pyglet
# Fenster erstellen
window = pyglet.window.Window(
width=800,
height=600,
caption="Mein Pyglet-Fenster",
resizable=True,
fullscreen=False
)
# Hintergrundfarbe (RGBA 0.0–1.0)
pyglet.gl.glClearColor(0.1, 0.1, 0.2, 1.0)
@window.event
def on_draw():
window.clear()
# Hier zeichnen
@window.event
def on_key_press(symbol, modifiers):
if symbol == pyglet.window.key.ESCAPE:
window.close()
pyglet.app.run()
import pyglet
class GameWindow(pyglet.window.Window):
def __init__(self):
super().__init__(800, 600, caption="Game")
self.batch = pyglet.graphics.Batch()
self.fps_display = pyglet.window.FPSDisplay(window=self)
self.player_x = 400
def on_draw(self):
self.clear()
self.batch.draw()
self.fps_display.draw()
def on_update(self, dt):
self.player_x += 100 * dt # frame-rate-unabhängig
def on_key_press(self, symbol, modifiers):
if symbol == pyglet.window.key.F11:
self.set_fullscreen(not self.fullscreen)
window = GameWindow()
pyglet.clock.schedule_interval(window.on_update, 1/60)
pyglet.app.run()
@window.event
def on_draw():
"""Jeden Frame: zeichnen."""
window.clear()
@window.event
def on_key_press(symbol, modifiers):
"""Taste gedrückt."""
print(pyglet.window.key.symbol_string(symbol))
@window.event
def on_key_release(symbol, modifiers):
"""Taste losgelassen."""
@window.event
def on_mouse_press(x, y, button, modifiers):
"""Mausklick bei (x,y)."""
print(f"Klick: {x}, {y}, Button: {button}")
@window.event
def on_mouse_motion(x, y, dx, dy):
"""Mausbewegung. dx/dy = Delta."""
@window.event
def on_resize(width, height):
"""Fenstergröße geändert."""
print(f"Neue Größe: {width}x{height}")
Grafik & Batch-Rendering
Pyglets Batch-System gruppiert Zeichenoperationen für maximale GPU-Effizienz. Groups steuern die Zeichenreihenfolge (Z-Order).
import pyglet
from pyglet.graphics import Batch, Group
window = pyglet.window.Window(800, 600)
batch = Batch()
# Groups für Z-Order (niedrigere Order = weiter hinten)
bg_group = Group(order=0) # Hintergrund
fg_group = Group(order=1) # Vordergrund
ui_group = Group(order=2) # UI ganz oben
# Sprite laden und zur Batch hinzufügen
img = pyglet.resource.image("player.png")
img.anchor_x = img.width // 2
img.anchor_y = img.height // 2
player = pyglet.sprite.Sprite(
img,
x=400, y=300,
batch=batch,
group=fg_group
)
# Mehrere Sprites effizient
coins = []
for i in range(20):
coin = pyglet.sprite.Sprite(
pyglet.resource.image("coin.png"),
x=i*40, y=100,
batch=batch, # Gleicher Batch!
group=fg_group
)
coins.append(coin)
@window.event
def on_draw():
window.clear()
batch.draw() # ALLE Sprites in einem Draw Call
# Sprite-Attribute
player.x = 200
player.y = 400
player.rotation = 45 # Grad (im Uhrzeigersinn bei Pyglet!)
player.scale = 2.0
player.opacity = 128 # 0-255
player.visible = False
rotation in Pyglet dreht im Uhrzeigersinn, in Arcade gegen den Uhrzeigersinn!
Pyglet Shapes (2.0+)
Seit Pyglet 2.0 gibt es pyglet.shapes für einfaches Zeichnen von Grundformen – ohne OpenGL-Direktzugriff.
import pyglet
import pyglet.shapes
window = pyglet.window.Window(800, 600)
batch = pyglet.graphics.Batch()
# Kreis
circle = pyglet.shapes.Circle(
x=400, y=300,
radius=80,
color=(255, 100, 0, 255), # RGBA!
batch=batch
)
# Rechteck
rect = pyglet.shapes.Rectangle(
x=50, y=50,
width=200, height=100,
color=(0, 200, 255),
batch=batch
)
# Linie
line = pyglet.shapes.Line(
x=0, y=0,
x2=800, y2=600,
width=3,
color=(255, 255, 0),
batch=batch
)
# Bogen
arc = pyglet.shapes.Arc(
x=600, y=400,
radius=60,
angle=3.14, # Radiant! 3.14 = 180°
color=(255, 0, 200),
batch=batch
)
# Stern
star = pyglet.shapes.Star(
x=200, y=200,
outer_radius=60,
inner_radius=25,
num_spikes=5,
color=(255, 220, 0),
batch=batch
)
# Ellipse
ellipse = pyglet.shapes.Ellipse(
x=600, y=150,
a=100, b=50, # Halb-Achsen
color=(0, 255, 100),
batch=batch
)
# Polygon
poly = pyglet.shapes.Polygon(
(100,500), (200,550), (250,480), (150,430),
color=(200, 50, 255),
batch=batch
)
@window.event
def on_draw():
window.clear()
batch.draw()
Text & Fonts
import pyglet
# Einfaches Label
label = pyglet.text.Label(
text="Score: 1337",
font_name="Arial",
font_size=24,
x=10, y=580,
anchor_x="left",
anchor_y="top",
color=(255, 255, 255, 255), # RGBA!
batch=batch
)
# Mehrzeiliges HTML-Label
html_label = pyglet.text.HTMLLabel(
text="<b>Fett</b> und <i>kursiv</i>",
x=200, y=300,
width=300,
multiline=True
)
# Schrift aus Datei laden
pyglet.font.add_file("fonts/Orbitron.ttf")
custom_label = pyglet.text.Label(
text="Custom Font!",
font_name="Orbitron",
font_size=30,
x=400, y=300,
anchor_x="center",
anchor_y="center"
)
# Text animieren
def update(dt):
label.text = f"Zeit: {pyglet.clock.get_default().time:.1f}s"
pyglet.clock.schedule_interval(update, 0.1)
Sound & Media
Pyglets Media-System unterstützt Audio und Video. Der MediaPlayer ist die Kern-Klasse für Wiedergabe.
import pyglet
# Sound statisch laden (kurze SFX)
sfx = pyglet.media.load("sounds/coin.wav", streaming=False)
sfx.play()
# Musik als Stream (große Dateien)
music = pyglet.media.load("sounds/background.mp3", streaming=True)
player = pyglet.media.Player()
player.queue(music)
player.loop = True
player.volume = 0.3
player.play()
# Lautstärke und Pan steuern
player.volume = 0.5 # 0.0–1.0
player.pitch = 1.0 # Tonhöhe (0.5=halbe, 2.0=doppelte)
# 3D-Sound (OpenAL Backend)
player.position = (100, 0, 0) # 3D-Raumposition
pyglet.media.get_audio_driver().get_listener().position = (0, 0, 0)
# Auf Event reagieren wenn Sound endet
@player.on("on_eos")
def on_end_of_stream():
print("Sound fertig!")
# Wiedergabe pausieren/fortsetzen
player.pause()
player.play()
# Stoppen
player.delete()
Clock & Scheduling
Pyglets Clock ist der Timer-Dienst – für regelmäßige Updates, verzögerte Aktionen und FPS-Management.
import pyglet
# Funktion alle N Sekunden aufrufen
def spawn_enemy(dt):
print(f"Neuer Gegner! dt={dt:.3f}s")
pyglet.clock.schedule_interval(spawn_enemy, interval=2.0)
# Funktion einmalig nach Verzögerung
def game_start(dt):
print("Spiel startet jetzt!")
pyglet.clock.schedule_once(game_start, delay=3.0)
# So schnell wie möglich (jeden Frame)
def update(dt):
player.x += 200 * dt # Frame-rate-unabhängig
pyglet.clock.schedule(update)
# Timer stoppen
pyglet.clock.unschedule(spawn_enemy)
# FPS begrenzen
pyglet.clock.set_fps_limit(60)
# Aktuelle FPS
fps = pyglet.clock.get_fps()
# Elapsed time seit Start
elapsed = pyglet.clock.get_default().time
FPS Display & Timing
import pyglet
window = pyglet.window.Window(800, 600)
# Eingebauter FPS-Counter
fps_display = pyglet.window.FPSDisplay(window=window)
# Eigener FPS-Counter
fps_label = pyglet.text.Label("FPS: ?", x=10, y=10)
def update_fps(dt):
fps = pyglet.clock.get_fps()
fps_label.text = f"FPS: {fps:.1f}"
pyglet.clock.schedule_interval(update_fps, 0.5)
@window.event
def on_draw():
window.clear()
fps_display.draw() # Eingebauter Counter
fps_label.draw() # Eigenes Label
# Frame-Zeit messen
import time
def timed_update(dt):
start = time.perf_counter()
# ... teure Operation ...
elapsed = time.perf_counter() - start
if elapsed > 0.016: # > 16ms = Frame-Drop
print(f"Frame zu langsam: {elapsed*1000:.1f}ms")
OpenGL direkt nutzen
Pyglet erlaubt direkten Zugriff auf OpenGL – für Custom Shader, Framebuffer, Vertex Buffer Objects und mehr.
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(800, 600)
# Vertex & Fragment Shader schreiben
vert_shader = """
#version 330 core
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
"""
frag_shader = """
#version 330 core
uniform float time;
out vec4 color;
void main() {
float r = 0.5 + 0.5 * sin(time);
color = vec4(r, 0.5, 1.0 - r, 1.0);
}
"""
# Shader-Programm erstellen
program = pyglet.gl.current_context.create_program(
(vert_shader, "vertex"),
(frag_shader, "fragment")
)
# Uniform setzen
program["time"] = 0.0
def update(dt):
program["time"] += dt
pyglet.clock.schedule(update)
Pyglet vs Arcade – Wann was?
| Aspekt | Pyglet 2.1 | Arcade 3.3 |
|---|---|---|
| Abstraktionsebene | Niedrig (OpenGL-nah) | Hoch (Spielorientiert) |
| Lernkurve | Steiler | Flacher |
| Sprites & Kollision | Manuell | Eingebaut |
| Physik | Nicht eingebaut | 3 Engines eingebaut |
| GUI | Nicht eingebaut | UIManager eingebaut |
| Custom Shader | ✅ Volle Kontrolle | ⚠️ Eingeschränkt |
| 3D-Unterstützung | ✅ Möglich | ❌ Nur 2D |
| Zielgruppe | Erfahrene Entwickler | Anfänger bis Fortgeschrittene |
| Leistung | Maximum (direkter GPU-Zugriff) | Sehr gut (optimiertes Arcade) |
| Community | Klein aber aktiv | Wächst (Education-Fokus) |