image image


image

Spaß mit Pygame Zero: Cute Space

Mit einem kleinen Spiel möchte ich meine Erkundungen von Pygame Zero fortsetzen, der auf Pygame aufsetzenden Bibliothek, die die Programmierung von Spielen mit Python radikal vereinfachen will. Inspiriert wurde ich dabei von dem Kapitel 8 des Buches »Spiele mit Python supereasy programmieren«, das trotz des idiotischen Titels recht empfehlenswert ist – nicht nur für Kinder, die programmieren lernen wollen, sondern auch für kindisch gebliebene, erwachsene Spielkälber wie mich. 🤓

Doch jetzt das Spiel: Die Weltraumagentin Octopussy fliegt mir ihrem Raumschiff von einem Einsatz, bei dem sie natürlich die Welt gerettet hat, zurück zu ihrem Heimatplaneten. Doch dabi wird sie von den bösen Raketenenjungen (Rocketboys) belästigt, denen sie ausweichen muß. Kollidiert sie mit einem der Rocketboys ist das Spiel zu Ende. Es ist ebenfalls zu Ende, wenn sie den oberen oder unteren Bildschirmrand berührt.

Wenn der Spieler nichts unternimmt, sinkt Octopussy mit ihrer Rakete langsam zum unteren Bildschirmrand, klickt der Spieler jedoch auf eine Maustaste, schießt sie um 50 Pixel nach oben.

Dann sind da noch die Planeten. Momentan gleiten sie einfach funktionslos langsam über den Bildschirm. Aber in einer fortgeschritteneren Fassung des Spiels könnte ihnen durchaus noch eine Bedeutung zuwachsen.

Gelingt es Octopussy einem Rocketboy auszuweichen und verschwindet dieser am linken Bildschirmrand, bekommt sie einen Punkt zugewiesen. Der Punktestand wird links oben am Bildschirm angezeigt.

image image image

Für das Spiel habe ich diese drei Bilder ausgesucht. Sie stammen aus dem von Daniel Cook (Danc) in seinem Blog Lost Garden unter einer freien Lizenz (CC BY 3.0 US) zur Verfügung gestellten Tileset Planet Cute. Regelmäßige Leser dieses Blogs Kritzelheftes wissen, daß ich diese schon häufiger verwendet habe.

Doch nun zum Quellcode: Nach den Importen und den Konstanten für das Fenster habe ich die Akteure initialisiert:

octopussy = Actor("octopussy", center = (100, HEIGHT/2))

rocketboys = []
for _ in range(3):
    rocketboys.append(Actor("rocketboy", center = (randint(700, 1400), randint(40, 440))))

for rocketboy in rocketboys:
    rocketboy.speed = randint(2, 4)

planets = []
for i in range(2):
    planets.append(Actor("planet"))
planets[0].pos = (800, 125)
planets[1].pos = (1025, 220)

Während die Rocketboys eine zufällige Startposition zugewiesen bekommen, habe ich diese bei den Planeten von Hand festgelegt. Sie waren sonst zu oft zu dicht beieinander und während das bei den Rocketboys nicht störte, sah es bei den Planeten irgendwie seltsam aus.

Beim reset(), also wenn die Rocketboys und die Planeten, die beide von rechts nach links über den Bildschirm ziehen, diesen links verlassen haben, wird aber beiden eine neue, zufällige Position rechts weit außerhalb des Bildschirms zugewiesen:

def reset(actor):
    actor.x = randint(700, 2100)
    actor.y = randint(40, 440)

Kern jedes Pygame-Zero-Programms sind ja die Methoden draw() und update(), die beide von Pygame Zero autonom aufgerufen werden, update() etwa 60 mal in der Sekunde, draw() aus Performance-Gründen nur, wenn wirklich etwas neu gezeichnet werden muß. Die update()-Methode enthält daher die gesamte Spiellogik,

def update():
    global game_over, score
    if not game_over:
        if not up:
            octopussy.y += 1
        for rocketboy in rocketboys:
            if rocketboy.x > -rocketboy.width:
                rocketboy.x -= rocketboy.speed
            else:
                score += 1
                reset(rocketboy)
        for planet in planets:
            if planet.x > -planet.width:
                planet.x -= 0.25 
            else:
                reset(planet)
        if octopussy.top < 0 or octopussy.bottom > HEIGHT:
            game_over = True
        for rocketboy in rocketboys:
            if octopussy.collidepoint(rocketboy.x, rocketboy.y):
                game_over = True

während die draw()-Methode sich wirklich nur um das Neuzeichnen des Bildschirmfensters kümmert:

def draw():
    if not game_over:
        screen.fill((0, 80, 125))
        for planet in planets:
            planet.draw()
        octopussy.draw()
        for rocketboy in rocketboys:
            rocketboy.draw()
        screen.draw.text("Punkte: " + str(score), (10, 10), color = "white")
    else:
        screen.fill((0, 80, 125))
        screen.draw.text("GAME OVER!", (WIDTH/2 - 50, HEIGHT/2), color = "white")

Und dann sind da noch die Methoden on_mouse_down() und on_mouse_up(). Mit diesen beiden Methode steuert die Spielerin oder der Spieler Octopussy in ihrem Raumschiff:

def on_mouse_down():
    global up
    up = True
    octopussy.y -= 50

def on_mouse_up():
    global up
    up = False

Für meinen Geschmack sind es eigentlich zu viele globale Variablen, die benötigt werden, aber mir ist nicht eingefallen, wie ich diese vermeiden kann, ohne in das Framework selber einzugreifen.

Hier noch einmal das vollständige Programm, für alle, die es nachvollziehen oder nachprogrammieren oder erweiternwollen:

import pgzrun
from random import randint

WIDTH = 640
HEIGHT = 480
TITLE = "Cute Space"

octopussy = Actor("octopussy", center = (100, HEIGHT/2))

rocketboys = []
for _ in range(3):
    rocketboys.append(Actor("rocketboy", center = (randint(700, 1400), randint(40, 440))))

for rocketboy in rocketboys:
    rocketboy.speed = randint(2, 4)

planets = []
for i in range(2):
    planets.append(Actor("planet"))
planets[0].pos = (800, 125)
planets[1].pos = (1025, 220)

score = 0
game_over = False
up = False

def reset(actor):
    actor.x = randint(700, 2100)
    actor.y = randint(40, 440)

def draw():
    if not game_over:
        screen.fill((0, 80, 125))
        for planet in planets:
            planet.draw()
        octopussy.draw()
        for rocketboy in rocketboys:
            rocketboy.draw()
        screen.draw.text("Punkte: " + str(score), (10, 10), color = "white")
    else:
        screen.fill((0, 80, 125))
        screen.draw.text("GAME OVER!", (WIDTH/2 - 50, HEIGHT/2), color = "white")

def update():
    global game_over, score
    if not game_over:
        if not up:
            octopussy.y += 1
        for rocketboy in rocketboys:
            if rocketboy.x > -rocketboy.width:
                rocketboy.x -= rocketboy.speed
            else:
                score += 1
                reset(rocketboy)
        for planet in planets:
            if planet.x > -planet.width:
                planet.x -= 0.25 
            else:
                reset(planet)
        if octopussy.top < 0 or octopussy.bottom > HEIGHT:
            game_over = True
        for rocketboy in rocketboys:
            if octopussy.collidepoint(rocketboy.x, rocketboy.y):
                game_over = True
    
def on_mouse_down():
    global up
    up = True
    octopussy.y -= 50

def on_mouse_up():
    global up
    up = False
    
pgzrun.go()

Dafür, daß dies schon ein vollständiges kleines Spiel ist, ist der Quellcode dank Pygame Zero mit 75 Zeilen immer noch recht kurz.

image

Natürlich kann man das Spiel noch erweitern. Es gibt aus der gleichen Quelle noch den Käferjungen (Beetleboy), denn man wahlweise als Boss-Gegner oder auch als Retter von Octopussy einsetzen könnte. Außerdem sind mehrere Level denkbar, bei denen es mehr und schnellere Rocketboys gibt. Oder man gibt Octopussy mehrere Leben, so daß das Spiel nicht mit der ersten Kollision endet. Und jedesmal, wenn sie einen Planeten überfliegt, wird die Anzahl ihrer Leben wieder erhöht.

Im Buch gibt es auch noch einen High Score, der zum Schluß des Spieles angezeigt wird und den es zu verwalten gilt. Man kann also noch einiges mit Octopussy anstellen und dabei Pygame Zero lernen. Still digging!


(Kommentieren) 

image image



Über …

Der Schockwellenreiter ist seit dem 24. April 2000 das Weblog digitale Kritzelheft von Jörg Kantel (Neuköllner, EDV-Leiter, Autor, Netzaktivist und Hundesportler — Reihenfolge rein zufällig). Hier steht, was mir gefällt. Wem es nicht gefällt, der braucht ja nicht mitzulesen. Wer aber mitliest, ist herzlich willkommen und eingeladen, mitzudiskutieren!

Alle eigenen Inhalte des Schockwellenreiters stehen unter einer Creative-Commons-Lizenz, jedoch können fremde Inhalte (speziell Videos, Photos und sonstige Bilder) unter einer anderen Lizenz stehen.

Der Besuch dieser Webseite wird aktuell von der Piwik Webanalyse erfaßt. Hier können Sie der Erfassung widersprechen.

Diese Seite verwendet keine Cookies. Warum auch? Was allerdings die iframes von Amazon, YouTube und Co. machen, entzieht sich meiner Kenntnis.


Werbung


Werbung


image  image  image
image  image  image


image