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 undpygame
bedeutet, 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:
Lassen Sie uns diesen Code abschnittsweise aufschlüsseln:
-
Lines 4 and 5 importiert und initialisiert die
pygame
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 nur
pygame.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ässt
pygame
. 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 inSurface
laden 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:
-
Verarbeitet Benutzereingaben
-
Aktualisiert den Status aller Spielobjekte
-
Aktualisiert die Anzeige und die Audioausgabe
-
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:
-
Der Spieler kollidiert mit einem Hindernis. (Sie werden sich später mit der Kollisionserkennung befassen.)
-
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 EreignistypKEYDOWN
auch 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 Sie
running = 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 aktuelle
event.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 Ereignistyp
QUIT
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:
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:
-
screen.fill()
, um den Hintergrund zu füllen -
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:
-
Die zu zeichnenden
Surface
-
Der Ort, an dem es auf die Quelle
Surface
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:
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:
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:
Möglicherweise stellen Sie zwei kleine Probleme fest:
-
Das Spielerrechteck kann sich sehr schnell bewegen, wenn eine Taste gedrückt gehalten wird. Daran werden Sie später arbeiten.
-
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
:
-
On lines 62 to 67 aktualisieren Sie
rect
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. -
On line 68 definieren Sie
.speed
als Zufallszahl zwischen 5 und 20. Dies gibt an, wie schnell sich dieser Feind auf den Spieler zubewegt. -
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. -
On line 74 überprüfen Sie, ob sich der Feind vom Bildschirm entfernt hat. Um sicherzustellen, dass
Enemy
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 IhresSprite
in 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:
-
Die ersten
Group
enthalten alleSprite
im Spiel. -
Das zweite
Group
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:
-
Erstellen Sie ein neues
Enemy
. -
Addiere es zu
all_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:
Dies ist jedoch nicht der einzige Grund, warum es eine Gruppe für nurenemies
gibt.
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:
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.locals
in 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 anEnemy
aus:
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
:
-
Erstellen Sie die Klasse
Cloud
. -
Fügen Sie ein Bild einer Wolke hinzu.
-
Erstellen Sie eine Methode
.update()
, diecloud
zur linken Seite des Bildschirms verschiebt. -
Erstellen Sie ein benutzerdefiniertes Ereignis und einen Handler, um in einem festgelegten Zeitintervall neue
cloud
-Objekte zu erstellen. -
Fügen Sie die neu erstellten
cloud
-Objekte zu einem neuenGroup
mit dem Namenclouds
hinzu. -
Aktualisiere und zeichne die
clouds
in deiner Spielschleife.
So siehtCloud
aus:
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 mit
all_sprites
durchgeführt. -
Position updates werden mit
clouds
undenemies
durchgeführt. -
Collision detection wird mit
enemies
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:
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:
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:
-
OpenGameArt.org: Sounds, Soundeffekte, Sprites und andere Grafiken
-
Kenney.nl: Sounds, Soundeffekte, Sprites und andere Grafiken
-
Gamer Art 2D: Sprites und andere Grafiken
-
CC Mixter: Sounds und Soundeffekte
-
Freesound: Sounds und Soundeffekte
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!