image image


image

Spieleprogrammierung: Apple Invaders in Pygame Zero

In Vorfreude auf mein ursprünglich als Festtagslektüre geplante Buch »Code the Classics«, das wohl nun doch erst am nächsten Sonnabend ausgeliefert wird, hatte ich mir noch einmal die zehn kurzen Video-Tutorials »Create a Casual Game with Pygame Zero« reingezogen. Das brachte mir soviel Spaß, daß auch ich ein Casual Game in Pygame Zero programmieren wollte. Im letzten Jahre hatte ich ja schon einmal das kleine Spiel Apple Invaders (Teil 2) nach einer Idee von Matthew Hopson in Processing.py, dem Python-Mode von Processing programmiert und dies ähnelte doch sehr dem Spiel im oben erwähnten Tutorial. Was lag also näher, als eine weitere Variante davon zu erstellen? Dieses Mal natürlich in Pygame Zero!

Die Idee des Spiels ist einfach. Aus irgendwelchen Gründen regnet es Äpfel und der Spieler, ein Gripe (was immer für ein Fabelwesen das auch sein mag 1), muß die grünen Äpfel fangen (jeder aufgefangenene oder aufgesammelte grüne Apfel bringt einen Punkt). Damit das Spiel nicht zu einfach wird, liegen die gefallenen Äpfel nur weniger als eine Sekunde (40 Frames) auf dem Fußboden, dann verschwinden sie. Jeder grüne Apfel, den der Gripe nicht aufgesammelt hat, kostet einen Punkt. Hat der Gripe weniger als null Punkte, endet das Spiel.

Absolut toxisch sind dagegen die roten Äpfel. Eine Berührung mit einem dieser roten Äpfel kostet dem Gripe das Leben und das Spiel ist ebenfalls zu Ende.

Zuerst einmal habe ich das Spielefenster mit dem Hintergrundbild und dem Fußboden, auf dem sich der Spieler bewegt, aufgebaut:

WIDTH = 480
HEIGHT = 380
CENTER = WIDTH/2
TITLE = "🍏🍎 Apple Invaders 🍎🍏"
GROUNDWIDTH = 32

def draw():
    screen.blit(images.background, (0, 0))

    for i in range(15):
        screen.blit(images.ground, (i*GROUNDWIDTH, HEIGHT - 32))
        screen.blit(images.checker, (i*GROUNDWIDTH, HEIGHT - 16))

Das Hintergrundbild, die Kacheln für den Fußboden wie auch den Gripe habe ich dem freien (CC-BY-4.0) Tileset von Marc Russell von Zingot Games entnommen, das es auf OpenGameArt.org zum Download gibt. Die Lizenzbedingungen verlangen die Namensnennung und das habe ich damit erledigt.

Dann habe ich den Gripe hinzugefügt:

## Player
gripe = Actor("gripe")
gripe.pos = midbottom=(CENTER, HEIGHT - 48)
gripe.speed = 5
gripe_frame = 0

def gripe_walk_left():
    global gripe_frame
    if gripe_frame < 7:
        gripe_frame += 1
    else:
        gripe_frame = 0
    gripe.image = "gl" + str(int(gripe_frame))


def gripe_walk_right():
    global gripe_frame
    if gripe_frame < 7:
        gripe_frame += 1
    else:
        gripe_frame = 0
    gripe.image = "gr" + str(int(gripe_frame))

def gripe_move():
    gripe.image = "gripe"
    if keyboard.left and gripe.left > 0:
        gripe_walk_left()
        gripe.x -= gripe.speed
    elif keyboard.right and gripe.right < WIDTH:
        gripe_walk_right()
        gripe.x += gripe.speed

Wenn man jetzt noch in der draw()-Methode

def draw():
    screen.blit(images.background, (0, 0))
    gripe.draw()

und in der update()-Funktion

def update():
    gripe_move()

hinzufügt, dann kan man die Spielerfigur mit den Pfeiltasten rechts und links über den Bildschirm wuseln lassen.

Für die Bewegungen nach rechts und links existieren jeweils acht Einzelbilder, die grx.png respektive glx.png heißen, wobei das x jeweils für eine Ziffer zwischen 0 und 8 steht. Daher habe ich in den jeweiligen Funktionen die Dateinamen mit

    gripe.image = "gr" + str(int(gripe_frame))

respektive

    gripe.image = "gl" + str(int(gripe_frame))

zusammengesetzt. Die Variable gripe_frame mußte dafür als global deklariert werden.

Ähnlich habe ich mit den Äpfeln verfahren, nur daß ich sie in Listen zusammengefaßt habe. Die Anzahl der Äpfel ist fix, die grünen Äpfel fallen auf den Fußboden und werden nach 40 Frames wieder an eine zufällige Position oberhalb des Bildschirmfensters gebeamt, die roten Äpfel fallen aus dem unteren Rand des Bildschirmfenster heraus, bevor sie wieder nach oben katapultiert werden.

image

Da die Äpfel auch in ihren Listen initialisiert werden, konnte ich ihnen keine Eigenschaften verpassen, sondern mußte diese in einfache Variablen hinterlegen, wie zum Beispiel:

green_apple_speed = 2
red_apple_speed = 3

Beispielhaft zeigen die Code-Schnipsel hier, wie die grünen Äpfel erzeugt werden, bis auf kleine Änderungen werden die roten Äpfel gleich behandelt:

green_apples = []
number_of_green_apples = 6
apple_frame = 0

def green_apples_spawn():
    green_apples.append(Actor("applegreen", pos = (randint(8, WIDTH -  8), 16)))

def green_apple_fall(apple):
    global apple_frame, score, game_over
    if apple.y < HEIGHT - 40:
        apple.y += green_apple_speed
    else:
        if apple_frame < 1:
            apple_frame += 0.025
        else:
            apple.pos = (randint(8, WIDTH - 8), 16)
            score -= 1
            if score < 0:
                score = 0
                game_over = True
            apple_frame = 0
            

for i in range(number_of_green_apples):
    clock.schedule(green_apples_spawn, i*2)

Beide Äpfelsorten müssen natürlich in der draw()- und in der update()-Methode abgearbeitet werden:

def draw():
    []
    if not game_over:
        for apple in green_apples:
            apple.draw()
        for apple in red_apples:
            apple.draw()
[]
def update():
    []
        for apple in green_apples:
            green_apple_fall(apple)
            if gripe.colliderect(apple):
                apple.pos = (randint(8, WIDTH - 8), 16)
                score += 1
        for apple in red_apples:
            red_apple_fall(apple)
            if gripe.colliderect(apple):
                game_over = True

Die Bildchen für die Äpfel habe ich Twitters freiem (CC-BY-4.0) Emoji-Set Twemoji entnommen und mit einem Bildverarbeitungsprogramm meines Vertrauens auf 16x16 Pixel heruntergerechnet.

Der Rest – wie zum Beispiel die Behandlung des Spielendes – war dann schnell erledigt, wie Ihr dem gesamten Quellcode des Spieles entnehmen könnt:

import pgzrun
from random import randint

WIDTH = 480
HEIGHT = 380
CENTER = WIDTH/2
TITLE = "🍏🍎 Apple Invaders 🍎🍏"
GROUNDWIDTH = 32
green_apple_speed = 2
red_apple_speed = 3
score = 0
game_over = False

## Player
gripe = Actor("gripe")
gripe.pos = midbottom=(CENTER, HEIGHT - 48)
gripe.speed = 5
gripe_frame = 0

def gripe_walk_left():
    global gripe_frame
    if gripe_frame < 7:
        gripe_frame += 1
    else:
        gripe_frame = 0
    gripe.image = "gl" + str(int(gripe_frame))


def gripe_walk_right():
    global gripe_frame
    if gripe_frame < 7:
        gripe_frame += 1
    else:
        gripe_frame = 0
    gripe.image = "gr" + str(int(gripe_frame))

def gripe_move():
    gripe.image = "gripe"
    if keyboard.left and gripe.left > 0:
        gripe_walk_left()
        gripe.x -= gripe.speed
    elif keyboard.right and gripe.right < WIDTH:
        gripe_walk_right()
        gripe.x += gripe.speed

## Green Apples
green_apples = []
number_of_green_apples = 6
apple_frame = 0

def green_apples_spawn():
    green_apples.append(Actor("applegreen", pos = (randint(8, WIDTH -  8), 16)))

def green_apple_fall(apple):
    global apple_frame, score, game_over
    if apple.y < HEIGHT - 40:
        apple.y += green_apple_speed
    else:
        if apple_frame < 1:
            apple_frame += 0.025
        else:
            apple.pos = (randint(8, WIDTH - 8), 16)
            score -= 1
            if score < 0:
                score = 0
                game_over = True
            apple_frame = 0
            

for i in range(number_of_green_apples):
    clock.schedule(green_apples_spawn, i*2)

## Red Apples
red_apples = []
number_of_red_apples = 2

def red_apples_spawn():
    red_apples.append(Actor("applered", pos = (randint(8, WIDTH -  8), 16)))

def red_apple_fall(apple):
    if apple.y < HEIGHT:
        apple.y += red_apple_speed
    else:
        apple.pos = (randint(8, WIDTH -  8), 16)

for i in range(number_of_red_apples):
    clock.schedule(red_apples_spawn, i*5)

## Game Loop

def draw():
    screen.blit(images.background, (0, 0))
    gripe.draw()
    
    if not game_over:
        for apple in green_apples:
            apple.draw()
        for apple in red_apples:
            apple.draw()
    else:
        gripe.image = "gdie"
        screen.draw.text("Game Over", center = (CENTER, HEIGHT/2), color = "red", fontsize = 64)
    
    for i in range(15):
        screen.blit(images.ground, (i*GROUNDWIDTH, HEIGHT - 32))
        screen.blit(images.checker, (i*GROUNDWIDTH, HEIGHT - 16))
    screen.draw.text(f"Score: {score}", midtop = (CENTER, 10), fontsize = 36)
    screen.draw.text("Vermeide die Roten und schnappe die Grünen", midbottom = (CENTER, HEIGHT - 3), fontsize = 24)

def update():
    global score, game_over
    if not game_over:
        gripe_move()
    
        for apple in green_apples:
            green_apple_fall(apple)
            if gripe.colliderect(apple):
                apple.pos = (randint(8, WIDTH - 8), 16)
                score += 1
        for apple in red_apples:
            red_apple_fall(apple)
            if gripe.colliderect(apple):
                game_over = True

pgzrun.go()

Es ist ein komplettes Spiel, das ich hier in weniger als 125 Zeilen Quellcode entwickelt habe. Und es ist – zumindest für meine Fähigkeiten – gar nicht so einfach zu spielen. Ihr könnt es ausprobieren, denn der gesamte Quellcode inklusive der Bildchen findet Ihr in meinem GitHub-Repositorium. Wenn Ihr die Ordnerstruktur beibehaltet und Pygame Zero auf Eurem Computer installiert habt, könnt Ihr es einfach aus einem Editor heraus starten. Habt Spaß damit!

Die Anleitung zum Spiel »Vermeide die Roten und schnappe die Grünen« solltet Ihr als politische Anspielung nicht zu ernst nehmen. Aber sie lag einfach in der Luft.

image

  1. Ursprünglich hatte ich vorgesehen, als Spieler »Pingus«, die Spielfigur aus dem gleichnamigen, freien Lemmings-Klon einzusetzen, aber das wären der Anspielungen zu viele gewesen. 🤓 


(Kommentieren) 

image image



Über …

Der Schockwellenreiter ist seit dem 24. April 2000 das Weblog digitale Kritzelheft von Jörg Kantel (Neuköllner, EDV-Leiter Rentner, 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