• Home
  • |
  • Blog
  • |
  • PyGame Python Tutorial #4 – Einfache Steuerung programmieren

PyGame Python Tutorial #4 – Einfache Steuerung programmieren

Zuletzt aktualisiert: Juni 29, 2023

Im vergangenen Beitrag haben wir bereits gelernt, wie man in PyGame Python dafür einsetzen kann, um einfache Formen wie Rechtecke und Kreise auf den Bildschirm zu zeichnen. Wie man diese Formen nun zum Leben erweckt und dafür sorgt, dass man sie gezielt bewegen und steuern kann, wirst du in diesem Artikel lernen.

Inhaltsverzeichnis

1. Wie lassen sich in PyGame Objekte bewegen?

Zu einem richtigen Spiel gehört dazu, dass man damit interagieren kann. Aus diesem Grund werde ich dir heute zeigen, wie man Objekte auf dem Bildschirm bewegt, indem man Tastatureingaben abfragt und entsprechend auf diese reagiert.

Wir sehen uns das Ganze am Beispiel eines Rechtecks an, das wir anschließend mit unseren Pfeiltasten bewegen werden.

Info:
Da wir uns in diesem Tutorial über PyGame Python ebenfalls genauer ansehen werden, ist es erforderlich, die Grundlagen von Python verinnerlicht zu haben. Falls du dich mit diesen noch nicht vertraut gemacht haben solltest, sieh dir gerne unseren Python Masterkurs an.

2. Den Spieler in Form eines Rechtecks zeichnen

Im ersten Schritt zeichnen wir ein Rechteck auf den Bildschirm. Dafür schreiben wir unter „window.fill“ die pygame.draw.rect()-Funktion. Mit dieser können wir auf unsere Windowsurface ein Rechteck in einer beliebigen Farbe zeichnen.

Beispielhaft wähle ich hierfür die Farbe Rot.

window.fill(...)
pygame.draw.rect(window, "red", )

Anschließend müssen wir noch weitere Parameter übergeben. Das sind einmal die x- und y-Position, an der wir das Rechteck zeichnen möchten.

Dafür lege ich direkt eine neue Variable oberhalb unserer Schleife an. Würden wir diese innerhalb der Schleife erstellen, würden wir den Wert der Variablen immer wieder überschreiben.

Da es sich bei dem Rechteck um unseren Spieler handeln soll, wählen wir für die x-Position den Namen player_x und setzen diese zu Beginn auf 32. Die Variable player_y setzen wir ebenfalls auf 32.

player_x = 32
player_y = 32

running = True
while ...

Im Tupel, das sich innerhalb der draw.rect-Funktion befindet, übergeben wir dann die Variablen als Koordinaten:

window.fill(...)
pygame.draw.rect(window, "red", (player_x, player_y, ))

Nach player_y müssen wir nun noch eine Breite und eine Höhe angeben. Dafür wählen wir jeweils 64 Pixel.

window.fill(...)
pygame.draw.rect(window, "red", (player_x, player_y, 64, 64))

Damit haben wir unser Rechteck definiert! Wenn wir das Programm starten, erscheint dieses auch schon auf unserem Bildschirm:

PyGame Python Tutorial: Das Rechteck erscheint auf dem Bildschirm

3. Wie lässt sich das Rechteck grundsätzlich bewegen?

Um dieses Rechteck jetzt bewegen zu können, müssen wir nur noch die Koordinaten, für die wir Variablen erstellt haben, verändern.

Erhöhen wir nun also beispielsweise player_x jeden Frame um 1, sehen wir, dass unser Rechteck direkt nach rechts fährt:

window.fill(…)
player_x += 1
pygame.draw.rect(window, "red", (player_x, player_y, 64, 64))

Das Rechteck fährt nach rechts

Nun löschen wir die Zeile aber wieder, da wir schließlich das Rechteck nur dann bewegen möchten, wenn der Benutzer die Pfeiltasten drückt. Wie können wir das umsetzen? Die erste Idee, auf die man kommen könnte, wäre, die Event-Loop dafür zu verwenden:

Die PyGame Eventloop

Darin haben wir bereits ein Event abgefragt, bei dem die ESC-Taste gedrückt wird. Eigentlich müsste doch auf diesem Weg dann auch die Steuerung unseres Rechtecks funktionieren. Schreiben wir also beispielsweise unter der letzten elif-Anweisung den folgenden Code:

elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:

Wenn event.key also die rechte Pfeiltaste ist, könnten wir festlegen:

elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
       player_x += 1

Beim Ausführen des Programms wird uns allerdings eine Sache auffallen:

Wenn wir die rechte Pfeiltaste drücken, bewegt sich das Rechteck tatsächlich um einen Pixel nach rechts. Halten wir die Taste gedrückt, passiert allerdings nichts mehr.

Wir müssen die Taste also für jeden Schritt einmal drücken. Allerdings möchten wir bei der Steuerung, dass das Programm jeden Frame erkennt, ob die Taste noch gedrückt wird oder nicht. Dafür müssen wir uns also eine andere Lösung einfallen lassen.

Ein Event namens „key_pressed“ oder „key_hold“ stellt in Pygame Python nicht bereit. Wir können die Eventloop also nicht direkt verwenden, um unsere Steuerung umzusetzen.

4. Den Spieler per Tastaturkommando steuern

Im key-Modul von PyGame gibt es die get_pressed()-Funktion. Diese Funktion gibt uns den Zustand aller Tasten auf der Tastatur zurück.

Damit können wir jeden Frame abfragen, ob gerade eine bestimmte Taste gedrückt wird oder nicht. Diese Funktion nutzt man folgendermaßen.

Jeden Frame legen wir eine neue Liste namens „keys“ an. pygame.key.get_pressed() gibt diese Liste zurück.

keys = pygame.key.get_pressed()
window.fill(...)

Diese Liste enthält alle Tasten auf der Tastatur und deren aktuellen Zustand. Mit der obenstehenden Zeile schreiben wir diese Liste in „keys“ hinein.

Abfragen können wir diese Liste dann, indem wir zum Beispiel Folgendes schreiben:

keys = pygame.key.get_pressed()

if keys[pygame.K_RIGHT]:

window.fill(...)

Wenn der Benutzer die rechte Pfeiltaste drückt, soll dann folgendes geschehen:

if keys[pygame.K_RIGHT]:
       player_x += 1

Anschließend können wir noch den Code für die linke Pfeiltaste ergänzen:

elif keys[pygame.K_LEFT]:
        player_x -= 1

Für die vertikalen Tasten erstellen wir direkt darunter ein neues if-Statement:

if keys[pygame.K_DOWN]:
      player_y += 1
elif keys[pygame.K_UP]:
      player_y -= 1

Erläuterung des Codes:

Mit der Zeile „keys = pygame.key.get_pressed()“ fragen wir jedes Mal die Keys ab, die gerade gedrückt werden. Dann prüfen wir, ob in der Keys-Liste gerade pygame.K_RIGHT gedrückt wird. Wenn das der Fall ist, möchten wir, dass sich der Spieler um einen Pixel nach rechts bewegt. Drückt der Benutzer hingegen „K_LEFT“, dann soll sich der Spieler nach links bewegen. Gleiches gilt für die Bewegung nach oben und unten.

Beim erneuten Ausführen des Programms stellen wir fest, dass wir unsere Figur jetzt bewegen können.

Wie du womöglich merken wirst, bewegt sich das Rechteck allerdings ziemlich schnell. Das liegt daran, dass unser Spiel noch nicht Framerate-unabhängig läuft. Wenn du einen sehr schnellen Rechner hast, läuft das Spiel schneller. Umgekehrt läuft es jedoch auch bei einem langsamen Rechner ziemlich langsam.

Wir wollen allerdings, dass unser Spiel auf jedem Gerät gleich schnell läuft. Dafür müssen wir in PyGame Python Code für das sogenannte Delta-Timing verwenden.

5. Delta-Timing: Wie das Spiel auf allen Geräten gleich schnell läuft

Zuerst werden wir die Framerate für unser Spiel einstellen. Wir möchten, dass unser Spiel mit 60 Bildern pro Sekunde läuft. Wie setzen wir nun für die Umsetzung in PyGame Python Code ein?

Zunächst erstellen wir eine Variable, die wir „clock“ nennen. In diese Variable schreiben wir ein Objekt des Typen Clock. Die Clock-Klasse befindet sich innerhalb von pygame.time.

player_y = 32
clock = pygame.time.Clock()

running = True

In die erste Zeile der running-Schleife schreiben wir den folgenden Code:

while running:
        clock.tick()
        for event in ...

Zwischen den Klammern können wir nun die gewünschte Framerate angeben. Wir wählen dafür 60:

while running:
        clock.tick(60)
        for event in ...

Damit wird die Clock mit jedem Frame einmal ticken und ist so getimt, dass unser Programm mit maximal 60 Bildern pro Sekunde läuft.

Unser Spieler soll sich nun also nicht mehr um einen Pixel pro Frame bewegen, sondern um einen Pixel pro Sekunde, unabhängig von der Framerate.

Denn auf die Framerate haben wir nicht immer einen Einfluss, da sie von der Geschwindigkeit eines Rechners abhängt.

Wir haben sie zwar auf 60 limitiert, allerdings kann diese auch darunter liegen. Deshalb werden wir dafür sorgen, dass sich der Spieler bzw. das Rechteck um eine bestimmte Anzahl an Pixeln pro Sekunde statt pro Frame bewegt.

Wie bewerkstelligen wir das? Zuerst legen wir eine Variable namens „delta_time“ an, die immer die Zeit in Sekunden enthalten wird, die es gebraucht hat, um den letzten Frame zu berechnen.

Dafür steuern wir zurück in die Zeile clock.tick(60) und schreiben das, was uns die Funktion zurückgibt, in die Variable „delta_time“. Anschließend teilen wir das Ganze durch 1000, da die Funktion die Zeit in Millisekunden zurückgibt, wir sie aber in Sekunden umwandeln möchten.

while running:
        delta_time = clock.tick(60) / 1000
        for event in ...

Wir haben hier also unsere delta_time berechnet. 

Wie funktioniert der Code?

tick gibt uns die Zeit zurück, die es gebraucht hat, um den Frame zu berechnen. Dabei handelt es sich also um die Zeit in Millisekunden, die zwischen jedem Tick liegt. Das Ergebnis teilen wir durch 1000, um die Millisekunden in Sekunden umzuwandeln und schreiben es anschließend in die Variable delta_time.

Jetzt können wir an der Stelle, an der wir unseren Character bewegen, festlegen, um wie viele Pixel pro Sekunde wir diesen bewegen möchten. Das setzen wir um, indem wir die Anzahl der Pixel mit delta_time multiplizieren:

player_x += 1 * delta_time

Außerdem können wir noch eine Variable namens „speed“ anlegen und diese mal auf 120 Pixel setzen. Damit bewegen wir den Spieler um 120 Pixel pro Sekunde. Wir schreiben in unserem Code dann also nicht mehr „+= 1“, sondern „+= speed“:

PyGame Python - wir erstellen die Variable speed

Damit haben wir eine Framerate-unabhängige Steuerung programmiert! Wenn wir das Programm jetzt starten, können wir den Spieler frei bewegen. Die Geschwindigkeit lässt sich über die Variable speed beliebig anpassen und beispielsweise noch etwas schneller machen, indem wir sie von 120 auf 240 Pixel pro Sekunde setzen: 

speed = 240

Wir haben nun also unser Rechteck bzw. Quadrat mit dem Keyboard-Input zum Bewegen gebracht. Dazu haben wir die key.get_pressed()-Funktion verwendet und eine delta_time-Variable angelegt, um unser Programm Framerate-unabhängig zu machen.