PyGame: Eine Einführung in die Spielprogrammierung in Python

PyGame: Eine Einführung in die Spielprogrammierung in Python

Als ich Ende des letzten Jahrtausends anfing, Computerprogrammierung zu lernen, war dies auf meinen Wunsch zurückzuführen, Computerspiele zu schreiben. Ich habe versucht herauszufinden, wie man Spiele in jeder Sprache und auf jeder Plattform schreibt, die ich gelernt habe, einschließlich Python. Auf diese Weise habe ichpygame entdeckt und gelernt, wie man damit Spiele und andere grafische Programme schreibt. Zu der Zeit wollte ich wirklich eine Grundierung fürpygame.

Am Ende dieses Artikels können Sie:

  • Zeichnen Sie Elemente auf Ihrem Bildschirm

  • Spielen Sie Soundeffekte und Musik ab

  • Benutzereingaben verarbeiten

  • Implementieren Sie Ereignisschleifen

  • Beschreiben Sie, wie sich die Spielprogrammierung von der standardmäßigen prozeduralen Python-Programmierung unterscheidet

Dieser Primer setzt voraus, dass Siebasic understanding of writing Python programs haben, einschließlich benutzerdefinierter Funktionen,imports,loops undconditionals. Sie sollten auch mithow to open files auf Ihrer Plattform vertraut sein. Abasic understanding of object-oriented Python ist ebenfalls hilfreich. pygame funktioniert mit den meisten Versionen von Python, aber Python 3.6 wird in diesem Artikel empfohlen und verwendet.

Sie können den gesamten Code in diesem Artikel abrufen:

Clone Repo:Click here to clone the repo you’ll use, um in diesem Tutorial zu lernen, wie man PyGame verwendet.

Hintergrund und Einrichtung

pygame ist ein Python-Wrapper fürSDL library, der fürSimple DirectMedia Layer steht. SDL bietet plattformübergreifenden Zugriff auf die zugrunde liegenden Multimedia-Hardwarekomponenten Ihres Systems wie Sound, Video, Maus, Tastatur und Joystick. pygame begann sein Leben als Ersatz für die blockiertenPySDL project. Die plattformübergreifende Natur von SDL undpygamebedeutet, dass Sie Spiele und umfangreiche Multimedia-Python-Programme für jede Plattform schreiben können, die sie unterstützt!

Verwenden Sie den entsprechenden Befehlpip, umpygame auf Ihrer Plattform zu installieren:

$ pip install pygame

Sie können die Installation überprüfen, indem Sie eines der mit der Bibliothek gelieferten Beispiele laden:

$ python3 -m pygame.examples.aliens

Wenn ein Spielfenster angezeigt wird, istpygame ordnungsgemäß installiert! Wenn Sie auf Probleme stoßen, beschreibtGetting Started guideeinige bekannte Probleme und Einschränkungen für alle Plattformen.

Grundlegendes PyGame-Programm

Bevor wir uns mit Einzelheiten befassen, werfen wir einen Blick auf ein grundlegendespygame-Programm. Dieses Programm erstellt ein Fenster, füllt den Hintergrund mit Weiß und zeichnet einen blauen Kreis in der Mitte:

 1 # Simple pygame program
 2
 3 # Import and initialize the pygame library
 4 import pygame
 5 pygame.init()
 6
 7 # Set up the drawing window
 8 screen = pygame.display.set_mode([500, 500])
 9
10 # Run until the user asks to quit
11 running = True
12 while running:
13
14     # Did the user click the window close button?
15     for event in pygame.event.get():
16         if event.type == pygame.QUIT:
17             running = False
18
19     # Fill the background with white
20     screen.fill((255, 255, 255))
21
22     # Draw a solid blue circle in the center
23     pygame.draw.circle(screen, (0, 0, 255), (250, 250), 75)
24
25     # Flip the display
26     pygame.display.flip()
27
28 # Done! Time to quit.
29 pygame.quit()

Wenn Sie dieses Programm ausführen, wird ein Fenster angezeigt, das folgendermaßen aussieht:

A simple pygame program

Lassen Sie uns diesen Code abschnittsweise aufschlüsseln:

  • Lines 4 and 5 importiert und initialisiert diepygame Bibliothek. Ohne diese Linien gibt es keinpygame.

  • Line 8 richtet das Anzeigefenster Ihres Programms ein. Sie geben entweder eine Liste oder ein Tupel an, das die Breite und Höhe des zu erstellenden Fensters angibt. Dieses Programm verwendet eine Liste, um ein quadratisches Fenster mit 500 Pixeln auf jeder Seite zu erstellen.

  • Lines 11 and 12 richtet eingame loop ein, um zu steuern, wann das Programm endet. Sie werden später in diesem Tutorial Spieleschleifen behandeln.

  • Lines 15 to 17 scannen und behandelnevents innerhalb der Spielschleife. Sie werden auch etwas später zu Veranstaltungen gelangen. In diesem Fall wird nurpygame.QUIT behandelt. Dies tritt auf, wenn der Benutzer auf die Schaltfläche zum Schließen des Fensters klickt.

  • Line 20 füllt das Fenster mit einer Volltonfarbe. screen.fill() akzeptiert entweder eine Liste oder ein Tupel, in dem die RGB-Werte für die Farbe angegeben sind. Da(255, 255, 255) angegeben wurde, ist das Fenster mit Weiß gefüllt.

  • Line 23 zeichnet mit den folgenden Parametern einen Kreis im Fenster:

    • screen:ist das Fenster, in dem gezeichnet werden soll

    • (0, 0, 255):ist ein Tupel mit RGB-Farbwerten

    • (250, 250):ist ein Tupel, das die Mittelkoordinaten des Kreises angibt

    • 75:ist der Radius des Kreises, der in Pixel gezeichnet werden soll

  • Line 26 aktualisiert den Inhalt der Anzeige auf dem Bildschirm. Ohne diesen Aufruf erscheint nichts im Fenster!

  • Line 29 verlässtpygame. Dies geschieht erst, wenn die Schleife beendet ist.

Dies ist diepygame-Version von "Hallo Welt". Lassen Sie uns nun etwas tiefer in die Konzepte hinter diesem Code eintauchen.

PyGame-Konzepte

Dapygame und die SDL-Bibliothek auf verschiedenen Plattformen und Geräten portierbar sind, müssen beide Abstraktionen für verschiedene Hardware-Realitäten definieren und damit arbeiten. Wenn Sie diese Konzepte und Abstraktionen verstehen, können Sie Ihre eigenen Spiele entwerfen und entwickeln.

Initialisierung und Module

Diepygame-Bibliothek istcomposed of a number of Python constructs, die mehrere verschiedenemodules enthält. Diese Module bieten abstrakten Zugriff auf bestimmte Hardware auf Ihrem System sowie einheitliche Methoden für die Arbeit mit dieser Hardware. Beispielsweise ermöglichtdisplay einen einheitlichen Zugriff auf Ihre Videoanzeige, währendjoystick die abstrakte Steuerung Ihres Joysticks ermöglicht.

Nach dem Importieren derpygame-Bibliothek im obigen Beispiel haben Sie zuerstinitialize PyGame mitpygame.init() verwendet. Diese Funktioncalls the separate init() functions aller enthaltenenpygame Module. Da diese Module Abstraktionen für bestimmte Hardware sind, ist dieser Initialisierungsschritt erforderlich, damit Sie unter Linux, Windows und Mac mit demselben Code arbeiten können.

Anzeigen und Oberflächen

Zusätzlich zu den Modulen enthältpygame auch mehrere Pythonclasses, die nicht hardwareabhängige Konzepte enthalten. Eines davon istSurface, das im Grunde genommen einen rechteckigen Bereich definiert, auf den Sie zeichnen können. Surface Objekte werden in vielen Kontexten inpygame verwendet. Später erfahren Sie, wie Sie ein Bild inSurfaceladen und auf dem Bildschirm anzeigen.

Inpygame wird alles auf einem einzelnen vom Benutzer erstelltendisplay angezeigt. Dies kann ein Fenster oder ein Vollbild sein. Die Anzeige wird mit.set_mode() erstellt, wobeiSurface zurückgegeben werden, die den sichtbaren Teil des Fensters darstellen. Es ist diesesSurface, das Sie an Zeichenfunktionen wiepygame.draw.circle() übergeben, und der Inhalt diesesSurface wird auf die Anzeige verschoben, wenn Siepygame.display.flip() aufrufen.

Bilder und Rechtecke

Ihr grundlegendespygame-Programm hat eine Form direkt auf dieSurface des Displays gezeichnet. Sie können jedoch auch mit Bildern auf der Festplatte arbeiten. Mitimage module können Sieload undsave Bilder in einer Vielzahl gängiger Formate anzeigen. Bilder werden inSurface Objekte geladen, die dann auf vielfältige Weise bearbeitet und angezeigt werden können.

Wie oben erwähnt, werdenSurface Objekte durch Rechtecke dargestellt, ebenso wie viele andere Objekte inpygame, wie Bilder und Fenster. Rechtecke werden so häufig verwendet, dass es nurspecial Rect classgibt, um sie zu handhaben. Sie werdenRect Objekte und Bilder in Ihrem Spiel verwenden, um Spieler und Feinde zu zeichnen und Kollisionen zwischen ihnen zu verwalten.

Okay, das ist genug Theorie. Lass uns ein Spiel entwerfen und schreiben!

Grundlegendes Spieldesign

Bevor Sie mit dem Schreiben von Code beginnen, ist es immer eine gute Idee, ein Design einzurichten. Da dies ein Tutorial-Spiel ist, wollen wir auch ein grundlegendes Gameplay dafür entwerfen:

  • Das Ziel des Spiels ist es, eingehende Hindernisse zu vermeiden:

    • Der Player startet auf der linken Seite des Bildschirms.

    • Die Hindernisse treten zufällig von rechts ein und bewegen sich in einer geraden Linie nach links.

  • Der Spieler kann sich nach links, rechts, oben oder unten bewegen, um den Hindernissen auszuweichen.

  • Der Player kann sich nicht vom Bildschirm entfernen.

  • Das Spiel endet entweder, wenn der Spieler von einem Hindernis getroffen wird oder wenn der Benutzer das Fenster schließt.

Bei der Beschreibung von Softwareprojekten pflegte einformer colleague of minezu sagen: "Sie wissen nicht, was Sie tun, bis Sie wissen, was Sie nicht tun." In diesem Sinne werden in diesem Tutorial einige Dinge nicht behandelt:

  • Keine mehrfachen Leben

  • Keine Punktezählung

  • Keine Spielerangriffsfähigkeiten

  • Keine fortgeschrittenen Levels

  • Keine Boss-Charaktere

Sie können versuchen, diese und andere Funktionen zu Ihrem eigenen Programm hinzuzufügen.

Lass uns anfangen!

PyGame importieren und initialisieren

Nachdem Siepygame importiert haben, müssen Sie es auch initialisieren. Dadurch kannpygame seine Abstraktionen mit Ihrer spezifischen Hardware verbinden:

 1 # Import the pygame module
 2 import pygame
 3
 4 # Import pygame.locals for easier access to key coordinates
 5 # Updated to conform to flake8 and black standards
 6 from pygame.locals import (
 7     K_UP,
 8     K_DOWN,
 9     K_LEFT,
10     K_RIGHT,
11     K_ESCAPE,
12     KEYDOWN,
13     QUIT,
14 )
15
16 # Initialize pygame
17 pygame.init()

Diepygame-Bibliothek definiert neben Modulen und Klassen viele andere Dinge. Außerdem werden einigelocal constants für Tastenanschläge, Mausbewegungen und Anzeigeattribute definiert. Sie verweisen auf diese Konstanten mit der Syntaxpygame.<CONSTANT>. Durch Importieren bestimmter Konstanten auspygame.locals können Sie stattdessen die Syntax<CONSTANT> verwenden. Dies erspart Ihnen einige Tastenanschläge und verbessert die allgemeine Lesbarkeit.

Einrichten des Displays

Jetzt brauchen Sie etwas zum Zeichnen! Erstellen Sie einscreen als Gesamtfläche:

 1 # Import the pygame module
 2 import pygame
 3
 4 # Import pygame.locals for easier access to key coordinates
 5 # Updated to conform to flake8 and black standards
 6 from pygame.locals import (
 7     K_UP,
 8     K_DOWN,
 9     K_LEFT,
10     K_RIGHT,
11     K_ESCAPE,
12     KEYDOWN,
13     QUIT,
14 )
15
16 # Initialize pygame
17 pygame.init()
18
19 # Define constants for the screen width and height
20 SCREEN_WIDTH = 800
21 SCREEN_HEIGHT = 600
22
23 # Create the screen object
24 # The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
25 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

Sie erstellen den zu verwendenden Bildschirm, indem Siepygame.display.set_mode() aufrufen und ein Tupel oder eine Liste mit der gewünschten Breite und Höhe übergeben. In diesem Fall ist das Fenster 800 x 600, wie durch die KonstantenSCREEN_WIDTH undSCREEN_HEIGHT in den Zeilen 20 und 21 definiert. Dies gibt einSurface zurück, das die Innenabmessungen des Fensters darstellt. Dies ist der Teil des Fensters, den Sie steuern können, während das Betriebssystem die Fensterränder und die Titelleiste steuert.

Wenn Sie dieses Programm jetzt ausführen, wird kurz ein Fenster angezeigt, das beim Beenden des Programms sofort verschwindet. Blinzeln Sie nicht, sonst könnten Sie es verpassen! Im nächsten Abschnitt konzentrieren Sie sich auf die Hauptspielschleife, um sicherzustellen, dass Ihr Programm nur beendet wird, wenn Sie die richtige Eingabe erhalten.

Einrichten der Spielschleife

Jedes Spiel von Pong bis Fortnite verwendet eingame loop, um das Gameplay zu steuern. Die Spielschleife macht vier sehr wichtige Dinge:

  1. Verarbeitet Benutzereingaben

  2. Aktualisiert den Status aller Spielobjekte

  3. Aktualisiert die Anzeige und die Audioausgabe

  4. Behält die Geschwindigkeit des Spiels bei

Jeder Zyklus der Spielschleife wird alsframe bezeichnet. Je schneller Sie die einzelnen Schritte ausführen können, desto schneller läuft Ihr Spiel. Frames treten weiterhin auf, bis eine Bedingung zum Beenden des Spiels erfüllt ist. In Ihrem Design gibt es zwei Bedingungen, die die Spielschleife beenden können:

  1. Der Spieler kollidiert mit einem Hindernis. (Sie werden sich später mit der Kollisionserkennung befassen.)

  2. Der Spieler schließt das Fenster.

Das erste, was die Spielschleife tut, ist die Benutzereingabe zu verarbeiten, damit sich der Spieler auf dem Bildschirm bewegen kann. Daher benötigen Sie eine Möglichkeit, eine Vielzahl von Eingaben zu erfassen und zu verarbeiten. Sie tun dies mit dem Ereignissystempygame.

Ereignisse verarbeiten

Tastendrücke, Mausbewegungen und sogar Joystickbewegungen sind einige der Möglichkeiten, mit denen ein Benutzer Eingaben vornehmen kann. Alle Benutzereingaben führen dazu, dassevent generiert werden. Ereignisse können jederzeit auftreten und entstehen oft (aber nicht immer) außerhalb des Programms. Alle Ereignisse inpygame werden in die Ereigniswarteschlange gestellt, auf die dann zugegriffen und sie bearbeitet werden können. Der Umgang mit Ereignissen wird alshandling bezeichnet, und der Code dafür wird alsevent handler bezeichnet.

Jedem Ereignis inpygame ist ein Ereignistype zugeordnet. Für Ihr Spiel sind die Ereignistypen, auf die Sie sich konzentrieren, Tastendruck und Fensterschluss. Tastendruckereignisse haben den EreignistypKEYDOWN, und das Fensterschließereignis hat den TypQUIT. Mit verschiedenen Ereignistypen können auch andere Daten verknüpft sein. Beispielsweise hat der EreignistypKEYDOWNauch eine Variable namenskey, um anzugeben, welche Taste gedrückt wurde.

Sie greifen auf die Liste aller aktiven Ereignisse in der Warteschlange zu, indem Siepygame.event.get() aufrufen. Anschließend durchlaufen Sie diese Liste, überprüfen jeden Ereignistyp und reagieren entsprechend:

27 # Variable to keep the main loop running
28 running = True
29
30 # Main loop
31 while running:
32     # Look at every event in the queue
33     for event in pygame.event.get():
34         # Did the user hit a key?
35         if event.type == KEYDOWN:
36             # Was it the Escape key? If so, stop the loop.
37             if event.key == K_ESCAPE:
38                 running = False
39
40         # Did the user click the window close button? If so, stop the loop.
41         elif event.type == QUIT:
42             running = False

Schauen wir uns diese Spielschleife genauer an:

  • Line 28 richtet eine Steuervariable für die Spielschleife ein. Um die Schleife und das Spiel zu verlassen, setzen Sierunning = False. Die Spielschleife beginnt in Zeile 29.

  • Line 31 startet den Ereignishandler und durchläuft jedes Ereignis, das sich derzeit in der Ereigniswarteschlange befindet. Wenn keine Ereignisse vorhanden sind, ist die Liste leer und der Handler unternimmt nichts.

  • Lines 35 to 38 prüfen, ob das aktuelleevent.type einKEYDOWN-Ereignis ist. Wenn dies der Fall ist, überprüft das Programm anhand des Attributsevent.key, welche Taste gedrückt wurde. Wenn der Schlüssel der Schlüssel[.kbd .key-escape]#Esc # ist, der durchK_ESCAPE angezeigt wird, verlässt er die Spielschleife, indem errunning = False setzt.

  • Lines 41 and 42 führen eine ähnliche Prüfung für den EreignistypQUIT durch. Dieses Ereignis tritt nur auf, wenn der Benutzer auf die Schaltfläche zum Schließen des Fensters klickt. Der Benutzer kann auch eine andere Betriebssystemaktion verwenden, um das Fenster zu schließen.

Wenn Sie diese Zeilen zum vorherigen Code hinzufügen und ausführen, wird ein Fenster mit einem leeren oder schwarzen Bildschirm angezeigt:

An empty

Das Fenster verschwindet erst, wenn Sie die Taste[.kbd .key-escape]#Esc # drücken oder auf andere Weise einQUIT-Ereignis auslösen, indem Sie das Fenster schließen.

Zeichnen auf dem Bildschirm

Im Beispielprogramm haben Sie mit zwei Befehlen auf dem Bildschirm gezeichnet:

  1. screen.fill(), um den Hintergrund zu füllen

  2. pygame.draw.circle(), um einen Kreis zu zeichnen

Jetzt lernen Sie einen dritten Weg, um auf den Bildschirm zu zeichnen: Verwenden vonSurface.

Denken Sie daran, dass einSurface ein rechteckiges Objekt ist, auf das Sie wie ein leeres Blatt Papier zeichnen können. Dasscreen-Objekt ist einSurface, und Sie können Ihre eigenenSurface-Objekte unabhängig vom Bildschirm erstellen. Mal sehen, wie das funktioniert:

44 # Fill the screen with white
45 screen.fill((255, 255, 255))
46
47 # Create a surface and pass in a tuple containing its length and width
48 surf = pygame.Surface((50, 50))
49
50 # Give the surface a color to separate it from the background
51 surf.fill((0, 0, 0))
52 rect = surf.get_rect()

Nachdem der Bildschirm in Zeile 45 mit Weiß gefüllt ist, wird in Zeile 48 ein neuesSurface erstellt. DieseSurface sind 50 Pixel breit, 50 Pixel hoch undsurf zugeordnet. An diesem Punkt behandeln Sie es genauso wie diescreen. Online füllen Sie es also mit Schwarz. Sie können auch mit.get_rect()auf die zugrunde liegendenRect zugreifen. Dies wird zur späteren Verwendung alsrect gespeichert.

Verwenden von.blit() und.flip()

Das Erstellen eines neuenSurface reicht nicht aus, um es auf dem Bildschirm anzuzeigen. Dazu müssen SieblitSurface auf ein anderesSurface setzen. Der Begriffblit steht fürBlock Transfer, und mit.blit() kopieren Sie den Inhalt vonSurface in einen anderen. Sie können nur.blit() von einemSurface zum anderen übertragen, aber da der Bildschirm nur ein weitererSurface ist, ist dies kein Problem. So zeichnen Siesurf auf dem Bildschirm:

54 # This line says "Draw surf onto the screen at the center"
55 screen.blit(surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
56 pygame.display.flip()

Der Aufruf von.blit()in Zeile 55 erfordert zwei Argumente:

  1. Die zu zeichnendenSurface

  2. Der Ort, an dem es auf die QuelleSurface gezeichnet werden soll

Die Koordinaten(SCREEN_WIDTH/2, SCREEN_HEIGHT/2) weisen Ihr Programm an,surf genau in der Mitte des Bildschirms zu platzieren, aber es sieht nicht ganz so aus:

Blitting a surface onto the screen

Der Grund, warum das Bild außermittig aussieht, ist, dass.blit() dietop-left corner vonsurf an der angegebenen Stelle platziert. Wenn Sie möchten, dasssurf zentriert werden, müssen Sie einige Berechnungen durchführen, um sie nach oben und links zu verschieben. Sie können dies tun, indem Sie die Breite und Höhe vonsurf von der Breite und Höhe des Bildschirms subtrahieren, jeweils durch 2 teilen, um die Mitte zu lokalisieren, und diese Zahlen dann als Argumente anscreen.blit() übergeben:

54 # Put the center of surf at the center of the display
55 surf_center = (
56     (SCREEN_WIDTH-surf.get_width())/2,
57     (SCREEN_HEIGHT-surf.get_height())/2
58 )
59
60 # Draw surf at the new coordinates
61 screen.blit(surf, surf_center)
62 pygame.display.flip()

Beachten Sie den Aufruf vonpygame.display.flip() nach dem Aufruf vonblit(). Dadurch wird der gesamte Bildschirm mit allem aktualisiert, was seit dem letzten Flip gezeichnet wurde. Ohne den Aufruf von.flip() wird nichts angezeigt.

Sprites

In Ihrem Spieldesign beginnt der Spieler links und Hindernisse kommen von rechts herein. Sie können alle Hindernisse mitSurface-Objekten darstellen, um das Zeichnen zu vereinfachen. Aber woher wissen Sie, wo Sie sie zeichnen müssen? Woher wissen Sie, ob ein Hindernis mit dem Spieler kollidiert ist? Was passiert, wenn das Hindernis vom Bildschirm fliegt? Was ist, wenn Sie Hintergrundbilder zeichnen möchten, die sich auch bewegen? Was ist, wenn Ihre Bilder animiert werden sollen? Sie können all diese Situationen und mehr mitsprites bewältigen.

In Bezug auf die Programmierung ist einsprite eine 2D-Darstellung von etwas auf dem Bildschirm. Im Wesentlichen ist es ein Bild. pygame bietet einSprite class, das eine oder mehrere grafische Darstellungen eines Spielobjekts enthält, das Sie auf dem Bildschirm anzeigen möchten. Um es zu verwenden, erstellen Sie eine neue Klasse, dieSprite erweitert. Auf diese Weise können Sie die integrierten Methoden verwenden.

Spieler

So verwenden SieSprite Objekte mit dem aktuellen Spiel, um den Spieler zu definieren. Fügen Sie diesen Code nach Zeile 18 ein:

20 # Define a Player object by extending pygame.sprite.Sprite
21 # The surface drawn on the screen is now an attribute of 'player'
22 class Player(pygame.sprite.Sprite):
23     def __init__(self):
24         super(Player, self).__init__()
25         self.surf = pygame.Surface((75, 25))
26         self.surf.fill((255, 255, 255))
27         self.rect = self.surf.get_rect()

Sie definieren zuerstPlayer, indem Siepygame.sprite.Sprite in Zeile 22 erweitern. Dann verwendet.__init__().super(), um die.__init__()-Methode vonSprite aufzurufen. Weitere Informationen dazu, warum dies erforderlich ist, finden Sie unterSupercharge Your Classes With Python super().

Als Nächstes definieren und initialisieren Sie.surf, um das anzuzeigende Bild zu halten. Dies ist derzeit ein weißes Feld. Sie definieren und initialisieren auch.rect, mit denen Sie den Player später zeichnen. Um diese neue Klasse zu verwenden, müssen Sie ein neues Objekt erstellen und auch den Zeichnungscode ändern. Erweitern Sie den folgenden Codeblock, um alles zusammen anzuzeigen:

Führen Sie diesen Code aus. Sie sehen ungefähr in der Mitte des Bildschirms ein weißes Rechteck:

Basic player sprite being drawn

Was würde Ihrer Meinung nach passieren, wenn Sie Zeile 59 inscreen.blit(player.surf, player.rect) ändern würden? Probieren Sie es aus und sehen Sie:

55 # Fill the screen with black
56 screen.fill((0, 0, 0))
57
58 # Draw the player on the screen
59 screen.blit(player.surf, player.rect)
60
61 # Update the display
62 pygame.display.flip()

Wenn SieRect an.blit() übergeben, werden die Koordinaten der oberen linken Ecke zum Zeichnen der Oberfläche verwendet. Sie werden dies später verwenden, um Ihren Spieler in Bewegung zu setzen!

Benutzereingabe

Bisher haben Sie gelernt, wie Siepygame einrichten und Objekte auf dem Bildschirm zeichnen. Jetzt beginnt der wahre Spaß! Sie können den Player über die Tastatur steuern.

Zuvor haben Sie gesehen, dasspygame.event.get() eine Liste der Ereignisse in der Ereigniswarteschlange zurückgibt, die Sie nachKEYDOWN Ereignistypen durchsuchen. Dies ist nicht die einzige Möglichkeit, Tastendrücke zu lesen. pygame liefert auchpygame.event.get_pressed(), wasdictionary zurückgibt, das alle aktuellenKEYDOWN-Ereignisse in der Warteschlange enthält.

Fügen Sie dies direkt nach der Ereignisbehandlungsschleife in Ihre Spielschleife ein. Dies gibt ein Wörterbuch zurück, das die am Anfang jedes Frames gedrückten Tasten enthält:

54 # Get the set of keys pressed and check for user input
55 pressed_keys = pygame.key.get_pressed()

Als nächstes schreiben Sie eine Methode inPlayer, um dieses Wörterbuch zu akzeptieren. Dadurch wird das Verhalten des Sprites anhand der gedrückten Tasten definiert. So könnte das aussehen:

29 # Move the sprite based on user keypresses
30 def update(self, pressed_keys):
31     if pressed_keys[K_UP]:
32         self.rect.move_ip(0, -5)
33     if pressed_keys[K_DOWN]:
34         self.rect.move_ip(0, 5)
35     if pressed_keys[K_LEFT]:
36         self.rect.move_ip(-5, 0)
37     if pressed_keys[K_RIGHT]:
38         self.rect.move_ip(5, 0)

K_UP,K_DOWN,K_LEFT undK_RIGHT entsprechen den Pfeiltasten auf der Tastatur. Wenn der Wörterbucheintrag für diese TasteTrue lautet, ist diese Taste gedrückt, und Sie bewegen den Spieler.rect in die richtige Richtung. Hier verwenden Sie.move_ip(), was fürmove in place steht, um die aktuellenRect zu verschieben.

Dann können Sie.update() in jedem Frame aufrufen, um das Player-Sprite als Reaktion auf Tastendrücke zu bewegen. Fügen Sie diesen Aufruf direkt nach dem Aufruf zu.get_pressed() hinzu:

52 # Main loop
53 while running:
54     # for loop through the event queue
55     for event in pygame.event.get():
56         # Check for KEYDOWN event
57         if event.type == KEYDOWN:
58             # If the Esc key is pressed, then exit the main loop
59             if event.key == K_ESCAPE:
60                 running = False
61         # Check for QUIT event. If QUIT, then set running to false.
62         elif event.type == QUIT:
63             running = False
64
65     # Get all the keys currently pressed
66     pressed_keys = pygame.key.get_pressed()
67
68     # Update the player sprite based on user keypresses
69     player.update(pressed_keys)
70
71     # Fill the screen with black
72     screen.fill((0, 0, 0))

Jetzt können Sie Ihr Player-Rechteck mit den Pfeiltasten über den Bildschirm bewegen:

Keypresses moving a sprite in pygame

Möglicherweise stellen Sie zwei kleine Probleme fest:

  1. Das Spielerrechteck kann sich sehr schnell bewegen, wenn eine Taste gedrückt gehalten wird. Daran werden Sie später arbeiten.

  2. Das Player-Rechteck kann sich vom Bildschirm entfernen. Lösen wir das jetzt.

Um den Player auf dem Bildschirm zu halten, müssen Sie eine Logik hinzufügen, um festzustellen, ob sich dierect vom Bildschirm entfernen. Dazu überprüfen Sie, ob die Koordinaten vonrectüber die Bildschirmgrenze hinaus verschoben wurden. Wenn ja, weisen Sie das Programm an, es wieder an den Rand zu verschieben:

25 # Move the sprite based on user keypresses
26 def update(self, pressed_keys):
27     if pressed_keys[K_UP]:
28         self.rect.move_ip(0, -5)
29     if pressed_keys[K_DOWN]:
30         self.rect.move_ip(0, 5)
31     if pressed_keys[K_LEFT]:
32         self.rect.move_ip(-5, 0)
33     if pressed_keys[K_RIGHT]:
34         self.rect.move_ip(5, 0)
35
36     # Keep player on the screen
37     if self.rect.left < 0:
38         self.rect.left = 0
39     if self.rect.right > SCREEN_WIDTH:
40         self.rect.right = SCREEN_WIDTH
41     if self.rect.top <= 0:
42         self.rect.top = 0
43     if self.rect.bottom >= SCREEN_HEIGHT:
44         self.rect.bottom = SCREEN_HEIGHT

Anstatt.move() zu verwenden, ändern Sie hier einfach die entsprechenden Koordinaten von.top,.bottom,.left oder.right direkt. Wenn Sie dies testen, wird das Player-Rechteck nicht mehr vom Bildschirm verschoben.

Fügen wir nun einige Feinde hinzu!

Feinde

Was ist ein Spiel ohne Feinde? Sie werden die Techniken, die Sie bereits gelernt haben, verwenden, um eine grundlegende feindliche Klasse zu erstellen, und dann viele davon erstellen, die Ihr Spieler vermeiden kann. Importieren Sie zunächst die Bibliothekrandom:

 4 # Import random for random numbers
 5 import random

Erstellen Sie dann eine neue Sprite-Klasse mit dem NamenEnemy nach demselben Muster, das Sie fürPlayer verwendet haben:

55 # Define the enemy object by extending pygame.sprite.Sprite
56 # The surface you draw on the screen is now an attribute of 'enemy'
57 class Enemy(pygame.sprite.Sprite):
58     def __init__(self):
59         super(Enemy, self).__init__()
60         self.surf = pygame.Surface((20, 10))
61         self.surf.fill((255, 255, 255))
62         self.rect = self.surf.get_rect(
63             center=(
64                 random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
65                 random.randint(0, SCREEN_HEIGHT),
66             )
67         )
68         self.speed = random.randint(5, 20)
69
70     # Move the sprite based on speed
71     # Remove the sprite when it passes the left edge of the screen
72     def update(self):
73         self.rect.move_ip(-self.speed, 0)
74         if self.rect.right < 0:
75             self.kill()

Es gibt vier bemerkenswerte Unterschiede zwischenEnemy undPlayer:

  1. On lines 62 to 67 aktualisieren Sierect so, dass sie eine zufällige Position am rechten Bildschirmrand sind. Die Mitte des Rechtecks ​​befindet sich direkt neben dem Bildschirm. Es befindet sich an einer Position zwischen 20 und 100 Pixel vom rechten Rand entfernt und irgendwo zwischen dem oberen und unteren Rand.

  2. On line 68 definieren Sie.speed als Zufallszahl zwischen 5 und 20. Dies gibt an, wie schnell sich dieser Feind auf den Spieler zubewegt.

  3. On lines 73 to 76 definieren Sie.update(). Es braucht keine Argumente, da sich Feinde automatisch bewegen. Stattdessen bewegt.update() den Feind mit den.speed, die bei seiner Erstellung definiert wurden, zur linken Seite des Bildschirms.

  4. On line 74 überprüfen Sie, ob sich der Feind vom Bildschirm entfernt hat. Um sicherzustellen, dassEnemy vollständig vom Bildschirm entfernt ist und nicht einfach verschwindet, solange es noch sichtbar ist, überprüfen Sie, ob die rechte Seite von.rect über die linke Seite des Bildschirms hinausgegangen ist. Sobald der Feind außerhalb des Bildschirms ist, rufen Sie.kill() auf, um zu verhindern, dass er weiter verarbeitet wird.

Was macht.kill()? Um dies herauszufinden, müssen Sie überSprite Groups Bescheid wissen.

Sprite-Gruppen

Eine weitere sehr nützliche Klasse, diepygame bietet, istSprite Group. Dies ist ein Objekt, das eine Gruppe vonSprite Objekten enthält. Warum also? Können Sie nicht stattdessen einfach die Objekte IhresSpritein einer Liste verfolgen? Nun, Sie können, aber der Vorteil der Verwendung vonGroup liegt in den Methoden, die es verfügbar macht. Diese Methoden helfen zu erkennen, obEnemy mitPlayer kollidiert ist, was Aktualisierungen erheblich erleichtert.

Mal sehen, wie man Sprite-Gruppen erstellt. Sie erstellen zwei verschiedeneGroup-Objekte:

  1. Die erstenGroup enthalten alleSprite im Spiel.

  2. Das zweiteGroup enthält nur dieEnemy-Objekte.

So sieht das im Code aus:

82 # Create the 'player'
83 player = Player()
84
85 # Create groups to hold enemy sprites and all sprites
86 # - enemies is used for collision detection and position updates
87 # - all_sprites is used for rendering
88 enemies = pygame.sprite.Group()
89 all_sprites = pygame.sprite.Group()
90 all_sprites.add(player)
91
92 # Variable to keep the main loop running
93 running = True

Wenn Sie.kill() aufrufen, wirdSprite von jedemGroup entfernt, zu dem es gehört. Dadurch werden auch die Verweise aufSprite entfernt, sodass der Garbage Collector von Python den Speicher nach Bedarf zurückfordern kann.

Nachdem Sie eineall_sprites-Gruppe haben, können Sie ändern, wie Objekte gezeichnet werden. Anstatt.blit() nur fürPlayer aufzurufen, können Sie alles inall_sprites durchlaufen:

117 # Fill the screen with black
118 screen.fill((0, 0, 0))
119
120 # Draw all sprites
121 for entity in all_sprites:
122     screen.blit(entity.surf, entity.rect)
123
124 # Flip everything to the display
125 pygame.display.flip()

Jetzt wird alles, was inall_sprites eingegeben wird, mit jedem Frame gezeichnet, egal ob es sich um einen Feind oder den Spieler handelt.

Es gibt nur ein Problem ... Sie haben keine Feinde! Sie könnten zu Beginn des Spiels eine Reihe von Feinden erschaffen, aber das Spiel würde schnell langweilig werden, wenn sie alle einige Sekunden später den Bildschirm verlassen würden. Lassen Sie uns stattdessen untersuchen, wie Sie im Verlauf des Spiels einen stetigen Vorrat an Feinden aufrechterhalten können.

Benutzerdefinierte Ereignisse

Das Design fordert, dass Feinde in regelmäßigen Abständen erscheinen. Dies bedeutet, dass Sie in festgelegten Intervallen zwei Dinge tun müssen:

  1. Erstellen Sie ein neuesEnemy.

  2. Addiere es zuall_sprites undenemies.

Sie haben bereits Code, der zufällige Ereignisse behandelt. Die Ereignisschleife ist so konzipiert, dass sie nach zufälligen Ereignissen sucht, die in jedem Frame auftreten, und diese entsprechend behandelt. Glücklicherweise beschränktpygame Sie nicht darauf, nur die von ihm definierten Ereignistypen zu verwenden. Sie können Ihre eigenen Ereignisse definieren, die Sie nach Belieben behandeln möchten.

Lassen Sie uns sehen, wie Sie ein benutzerdefiniertes Ereignis erstellen, das alle paar Sekunden generiert wird. Sie können ein benutzerdefiniertes Ereignis erstellen, indem Sie es benennen:

78 # Create the screen object
79 # The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
80 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
81
82 # Create a custom event for adding a new enemy
83 ADDENEMY = pygame.USEREVENT + 1
84 pygame.time.set_timer(ADDENEMY, 250)
85
86 # Instantiate player. Right now, this is just a rectangle.
87 player = Player()

pygame definiert Ereignisse intern als Ganzzahlen, daher müssen Sie ein neues Ereignis mit einer eindeutigen Ganzzahl definieren. Das letzte Ereignispygame Reserven heißtUSEREVENT. Wenn Sie alsoADDENEMY = pygame.USEREVENT + 1 in Zeile 83 definieren, wird sichergestellt, dass es eindeutig ist.

Als nächstes müssen Sie dieses neue Ereignis während des Spiels in regelmäßigen Abständen in die Ereigniswarteschlange einfügen. Hier kommt dastime-Modul ins Spiel. Zeile 84 löst das neueADDENEMY-Ereignis alle 250 Millisekunden oder viermal pro Sekunde aus. Sie rufen.set_timer() außerhalb der Spielschleife auf, da Sie nur einen Timer benötigen, der jedoch während des gesamten Spiels ausgelöst wird.

Fügen Sie den Code hinzu, um Ihr neues Ereignis zu behandeln:

100 # Main loop
101 while running:
102     # Look at every event in the queue
103     for event in pygame.event.get():
104         # Did the user hit a key?
105         if event.type == KEYDOWN:
106             # Was it the Escape key? If so, stop the loop.
107             if event.key == K_ESCAPE:
108                 running = False
109
110         # Did the user click the window close button? If so, stop the loop.
111         elif event.type == QUIT:
112             running = False
113
114         # Add a new enemy?
115         elif event.type == ADDENEMY:
116             # Create the new enemy and add it to sprite groups
117             new_enemy = Enemy()
118             enemies.add(new_enemy)
119             all_sprites.add(new_enemy)
120
121     # Get the set of keys pressed and check for user input
122     pressed_keys = pygame.key.get_pressed()
123     player.update(pressed_keys)
124
125     # Update enemy position
126     enemies.update()

Immer wenn der Ereignishandler das neueADDENEMY-Ereignis in Zeile 115 sieht, erstellt er einEnemy und addiert es zuenemies undall_sprites. DaEnemy inall_sprites ist, wird es in jedem Frame gezeichnet. Sie müssen auchenemies.update() in Zeile 126 aufrufen, wodurch alles inenemies aktualisiert wird, um sicherzustellen, dass sie sich ordnungsgemäß bewegen:

Enemies flying by in pygame

Dies ist jedoch nicht der einzige Grund, warum es eine Gruppe für nurenemiesgibt.

Kollisionserkennung

Ihr Spieldesign fordert, dass das Spiel endet, wenn ein Feind mit dem Spieler kollidiert. Das Überprüfen auf Kollisionen ist eine grundlegende Technik der Spielprogrammierung und erfordert normalerweise eine nicht triviale Mathematik, um festzustellen, ob sich zwei Sprites überlappen.

Hier bietet sich ein Framework wiepygame an! Das Schreiben von Kollisionserkennungscode ist mühsam, aberpygame verfügt über eine Mengecollision detection methods, die Sie verwenden können.

In diesem Lernprogramm verwenden Sie eine Methode namens.spritecollideany(), die als "Sprite kollidieren beliebig" gelesen wird. Diese Methode akzeptiert einSprite und einGroup als Parameter. Es betrachtet jedes Objekt inGroup und prüft, ob sich seine.rect mit den.rect derSprite überschneiden. Wenn ja, gibt esTrue zurück. Andernfalls wirdFalse zurückgegeben. Dies ist perfekt für dieses Spiel, da Sie überprüfen müssen, ob die einzelnenplayer mit einem vonGroup vonenemies kollidieren.

So sieht das im Code aus:

130 # Draw all sprites
131 for entity in all_sprites:
132     screen.blit(entity.surf, entity.rect)
133
134 # Check if any enemies have collided with the player
135 if pygame.sprite.spritecollideany(player, enemies):
136     # If so, then remove the player and stop the loop
137     player.kill()
138     running = False

Zeile 135 testet, obplayer mit einem der Objekte inenemies kollidiert ist. Wenn ja, wirdplayer.kill() aufgerufen, um es aus jeder Gruppe zu entfernen, zu der es gehört. Da nurall_sprites gerendert werden, werdenplayer nicht mehr gerendert. Sobald der Spieler getötet wurde, müssen Sie das Spiel ebenfalls beenden, also setzen Sierunning = False, um aus der Spielschleife in Zeile 138 auszubrechen.

Zu diesem Zeitpunkt sind die Grundelemente eines Spiels vorhanden:

Pygame window

Lassen Sie es uns jetzt ein wenig verschönern, spielbarer machen und einige erweiterte Funktionen hinzufügen, um es hervorzuheben.

Sprite-Bilder

Okay, du hast ein Spiel, aber seien wir ehrlich ... Es ist irgendwie hässlich. Der Spieler und die Feinde sind nur weiße Blöcke auf einem schwarzen Hintergrund. Das war Stand der Technik, alsPong neu war, aber es schneidet es einfach nicht mehr. Ersetzen wir all diese langweiligen weißen Rechtecke durch coolere Bilder, damit sich das Spiel wie ein echtes Spiel anfühlt.

Zuvor haben Sie gelernt, dass Bilder auf der Festplatte mit Hilfe des Modulsimage inSurface geladen werden können. Für dieses Tutorial haben wir einen kleinen Jet für den Spieler und einige Raketen für die Feinde gemacht. Sie können diese Grafik gerne verwenden, Ihre eigenen zeichnen oder einigefree game art assetsherunterladen, um sie zu verwenden. Sie können auf den folgenden Link klicken, um die in diesem Tutorial verwendete Grafik herunterzuladen:

Sample Code:Click here to download the source code for the PyGame sample project wird in diesem Tutorial verwendet.

Ändern der Objektkonstruktoren

Bevor Sie Bilder zur Darstellung des Spielers und der feindlichen Sprites verwenden, müssen Sie einige Änderungen an deren Konstruktoren vornehmen. Der folgende Code ersetzt den zuvor verwendeten Code:

 7 # Import pygame.locals for easier access to key coordinates
 8 # Updated to conform to flake8 and black standards
 9 # from pygame.locals import *
10 from pygame.locals import (
11     RLEACCEL,
12     K_UP,
13     K_DOWN,
14     K_LEFT,
15     K_RIGHT,
16     K_ESCAPE,
17     KEYDOWN,
18     QUIT,
19 )
20
21 # Define constants for the screen width and height
22 SCREEN_WIDTH = 800
23 SCREEN_HEIGHT = 600
24
25
26 # Define the Player object by extending pygame.sprite.Sprite
27 # Instead of a surface, use an image for a better-looking sprite
28 class Player(pygame.sprite.Sprite):
29     def __init__(self):
30         super(Player, self).__init__()
31         self.image = pygame.image.load("jet.png").convert()
32         self.image.set_colorkey((255, 255, 255), RLEACCEL)
33         self.rect = self.image.get_rect()

Packen wir Zeile 31 etwas aus. pygame.image.load() lädt ein Image von der Festplatte. Sie übergeben ihm einen Pfad zur Datei. Es gibt einSurface zurück, und der.convert()-Aufruf optimiert dasSurface, wodurch zukünftige.blit()-Anrufe schneller werden.

Zeile 32 verwendet.set_colorkey(), um anzuzeigen, dass die Farbepygame als transparent dargestellt wird. In diesem Fall wählen Sie Weiß, da dies die Hintergrundfarbe des Jet-Bildes ist. DieRLEACCEL-Konstante ist ein optionaler Parameter, mit dempygame auf nicht beschleunigten Anzeigen schneller gerendert werden kann. Dies wird der Importanweisung vonpygame.localsin Zeile 11 hinzugefügt.

Sonst muss sich nichts ändern. Das Bild ist immer noch einSurface, außer dass jetzt ein Bild darauf gemalt ist. Sie verwenden es immer noch auf die gleiche Weise.

So sehen ähnliche Änderungen anEnemyaus:

59 # Define the enemy object by extending pygame.sprite.Sprite
60 # Instead of a surface, use an image for a better-looking sprite
61 class Enemy(pygame.sprite.Sprite):
62     def __init__(self):
63         super(Enemy, self).__init__()
64         self.surf = pygame.image.load("missile.png").convert()
65         self.surf.set_colorkey((255, 255, 255), RLEACCEL)
66         # The starting position is randomly generated, as is the speed
67         self.rect = self.surf.get_rect(
68             center=(
69                 random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
70                 random.randint(0, SCREEN_HEIGHT),
71             )
72         )
73         self.speed = random.randint(5, 20)

Das Ausführen des Programms sollte jetzt zeigen, dass dies das gleiche Spiel ist, das Sie zuvor hatten, außer dass Sie jetzt einige nette Grafikenskins mit Bildern hinzugefügt haben. Aber warum aufhören, nur den Spieler und die feindlichen Sprites schön aussehen zu lassen? Fügen wir ein paar Wolken hinzu, die vorbeiziehen, um den Eindruck eines Jets zu erwecken, der durch den Himmel fliegt.

Hintergrundbilder hinzufügen

Für Hintergrundwolken verwenden Sie dieselben Prinzipien wie fürPlayer undEnemy:

  1. Erstellen Sie die KlasseCloud.

  2. Fügen Sie ein Bild einer Wolke hinzu.

  3. Erstellen Sie eine Methode.update(), diecloud zur linken Seite des Bildschirms verschiebt.

  4. Erstellen Sie ein benutzerdefiniertes Ereignis und einen Handler, um in einem festgelegten Zeitintervall neuecloud-Objekte zu erstellen.

  5. Fügen Sie die neu erstelltencloud-Objekte zu einem neuenGroup mit dem Namenclouds hinzu.

  6. Aktualisiere und zeichne dieclouds in deiner Spielschleife.

So siehtCloudaus:

 83 # Define the cloud object by extending pygame.sprite.Sprite
 84 # Use an image for a better-looking sprite
 85 class Cloud(pygame.sprite.Sprite):
 86     def __init__(self):
 87         super(Cloud, self).__init__()
 88         self.surf = pygame.image.load("cloud.png").convert()
 89         self.surf.set_colorkey((0, 0, 0), RLEACCEL)
 90         # The starting position is randomly generated
 91         self.rect = self.surf.get_rect(
 92             center=(
 93                 random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
 94                 random.randint(0, SCREEN_HEIGHT),
 95             )
 96
 97     # Move the cloud based on a constant speed
 98     # Remove the cloud when it passes the left edge of the screen
 99     def update(self):
100         self.rect.move_ip(-5, 0)
101         if self.rect.right < 0:
102             self.kill()

Das sollte alles sehr vertraut aussehen. Es ist so ziemlich das gleiche wieEnemy.

Damit in bestimmten Intervallen Wolken angezeigt werden, verwenden Sie einen Code zur Ereigniserstellung, der dem Code ähnelt, mit dem Sie neue Feinde erstellt haben. Platziere es direkt unter dem feindlichen Schöpfungsereignis:

116 # Create custom events for adding a new enemy and a cloud
117 ADDENEMY = pygame.USEREVENT + 1
118 pygame.time.set_timer(ADDENEMY, 250)
119 ADDCLOUD = pygame.USEREVENT + 2
120 pygame.time.set_timer(ADDCLOUD, 1000)

Dies bedeutet, dass Sie 1000 Millisekunden oder eine Sekunde warten müssen, bevor Sie die nächstencloud erstellen.

Erstellen Sie als Nächstes ein neuesGroup, um jedes neu erstelltecloud aufzunehmen:

125 # Create groups to hold enemy sprites, cloud sprites, and all sprites
126 # - enemies is used for collision detection and position updates
127 # - clouds is used for position updates
128 # - all_sprites is used for rendering
129 enemies = pygame.sprite.Group()
130 clouds = pygame.sprite.Group()
131 all_sprites = pygame.sprite.Group()
132 all_sprites.add(player)

Fügen Sie als Nächstes einen Handler für das neueADDCLOUD-Ereignis im Ereignishandler hinzu:

137 # Main loop
138 while running:
139     # Look at every event in the queue
140     for event in pygame.event.get():
141         # Did the user hit a key?
142         if event.type == KEYDOWN:
143             # Was it the Escape key? If so, then stop the loop.
144             if event.key == K_ESCAPE:
145                 running = False
146
147         # Did the user click the window close button? If so, stop the loop.
148         elif event.type == QUIT:
149             running = False
150
151         # Add a new enemy?
152         elif event.type == ADDENEMY:
153             # Create the new enemy and add it to sprite groups
154             new_enemy = Enemy()
155             enemies.add(new_enemy)
156             all_sprites.add(new_enemy)
157
158         # Add a new cloud?
159         elif event.type == ADDCLOUD:
160             # Create the new cloud and add it to sprite groups
161             new_cloud = Cloud()
162             clouds.add(new_cloud)
163             all_sprites.add(new_cloud)

Stellen Sie schließlich sicher, dass dieclouds in jedem Frame aktualisiert werden:

167 # Update the position of enemies and clouds
168 enemies.update()
169 clouds.update()
170
171 # Fill the screen with sky blue
172 screen.fill((135, 206, 250))

Zeile 172 aktualisiert die ursprünglichenscreen.fill(), um den Bildschirm mit einer angenehmen himmelblauen Farbe zu füllen. Sie können diese Farbe in etwas anderes ändern. Vielleicht möchten Sie eine fremde Welt mit einem lila Himmel, einem giftigen Ödland in Neongrün oder der Marsoberfläche in Rot!

Beachten Sie, dass alle neuenCloud undEnemy zuall_sprites sowieclouds undenemies addiert werden. Dies geschieht, weil jede Gruppe für einen separaten Zweck verwendet wird:

  • Rendering wird mitall_sprites durchgeführt.

  • Position updates werden mitclouds undenemies durchgeführt.

  • Collision detection wird mitenemies durchgeführt.

Sie erstellen mehrere Gruppen, damit Sie die Art und Weise ändern können, wie sich Sprites bewegen oder verhalten, ohne die Bewegung oder das Verhalten anderer Sprites zu beeinflussen.

Spielgeschwindigkeit

Beim Testen des Spiels haben Sie möglicherweise bemerkt, dass sich die Feinde etwas schnell bewegen. Wenn nicht, ist das in Ordnung, da verschiedene Maschinen zu diesem Zeitpunkt unterschiedliche Ergebnisse sehen.

Der Grund dafür ist, dass die Spielschleife Frames so schnell verarbeitet, wie es der Prozessor und die Umgebung zulassen. Da sich alle Sprites einmal pro Frame bewegen, können sie sich hunderte Male pro Sekunde bewegen. Die Anzahl der Frames, die pro Sekunde verarbeitet werden, wird alsframe rate bezeichnet. Wenn Sie dies richtig machen, ist dies der Unterschied zwischen einem spielbaren und einem unvergesslichen Spiel.

Normalerweise möchten Sie eine möglichst hohe Bildrate, aber für dieses Spiel müssen Sie sie etwas verlangsamen, damit das Spiel spielbar ist. Glücklicherweise enthält das Modultime einClock, das genau für diesen Zweck ausgelegt ist.

Die Verwendung vonClock zum Festlegen einer abspielbaren Bildrate erfordert nur zwei Codezeilen. Der erste erstellt ein neuesClock, bevor die Spielschleife beginnt:

106 # Setup the clock for a decent framerate
107 clock = pygame.time.Clock()

Der zweite Aufruf.tick(), umpygame darüber zu informieren, dass das Programm das Ende des Frames erreicht hat:

188 # Flip everything to the display
189 pygame.display.flip()
190
191 # Ensure program maintains a rate of 30 frames per second
192 clock.tick(30)

Das an.tick() übergebene Argument legt die gewünschte Bildrate fest. Zu diesem Zweck berechnet.tick() die Anzahl der Millisekunden, die jeder Frame benötigen sollte, basierend auf der gewünschten Framerate. Anschließend wird diese Zahl mit der Anzahl der Millisekunden verglichen, die seit dem letzten Aufruf von.tick() vergangen sind. Wenn nicht genügend Zeit vergangen ist, verzögert.tick() die Verarbeitung, um sicherzustellen, dass die angegebene Bildrate nie überschritten wird.

Wenn Sie eine kleinere Framerate eingeben, bleibt in jedem Frame mehr Zeit für Berechnungen, während eine größere Framerate für ein flüssigeres (und möglicherweise schnelleres) Gameplay sorgt:

Setting the frame rate in pygame

Spielen Sie mit dieser Nummer herum, um zu sehen, was sich für Sie am besten anfühlt!

Soundeffekte

Bisher haben Sie sich auf das Gameplay und die visuellen Aspekte Ihres Spiels konzentriert. Lassen Sie uns nun untersuchen, wie Sie Ihrem Spiel auch etwas Hörgeschmack verleihen können. pygame bietetmixer für alle klangbezogenen Aktivitäten. Sie verwenden die Klassen und Methoden dieses Moduls, um Hintergrundmusik und Soundeffekte für verschiedene Aktionen bereitzustellen.

Der Namemixer bezieht sich auf die Tatsache, dass das Modul verschiedene Klänge zu einem zusammenhängenden Ganzen mischt. Mit dem Untermodulmusic können Sie einzelne Audiodateien in einer Vielzahl von Formaten streamen, z. B.MP3,Ogg undMod. Sie können auchSound verwenden, um einen einzelnen abzuspielenden Soundeffekt in den Formaten Ogg oderuncompressed WAVzu halten. Die gesamte Wiedergabe erfolgt im Hintergrund. Wenn Sie alsoSound abspielen, kehrt die Methode sofort zurück, während der Sound abgespielt wird.

Note:pygame documentation gibt an, dass die MP3-Unterstützung begrenzt ist und nicht unterstützte Formate zu Systemabstürzen führen können. Die Sounds, auf die in diesem Artikel verwiesen wird, wurden getestet. Wir empfehlen, alle Sounds gründlich zu testen, bevor Sie Ihr Spiel veröffentlichen.

Wie bei den meisten Dingenpygame beginnt die Verwendung vonmixer mit einem Initialisierungsschritt. Zum Glück wird dies bereits vonpygame.init() erledigt. Sie müssenpygame.mixer.init() nur aufrufen, wenn Sie die Standardeinstellungen ändern möchten:

106 # Setup for sounds. Defaults are good.
107 pygame.mixer.init()
108
109 # Initialize pygame
110 pygame.init()
111
112 # Set up the clock for a decent framerate
113 clock = pygame.time.Clock()

pygame.mixer.init() akzeptierta number of arguments, aber die Standardeinstellungen funktionieren in den meisten Fällen einwandfrei. Beachten Sie, dass Siepygame.mixer.init() aufrufen müssen, bevor Siepygame.init() aufrufen möchten, wenn Sie die Standardeinstellungen ändern möchten. Andernfalls bleiben die Standardeinstellungen unabhängig von Ihren Änderungen wirksam.

Nach der Initialisierung des Systems können Sie Ihre Sounds und Hintergrundmusik einrichten:

135 # Load and play background music
136 # Sound source: http://ccmixter.org/files/Apoxode/59262
137 # License: https://creativecommons.org/licenses/by/3.0/
138 pygame.mixer.music.load("Apoxode_-_Electric_1.mp3")
139 pygame.mixer.music.play(loops=-1)
140
141 # Load all sound files
142 # Sound sources: Jon Fincher
143 move_up_sound = pygame.mixer.Sound("Rising_putter.ogg")
144 move_down_sound = pygame.mixer.Sound("Falling_putter.ogg")
145 collision_sound = pygame.mixer.Sound("Collision.ogg")

Lines 138 and 139 lädt einen Hintergrundsoundclip und beginnt mit der Wiedergabe. Sie können den Soundclip anweisen, sich zu wiederholen und niemals zu beenden, indem Sie den genannten Parameterloops=-1 einstellen.

Lines 143 to 145 laden drei Sounds, die Sie für verschiedene Soundeffekte verwenden. Die ersten beiden sind steigende und fallende Geräusche, die gespielt werden, wenn sich der Spieler nach oben oder unten bewegt. Der letzte ist der Ton, der bei einer Kollision verwendet wird. Sie können auch andere Sounds hinzufügen, z. B. einen Sound, wenn einEnemy erstellt wird, oder einen endgültigen Sound, wenn das Spiel endet.

Wie verwenden Sie die Soundeffekte? Sie möchten jeden Sound abspielen, wenn ein bestimmtes Ereignis eintritt. Wenn sich das Schiff beispielsweise nach oben bewegt, möchten Siemove_up_sound spielen. Daher fügen Sie.play() einen Aufruf hinzu, wenn Sie dieses Ereignis behandeln. Im Entwurf bedeutet dies, dass.update() fürPlayer die folgenden Aufrufe hinzugefügt werden:

26 # Define the Player object by extending pygame.sprite.Sprite
27 # Instead of a surface, use an image for a better-looking sprite
28 class Player(pygame.sprite.Sprite):
29     def __init__(self):
30         super(Player, self).__init__()
31         self.surf = pygame.image.load("jet.png").convert()
32         self.surf.set_colorkey((255, 255, 255), RLEACCEL)
33         self.rect = self.surf.get_rect()
34
35     # Move the sprite based on keypresses
36     def update(self, pressed_keys):
37         if pressed_keys[K_UP]:
38             self.rect.move_ip(0, -5)
39             move_up_sound.play()
40         if pressed_keys[K_DOWN]:
41             self.rect.move_ip(0, 5)
42             move_down_sound.play()

Bei einer Kollision zwischen dem Spieler und einem Feind spielen Sie den Ton ab, wenn Kollisionen erkannt werden:

201 # Check if any enemies have collided with the player
202 if pygame.sprite.spritecollideany(player, enemies):
203     # If so, then remove the player
204     player.kill()
205
206     # Stop any moving sounds and play the collision sound
207     move_up_sound.stop()
208     move_down_sound.stop()
209     collision_sound.play()
210
211     # Stop the loop
212     running = False

Hier stoppen Sie zuerst alle anderen Soundeffekte, da sich der Player bei einer Kollision nicht mehr bewegt. Dann spielen Sie den Kollisionssound ab und setzen die Ausführung von dort aus fort.

Wenn das Spiel vorbei ist, sollten alle Sounds aufhören. Dies gilt unabhängig davon, ob das Spiel aufgrund einer Kollision endet oder der Benutzer manuell beendet wird. Fügen Sie dazu am Ende des Programms nach der Schleife die folgenden Zeilen hinzu:

220 # All done! Stop and quit the mixer.
221 pygame.mixer.music.stop()
222 pygame.mixer.quit()

Technisch gesehen sind diese letzten Zeilen nicht erforderlich, da das Programm direkt danach endet. Wenn Sie sich jedoch später dazu entschließen, Ihrem Spiel einen Intro- oder einen Exit-Bildschirm hinzuzufügen, wird nach dem Ende des Spiels möglicherweise mehr Code ausgeführt.

Das ist es! Testen Sie es erneut, und Sie sollten Folgendes sehen:

Pygame window

Ein Hinweis zu Quellen

Möglicherweise haben Sie den Kommentar in den Zeilen 136-137 beim Laden der Hintergrundmusik bemerkt, in dem die Quelle der Musik und ein Link zur Creative Commons-Lizenz aufgeführt sind. Dies geschah, weil der Schöpfer dieses Sounds es benötigte. In den Lizenzanforderungen wurde festgelegt, dass zur Verwendung des Sounds sowohl eine ordnungsgemäße Zuordnung als auch ein Link zur Lizenz bereitgestellt werden müssen.

Hier sind einige Quellen für Musik, Sound und Kunst, nach denen Sie nach nützlichen Inhalten suchen können:

Stellen Sie beim Erstellen Ihrer Spiele und Verwenden von heruntergeladenen Inhalten wie Kunst, Musik oder Code aus anderen Quellen sicher, dass Sie die Lizenzbestimmungen dieser Quellen einhalten.

Fazit

In diesem Tutorial haben Sie gelernt, wie sich die Spielprogrammierung mitpygame von der Standardprozedur unterscheidet. Sie haben auch gelernt, wie man:

  • Implementieren Sie Ereignisschleifen

  • Zeichnen Sie Elemente auf dem Bildschirm

  • Spielen Sie Soundeffekte und Musik ab

  • Benutzereingaben verarbeiten

Zu diesem Zweck haben Sie eine Teilmenge der Modulepygame verwendet, einschließlichdisplay,mixer undmusic,time,image,event undkey Module. Sie haben auch mehrerepygame-Klassen verwendet, einschließlichRect,Surface,Sound undSprite. Aber diese kratzen nur an der Oberfläche dessen, waspygame kann! Inofficial pygame documentation finden Sie eine vollständige Liste der verfügbaren Module und Klassen.

Sie finden alle Code-, Grafik- und Sounddateien für diesen Artikel, indem Sie auf den folgenden Link klicken:

Clone Repo:Click here to clone the repo you’ll use, um in diesem Tutorial zu lernen, wie man PyGame verwendet.

Fühlen Sie sich frei, auch unten Kommentare zu hinterlassen. Viel Spaß beim Pythoning!