Im letzten Python PyGame Tutorial haben wir uns angesehen, wie wir unser Projekt objektorientierter aufsetzen können. Wir haben uns darin unsere Klasse Game und eine Klasse Player erstellt. Heute werden wir einen Schritt weitergehen und uns ansehen, wie man in PyGame Kollisionen abfragen kann.
Inhaltsverzeichnis
1. Was werden wir in diesem Beitrag programmieren?
Wenn wir unser Programm starten, sehen wir dessen aktuellen Stand. Darin gibt es unseren Player, den ein rotes Rechteck darstellt und welchen wir mit den Pfeiltasten bewegen können.
Jetzt möchten wir ein Spiel programmieren, in welchem man Münzen aufsammeln kann. Dabei soll eine Kollision zwischen den Münzen und dem Player stattfinden, woraufhin die Münzen verschwinden. Genau das sehen wir uns in diesem Beitrag an.
Zuerst werden wir uns oberflächlich damit beschäftigen, wie das Abfragen von Kollisionen in PyGame funktioniert.
2. Kollisionen in PyGame abfragen
Für Kollisionen stellt Python PyGame sogenannte Rect-Objekte zur Verfügung. Dabei handelt es sich um Rechtecke. Es gibt die Klasse pygame.Rect, welche Rechteckobjekte darstellt und ein solches Rechteckobjekt kann man den eigenen Gameobjekt-Klassen zuweisen.
Darüberhinaus stellt diese Klasse verschiedene Funktionen zum Abfragen von Kollisionen bereit.
Die colliderect-Funktion
Mithilfe der colliderect-Funktion kann man prüfen, ob das Rect-Objekt, auf dem diese Funktion aufgerufen wird, mit einem anderen Rect-Objekt überlappt, also kollidiert. Dieses übergibt man als Parameter.
3. Ein Rect-Objekt erstellen
Wir werden an dieser Stelle zuerst unsere bereits existierende Player-Klasse mit einem Rect-Objekt ausstatten. Dazu bewegen wir uns in die Klasse hinein und erstellen darin in der init-Methode ein neues rect-Feld.
self.surface = game.window
self.rect = pygame.Rect()
def update(self):
Dabei handelt es sich um ein Objekt vom Typ pygame.Rect. Zuerst gibt man dafür die x-Koordinate des linken oberen Punktes des Rechtecks an, welcher self.x und self.y ist.
self.surface = game.window
self.rect = pygame.Rect(self.x, self.y, )
Anschließend wählen wir für die Länge und Breite noch jeweils den Wert 64.
self.surface = game.window
self.rect = pygame.Rect(self.x, self.y, 64, 64)
Auf diese Art haben wir bereits im vorherigen Beitrag ein Rectangle gezeichnet.
Jetzt müssen wir unser Rectangle noch jeden Frame aktualisieren.
Das Rect-Objekt jeden Frame aktualisieren
Das heißt, wir kopieren unser Rect-Objekt von oben und fügen es noch in der Update-Methode ein.
def update(self):
self.rect = pygame.Rect(self.x, self.y, 64, 64)
self.movement(300)
Damit haben wir immer das aktuelle Rectangle unserer Spielfigur. Sichtbar ist dieses zwar noch nicht, dafür können wir damit aber bereits Kollisionsabfragen umsetzen.
Nun benötigen wir natürlich noch andere Objekte, mit welchen unser Spieler kollidieren kann. Dafür erstellen wir eine neue Klasse namens Coin.
4. Die Klasse Coin in PyGame erstellen
Wir legen dafür eine neue Python-Datei an und nennen diese "coin":
In dieser Datei importieren wir wieder PyGame und erstellen dann eine neue Klasse, die wir ebenfalls „Coin“ nennen. Darin ergänzen wir auch die init-Funktion.
import pygame
class Coin:
def _ _init_ _(self):
Wir übergeben der init-Funktion das Spiel, die x- und die y-Koordinate und ergänzen sie folgendermaßen:
class Coin:
def _ _init_ _(self, game, x, y):
self.x = x
self.y = y
self.game = game
Außerdem können wir direkt das Rect-Objekt angeben. Als linken oberen Punkt legen wir hierfür auch wieder self.x und self.y fest und wählen den Wert 16 sowohl für die Länge als auch für die Breite des Rechtecks.
def _ _init_ _(self, game, x, y):
self.x = x
self.y = y
self.game = game
self.rect = pygame.Rect(self.x, self.y, 16, 16)
Jetzt braucht das ganze Konstrukt noch eine update- und eine draw-Funktion. So lange wir noch keinen Inhalt für die Funktionen haben, können wir "pass" als Platzhalter nutzen, damit wir keine Fehlermeldung erhalten:
def update(self):
pass
def draw(self):
pass
In der draw-Funktion möchten wir im Grunde nur ein kleines Rechteck zeichnen. Das machen wir mit dem Rect-Objekt, das wir oben für unseren Coin definiert haben.
Wir schreiben dazu folgenden Code:
def draw(self):
pygame.draw.rect(self.game.window, “yellow”, self.rect)
Dabei übergeben wir game.window, als Farbparameter den Wert „yellow“ also die Farbe Gelb und als das Rechteck, das wir zeichnen möchten, übergeben wir self.rect.
In der update-Funktion möchten wir dann draw aufrufen, was wir folgendermaßen umsetzen:
def update(self):
self.draw()
5. coin-Objekte erstellen
Nun können wir von der Klasse Coin Objekte erstellen und in unserem Level platzieren. Wir bewegen uns dafür zurück in unsere main.py-Datei.
Darin importieren wir die Python-Datei coin.py:
import pygame
import player
import coin
Um ein Coin-Objekt zu erstellen, schreiben wir folgendes:
self.player = player.Player(self, 32, 32)
self.coin = coin.Coin(self, 150, 150)
self.run()
Wir erstellen den coin mit self, übergeben damit also das Game-Objekt und platzieren ihn auf Position 150, 150.
In der run-Funktion rufen wir dann den folgenden Code auf:
self.player.update()
self.coin.update()
pygame.display.update()
Wenn wir das Programm nun starten, sehen wir den Coin in der Konsole:
Wir können diesen Coin zwar noch nicht mit unserem Charakter einsammeln, aber immerhin existiert er schon mal in der Spielwelt.
Jetzt möchten wir allerdings nicht nur ein einziges Coin-Objekt in der Spielwelt sehen, sondern die Möglichkeit haben, beliebig viele zu erstellen, die automatisch in der run-Loop aktualisiert werden. Wie machen wir das?
6. Eine Liste aus coins anlegen
Wir erstellen dafür eine neue Liste aus Coins, die wir „coins“ nennen und weisen dieser gleich zu Beginn drei Coin-Objekte auf unterschiedlichen Koordinaten zu:
self.player = player.Player(self, 32, 32)
self.coin = coin.Coin(self, 150, 150)
self.coins = [coin.Coin(self, 150, 150),
coin.Coin(self, 250, 150),
coin.Coin(self, 250, 250)]
Den vorherigen Coin löschen wir vor dem Starten:
self.player = player.Player(self, 32, 32)
self.coin = coin.Coin(self, 150, 150)
self.coins = [coin.Coin(self, 150, 150),
coin.Coin(self, 250, 150),
coin.Coin(self, 250, 250)]
Jetzt können wir nicht nur einen Coin updaten, so wie wir es zuvor im Code umgesetzt haben, sondern brauchen stattdessen eine andere Lösung für alle Coins:
Wir werden eine for-Schleife durchlaufen, die die ganze Liste aus Coins abarbeitet und auf jedem Coin die update-Methode aufruft.
Dafür schreiben wir den folgenden Code:
self.player.update()
for coin in self.coins:
coin.update()
pygame.display.update()
Mithilfe dieses Codes rufen wir auf jedem Coin die update-Funktion auf. Wenn wir das Spiel jetzt starten, sehen wir drei Coins auf dem Bildschirm:
Das ist auf jeden Fall schon deutlich besser! Somit können wir beliebig viele Coins hinzufügen. Um einen Coin zu ergänzen, hängen wir diesen einfach an die coins-Liste an.
Mithilfe von coins.append lassen sich neue Objekte erstellen, die auch direkt jeden Frame aktualisiert werden.
7. Einzelne coins zerstören
An dieser Stelle werden wir dafür sorgen, dass man einzelne Coins zerstören kann. So soll sichergestellt werden, dass die Coins verschwinden, die der Spieler aufgesammelt hat. Diese sollen dann nicht mehr in der Liste enthalten sein.
Wir erstellen dafür in der coin.py-Datei ein Feld, das wir self.is_destroyed nennen. Standardmäßig setzen wir dieses auf False:
self.rect = pygame.Rect(self.x, self.y, 16, 16)
self.is_destroyed = False
Dieses Feld soll dann auf True gesetzt werden, wenn das Rect des Coins mit dem Rect des Spielerobjekts
kollidiert. Also dann, wenn sich die Rects überlappen.
In der main.py-Datei möchten wir unterdessen bei coin.update jedes Mal folgendes prüfen:
for coin in self.coins:
coin.update()
if coin.is_destroyed:
self.coins.remove(coin)
Damit löschen wir das coin-Objekt aus der Liste.
Jetzt müssen wir nur noch in der coin.py-Datei innerhalb der update-Methode die Kollision prüfen.
Hierfür rufen wir auf dem Rect unseres Coins die colliderect-Methode auf und übergeben dieser als Parameter das Player-Rect, also game.player.rect:
def update(self):
self.draw()
if self.rect.colliderect(self.game.player.rect):
self.is_destroyed = True
Wenn die Bedingung der if-Anweisung erfüllt ist, setzen wir is_destroyed auf den Wert True. Beim Starten des Programms sehen wir nun, dass die Coins beim Einsammeln verschwinden:
8. Abschließende Worte
Man könnte dabei noch einen Sound abspielen oder einen Score hochzählen. Darum werden wir uns aber erst in einem späteren Blogartikel kümmern.
Das ist also die Art und Weise, wie man eine Coin-Klasse hinzufügt, man darin außerdem die Kollision mit dem Spieler prüfen kann und wie man die Coin-Objekte wieder aus der erstellten Liste entfernt.
Die coins-Liste durchläuft jeden Coin und sorgt dafür, dass wir dynamisch so viele neue Coins hinzufügen können, wie wir möchten. Auf jedem Coin ruft sie zudem die update-Methode auf.
Du konntest in diesem Beitrag über Python PyGame und weitere nützliche Funktionen besser kennenlernen und weißt jetzt, wie du in deinem eigenen Spiel Kollisionen
abfragen kannst.