Erstelle ein Aliens-Spiel mit Pygame

PythonPythonBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Projekt werden wir Ihnen den Prozess des Erstellens eines einfachen Spiels namens "Aliens" mit der Pygame-Bibliothek beibringen. Beim Spiel geht es darum, fremde Einfaller abzuwehren, indem man sie niederschießt. Wir werden den Entwicklungsprozess in mehrere Schritte unterteilen, von der Einrichtung der Projektdateien bis zum Ausführen des fertigen Spiels.

Pygame ist eine beliebte Bibliothek für die Erstellung von 2D-Spielen in Python. Sie bietet Funktionen zur Handhabung von Grafiken, Sound und Benutzereingaben, was sie zu einer ausgezeichneten Wahl für Einsteiger, die an der Spielentwicklung interessiert sind, macht.

👀 Vorschau

Aliens Game

Dieses Spiel ist von den Pygame-Beispielen revidiert.

🎯 Aufgaben

In diesem Projekt lernen Sie:

  • Wie Sie die initiale Projektstruktur einrichten und notwendige Ressourcen wie Bilder und Sounds laden.
  • Wie Sie die Klassen für den Spielercharakter und die fremden Einfaller definieren.
  • Wie Sie zusätzliche Klassen zur Handhabung von Explosionen, Spieler-Schüssen, fremden Bomben und Spielpunkten erstellen.
  • Wie Sie das Spiel initialisieren, Ressourcen laden und das Spielfenster einrichten.
  • Wie Sie die Hauptspielschleife implementieren, Benutzereingaben verarbeiten, die Spielentitäten aktualisieren, Kollisionen behandeln und die Spielszene zeichnen.

🏆 Errungenschaften

Nach Abschluss dieses Projekts werden Sie in der Lage sein:

  • Die Pygame-Bibliothek zur Entwicklung eines 2D-Spiels zu verwenden.
  • Bilder in Pygame zu laden und anzuzeigen.
  • Benutzereingaben zu verarbeiten und die Bewegung des Spielers zu steuern.
  • Spielentitäten mithilfe von Sprite-Klassen zu erstellen und zu aktualisieren.
  • Kollisionen zwischen Spielentitäten zu behandeln.
  • Die Spielszene zu zeichnen und den Bildschirm zu aktualisieren.
  • Soundeffekte und Musik im Spiel abzuspielen.
  • Eine Hauptspielschleife zu implementieren, um die Spiellogik zu verwalten.

Erstellen der Projektdateien

In diesem Schritt werden wir die initiale Projektstruktur einrichten und notwendige Ressourcen wie Bilder und Sounds laden.

Erstellen Sie eine neue Datei namens aliens.py und fügen Sie den folgenden Code hinzu:

cd ~/project
touch aliens.py
sudo pip install pygame

Öffnen Sie dann die Datei in Ihrem bevorzugten Texteditor und fügen Sie den folgenden Code hinzu:

import os
import random
from typing import List

## import basic pygame modules
import pygame as pg

## überprüfen, ob wir mehr als die Standard-BMP laden können
if not pg.image.get_extended():
    raise SystemExit("Sorry, extended image module required")


## Spielkonstanten
MAX_SHOTS = 2  ## maximale Anzahl von Spieler-Bullets auf dem Bildschirm
ALIEN_ODDS = 22  ## Wahrscheinlichkeit, dass ein neuer Alien erscheint
BOMB_ODDS = 60  ## Wahrscheinlichkeit, dass eine neue Bombe fällt
ALIEN_RELOAD = 12  ## Frames zwischen neuen Aliens
SCREENRECT = pg.Rect(0, 0, 640, 480)
SCORE = 0

main_dir = os.path.split(os.path.abspath(__file__))[0]


def load_image(file):
    """lädt ein Bild und bereitet es für das Spiel vor"""
    file = os.path.join(main_dir, "data", file)
    try:
        surface = pg.image.load(file)
    except pg.error:
        raise SystemExit(f'Could not load image "{file}" {pg.get_error()}')
    return surface.convert()


def load_sound(file):
    """weil pygame ohne Mixer kompiliert werden kann."""
    if not pg.mixer:
        return None
    file = os.path.join(main_dir, "data", file)
    try:
        sound = pg.mixer.Sound(file)
        return sound
    except pg.error:
        print(f"Warning, unable to load, {file}")
    return None

In diesem Schritt importieren wir die notwendigen Module, definieren Spielkonstanten und erstellen Funktionen zum Laden von Bildern und Soundeffekten. Wir legen auch das Hauptprojektverzeichnis fest.

In def load_image und def load_sound verwenden wir die os.path.join-Funktion, um das Hauptprojektverzeichnis mit dem data-Verzeichnis zu verbinden, in dem wir unsere Spielressourcen speichern werden. Wir werden dieses Verzeichnis im nächsten Schritt erstellen.

✨ Lösung prüfen und üben

Definieren von Spieler- und Alien-Klassen

Nun werden wir die Klassen für die Charaktere unseres Spiels definieren: Player und Alien. Die Player-Klasse repräsentiert den Spielercharakter, während die Alien-Klasse die fremden Einfaller repräsentiert.

#... (Code aus Schritt 1)

## Definiere die Player-Klasse
class Player(pg.sprite.Sprite):
    """Repräsentiert den Spieler als einen Mondbuggy-ähnlichen Wagen."""

    speed = 10
    bounce = 24
    gun_offset = -11
    images: List[pg.Surface] = []

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom)
        self.reloading = 0
        self.origtop = self.rect.top
        self.facing = -1

    def move(self, direction):
        if direction:
            self.facing = direction
        self.rect.move_ip(direction * self.speed, 0)
        self.rect = self.rect.clamp(SCREENRECT)
        if direction < 0:
            self.image = self.images[0]
        elif direction > 0:
            self.image = self.images[1]
        self.rect.top = self.origtop - (self.rect.left // self.bounce % 2)

    def gunpos(self):
        pos = self.facing * self.gun_offset + self.rect.centerx
        return pos, self.rect.top

Hier haben wir die Player-Klasse definiert. Sie enthält Attribute wie Geschwindigkeit, Bounce und Gun Offset. Die move-Methode behandelt die Spielerbewegung. Die gunpos-Methode gibt die Position der Waffe des Spielers zurück. Die Player-Klasse erbt von der pg.sprite.Sprite-Klasse, die eine Basisklasse für sichtbare Spielobjekte ist.

## Definiere die Alien-Klasse
class Alien(pg.sprite.Sprite):
    """Ein fremdes Raumschiff. Das langsam den Bildschirm hinunter bewegt."""

    speed = 13
    animcycle = 12
    images: List[pg.Surface] = []

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.facing = random.choice((-1, 1)) * Alien.speed
        self.frame = 0
        if self.facing < 0:
            self.rect.right = SCREENRECT.right

    def update(self):
        self.rect.move_ip(self.facing, 0)
        if not SCREENRECT.contains(self.rect):
            self.facing = -self.facing
            self.rect.top = self.rect.bottom + 1
            self.rect = self.rect.clamp(SCREENRECT)
        self.frame = self.frame + 1
        self.image = self.images[self.frame // self.animcycle % 3]

Die Alien-Klasse repräsentiert die fremden Einfaller. Sie hat Attribute für Geschwindigkeit und Animation. Die Aliens bewegen sich nach links und rechts, und ihre Bilder wechseln sich ab. Die update-Methode behandelt die Alienbewegung und -animation. Die Alien-Klasse erbt von der pg.sprite.Sprite-Klasse, die eine Basisklasse für sichtbare Spielobjekte ist.

✨ Lösung prüfen und üben

Definieren zusätzlicher Spielklassen

In diesem Schritt werden wir zusätzliche spielbezogene Klassen definieren: Explosion, Shot, Bomb und Score. Diese Klassen behandeln Explosionen, Spieler-Schüsse, fremde Bomben und das Spielscore.

#... (Code aus Schritt 2)

## Definiere die Explosion-Klasse
class Explosion(pg.sprite.Sprite):
    """Eine Explosion. Hoffentlich die des Aliens und nicht des Spielers!"""

    defaultlife = 12
    animcycle = 3
    images: List[pg.Surface] = []

    def __init__(self, actor, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(center=actor.rect.center)
        self.life = self.defaultlife

    def update(self):
        """Wird jedes Mal in der Spielschleife aufgerufen.

        Zeigt die Explosionsfläche für 'defaultlife' an.
        Bei jedem Spiel-Tick (Update) verringern wir das 'life'.

        Wir animieren auch die Explosion.
        """
        self.life = self.life - 1
        self.image = self.images[self.life // self.animcycle % 2]
        if self.life <= 0:
            self.kill()

## Definiere die Shot-Klasse
class Shot(pg.sprite.Sprite):
    """Ein Geschoss, das der Player-Sprite abfeuert."""

    speed = -11
    images: List[pg.Surface] = []

    def __init__(self, pos, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=pos)

    def update(self):
        """Wird jedes Mal in der Spielschleife aufgerufen.

        Bei jedem Tick bewegen wir das Geschoss nach oben.
        """
        self.rect.move_ip(0, self.speed)
        if self.rect.top <= 0:
            self.kill()

## Definiere die Bomb-Klasse
class Bomb(pg.sprite.Sprite):
    """Eine Bombe, die die Aliens fallen lassen."""

    speed = 9
    images: List[pg.Surface] = []

    def __init__(self, alien, explosion_group, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=alien.rect.move(0, 5).midbottom)
        self.explosion_group = explosion_group

    def update(self):
        """Wird jedes Mal in der Spielschleife aufgerufen.

        Bei jedem Frame bewegen wir den Sprite-'rect' nach unten.
        Wenn es den unteren Rand erreicht, tun wir Folgendes:

        - Machen eine Explosion.
        - Entfernen die Bombe.
        """
        self.rect.move_ip(0, self.speed)
        if self.rect.bottom >= 470:
            Explosion(self, self.explosion_group)
            self.kill()

## Definiere die Score-Klasse
class Score(pg.sprite.Sprite):
    """Um den Score zu verfolgen."""

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.font = pg.font.Font(None, 20)
        self.font.set_italic(1)
        self.color = "white"
        self.lastscore = -1
        self.update()
        self.rect = self.image.get_rect().move(10, 450)

    def update(self):
        """Wir aktualisieren den Score nur in update(), wenn er sich geändert hat."""
        if SCORE!= self.lastscore:
            self.lastscore = SCORE
            msg = f"Score: {SCORE}"
            self.image = self.font.render(msg, 0, self.color)

Diese Klassen behandeln verschiedene Aspekte des Spiels, wie Explosionen, Schüsse, Bomben und die Anzeige des Spielerscores.

✨ Lösung prüfen und üben

Initialisiere das Spiel und lade die Ressourcen

Nun werden wir mit der Initialisierung des Spiels fortfahren und die erforderlichen Ressourcen wie Bilder und Sounds laden.

#... (Code aus Schritt 3)

def main(winstyle=0):
    ## Initialisiere pygame
    if pg.get_sdl_version()[0] == 2:
        pg.mixer.pre_init(44100, 32, 2, 1024)
    pg.init()
    if pg.mixer and not pg.mixer.get_init():
        print("Warning, no sound")
        pg.mixer = None

    fullscreen = False
    ## Setze den Anzeigemodus
    winstyle = 0  ## |FULLSCREEN
    bestdepth = pg.display.mode_ok(SCREENRECT.size, winstyle, 32)
    screen = pg.display.set_mode(SCREENRECT.size, winstyle, bestdepth)

    ## Lade Bilder, weise sie den Sprite-Klassen zu
    ## (mache dies vor der Verwendung der Klassen, nach der Bildschirm-Einrichtung)
    img = load_image("player1.gif")
    Player.images = [img, pg.transform.flip(img, 1, 0)]
    img = load_image("explosion1.gif")
    Explosion.images = [img, pg.transform.flip(img, 1, 1)]
    Alien.images = [load_image(im) for im in ("alien1.gif", "alien2.gif", "alien3.gif")]
    Bomb.images = [load_image("bomb.gif")]
    Shot.images = [load_image("shot.gif")]

Hier initialisieren wir Pygame, setzen das Spielfenster ein und laden Bilder für den Spieler, die Aliens, die Explosionen, die Bomben und die Schüsse.

✨ Lösung prüfen und üben

Vollständige Spielinstallation und Hauptschleife

Jetzt werden wir die Spielinstallation abschließen und die Hauptspielschleife erstellen, in der alle Spiellogik und Interaktionen stattfinden werden.

#... (Code aus Schritt 4)

def main(winstyle=0):
    #... (vorheriger Code)

    ## Dekoriere das Spielfenster
    icon = pg.transform.scale(Alien.images[0], (32, 32))
    pg.display.set_icon(icon)
    pg.display.set_caption("Pygame Aliens")
    pg.mouse.set_visible(0)

    ## Erstelle den Hintergrund, wiederhole das Hintergrundbild
    bgdtile = load_image("background.gif")
    background = pg.Surface(SCREENRECT.size)
    for x in range(0, SCREENRECT.width, bgdtile.get_width()):
        background.blit(bgdtile, (x, 0))
    screen.blit(background, (0, 0))
    pg.display.flip()

    ## Lade die Soundeffekte
    boom_sound = load_sound("boom.wav")
    shoot_sound = load_sound("car_door.wav")
    if pg.mixer:
        music = os.path.join(main_dir, "data", "house_lo.wav")
        pg.mixer.music.load(music)
        pg.mixer.music.play(-1)

    ## Initialisiere die Spielgruppen
    aliens = pg.sprite.Group()
    shots = pg.sprite.Group()
    bombs = pg.sprite.Group()
    all = pg.sprite.RenderUpdates()
    lastalien = pg.sprite.GroupSingle()

    ## Erstelle einige Startwerte
    alienreload = ALIEN_RELOAD
    clock = pg.time.Clock()

    ## Initialisiere unsere Startsprites
    global SCORE
    player = Player(all)
    Alien(
        aliens, all, lastalien
    )  ## beachte, dies 'lebt', weil es in eine Sprite-Gruppe geht
    if pg.font:
        all.add(Score(all))

    ## Führe unsere Hauptschleife aus, solange der Spieler lebt.
    while player.alive():
        ## Empfange die Eingabe
        for event in pg.event.get():
            if event.type == pg.QUIT:
                return
            if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
                return
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_f:
                    if not fullscreen:
                        print("Wechsel zu FULLSCREEN")
                        screen_backup = screen.copy()
                        screen = pg.display.set_mode(
                            SCREENRECT.size, winstyle | pg.FULLSCREEN, bestdepth
                        )
                        screen.blit(screen_backup, (0, 0))
                    else:
                        print("Wechsel zu Fenstermodus")
                        screen_backup = screen.copy()
                        screen = pg.display.set_mode(
                            SCREENRECT.size, winstyle, bestdepth
                        )
                        screen.blit(screen_backup, (0, 0))
                    pg.display.flip()
                    fullscreen = not fullscreen

        keystate = pg.key.get_pressed()

        ## Leere/Lösche die zuletzt gezeichneten Sprites
        all.clear(screen, background)

        ## Aktualisiere alle Sprites
        all.update()

        ## Verarbeite die Spieler-Eingabe
        direction = keystate[pg.K_RIGHT] - keystate[pg.K_LEFT]
        player.move(direction)
        firing = keystate[pg.K_SPACE]
        if not player.reloading and firing and len(shots) < MAX_SHOTS:
            Shot(player.gunpos(), shots, all)
            if pg.mixer and shoot_sound is not None:
                shoot_sound.play()
        player.reloading = firing

        ## Erstelle einen neuen Alien
        if alienreload:
            alienreload = alienreload - 1
        elif not int(random.random() * ALIEN_ODDS):
            Alien(aliens, all, lastalien)
            alienreload = ALIEN_RELOAD

        ## Lasse Bomben fallen
        if lastalien and not int(random.random() * BOMB_ODDS):
            Bomb(lastalien.sprite, all, bombs, all)

        ## Entdecke Kollisionen zwischen Aliens und Spielern.
        for alien in pg.sprite.spritecollide(player, aliens, 1):
            if pg.mixer and boom_sound is not None:
                boom_sound.play()
            Explosion(alien, all)
            Explosion(player, all)
            SCORE = SCORE + 1
            player.kill()

        ## Überprüfe, ob Schüsse die Aliens treffen.
        for alien in pg.sprite.groupcollide(aliens, shots, 1, 1).keys():
            if pg.mixer and boom_sound is not None:
                boom_sound.play()
            Explosion(alien, all)
            SCORE = SCORE + 1

        ## Überprüfe, ob fremde Bomben den Spieler treffen.
        for bomb in pg.sprite.spritecollide(player, bombs, 1):
            if pg.mixer and boom_sound is not None:
                boom_sound.play()
            Explosion(player, all)
            Explosion(bomb, all)
            player.kill()

        ## Zeichne die Szene
        dirty = all.draw(screen)
        pg.display.update(dirty)

        ## Begrenze die Bildwiederholrate auf 40fps. Auch 40HZ oder 40 Mal pro Sekunde genannt.
        clock.tick(40)

    if pg.mixer:
        pg.mixer.music.fadeout(1000)
    pg.time.wait(1000)


## Rufe die "main"-Funktion auf, wenn dieses Skript ausgeführt wird
if __name__ == "__main__":
    main()
    pg.quit()

In der Hauptfunktion initialisieren wir Spielgruppen für verschiedene Entitäten, erstellen Startwerte und implementieren die Hauptspielschleife. Die Spiellogik, die Verarbeitung der Spieler-Eingabe, Kollisionen und das Zeichnen der Spielszene werden innerhalb dieser Schleife durchgeführt.

Spielen des Spiels

Jetzt können wir das Spiel starten, indem wir den folgenden Befehl ausführen:

cd ~/project
python aliens.py
✨ Lösung prüfen und üben

Spielen des Spiels

Jetzt können wir das Spiel starten, indem wir den folgenden Befehl ausführen:

cd ~/project
python aliens.py
Screenshot der Spielausführung

Zusammenfassung

In diesem Projekt haben wir den Prozess des Erstellens eines einfachen "Aliens"-Spiels mit Pygame in mehrere Schritte unterteilt. Wir haben die Projekt-Dateien eingerichtet, Spieler- und Alien-Klassen definiert, zusätzliche spielbezogene Klassen erstellt und das Spiel mit Ressourcen initialisiert. Schließlich haben wir die Hauptspielschleife implementiert, um die Spiellogik und Interaktionen zu verarbeiten.

In den kommenden Schritten werden wir fortfahren, Funktionen zu den Spieler- und Alien-Klassen hinzuzufügen, Bewegung und Schießen zu behandeln und die visuellen Elemente des Spiels zu aktualisieren.