image image


Basic Side Scroller objektorientiert und in Pygame Zero

Nach meinem Rant letzte Woche über fehlende Tutorials zur objektorientierten Programmierung in Pygame Zero dachte ich mir, daß ich doch mit gutem Beispiel vorangehen und selber einmal ein komplettes Spielchen objektorientiert in Pygame Zero programmieren könnte.

image

Gedacht, getan: Ich habe mir den Basic Side Scroller, den ich vor einigen Wochen in Processing.py, dem Python-Mode von Processing programmiert hatte (Teil 1, Teil 2) vorgenommen und ihn ein wenig aufgebohrt. Zum einen habe ich die Hintergrundbilder nicht mehr aus Kreisen zusammengesetzt, sondern mir auf OpenGameArt.org diese freien Bilder von GrumpyDiamond heruntergeladen und sie mit einem Bildverarbeitungsprogramm meines Vertrauens aufgehübscht. Die Hindernisse und das hüpfende Alien stammen wieder aus dem schier unerschöpflichen Fundes von Kenney.nl. Auch diese mußte ich ein wenig nachbearbeiten, weil sonst das umgebende Rechteck für die Kollisionserkennung zu groß gewesen wäre.

Zum anderen besitzt das Programm nun drei Basis-Klassen (Layer, Obstacle und Player), die alle von Actor abgeleitet wurden. Die Trennung zwischen Layer und Obstacle wäre nicht unbedingt erforderlich gewesen, aber in meinen Augen schien sie irgendwie logisch.

Neben den Klassendefinitionen nehmen den größten Teil des Programms die Erzeugung der einzelnen Instanzen ein, insbesondere, da die »beweglichen« Layer immer zwei Instanzen benötigten, damit das zweite Bild nahtlos an das erste Bild anschließen kann. Danach habe ich zwei Listen engelegt (layers und obstacles), die dann in der update()-Funktion des Hauptprgramms abgearbeitet werden:

def update():
    for layer in layers:
        layer.move()
    alien.move()
    for obstacle in obstacles:
        if alien.colliderect(obstacle):
            alien.score -= 1

Der draw()-Funktion habe ich noch eine Anzeige spendiert, die den Punktestand angibt.

def draw():
    for layer in layers:
        layer.show()
    alien.show()
    screen.draw.textbox("Score: " + str(alien.score), (0, 440, 100, 20))

Ich bin kein guter Spieler, daher habe ich es mir einfach gemacht und jeden erfolgreichen Sprung mit einem Punkt belohnt, während jeder Mißerfolg mit Punkteabzug bestraft wird (da das Alien durchaus mehrfach mit den Hindernissen kollidieren kann, kann der Punktabzug ziemlich heftig ausfallen). Hier ist sicher noch Optimierungsbedarf, aber für ein einfaches Beispiel reicht es erst einmal aus.

Für diejenigen unter Euch, die das Spiel nachprogrammieren oder eventuell sogar verbessern wollen, hier der komplette Quellcode:

import pgzrun

class Layer(Actor):
    
    def __init__(self, image):
        Actor.__init__(self, image)
        self.speed = 0
        self.turnvalue = 0
    
    def move(self):
        self.left -= self.speed
        if self.right <= 0:
            self.left = self.turnvalue
    
    def show(self):
        self.draw()

class Obstacle(Actor):
    
    def __init__(self, image, pos):
        Actor.__init__(self, image, pos)
        self.bottomleft = pos
        self.speed = 4

    def move(self):
        self.left -= self.speed
        if self.right <= 0:
            self.left = 3.4*WIDTH
    
    def show(self):
        self.draw()

class Player(Actor):
    
    def __init__(self, image, pos):
        Actor.__init__(self, image, pos)
        self.pos = pos
        self.status = "walking"
        self.hit = False
        self.count = 0
        self.vely = 0
        self.score = 0
        self.speed = 1
    
    def move(self):
        if self.status == "walking":
            self.count += self.speed
            if self.count > 10:
                self.count = 0
            if self.count < 6:
                self.image = "alienwalk1.png"
            else:
                self.image = "alienwalk2.png"
        if self.status == "jumping":
            self.image = "alienjump.png"
            self.bottom += self.vely
            self.vely += 0.1
            if self.bottom >= HEIGHT - 75:
                self.bottom = HEIGHT - 75
                self.vely = 0
                self.status = "walking"
                self.score += 1
                
    def show(self):
        self.draw()

WIDTH = 720
HEIGHT = 480
TITLE = "Jump, Älly, Jump!"

bg = Layer("sky_background")
bg.topleft = 0, 0
bg.turnvalue = 0

sun = Layer("sun")
sun.topleft = 500, 20
sun.turnvalue = 0

clouds = Layer("clouds_all")
clouds.topleft = WIDTH, 0
clouds.speed = 1
clouds.turnvalue = WIDTH

mountain1 = Layer("mountain_full")
mountain1.bottomleft = 0, HEIGHT - 75
mountain1.speed = 2
mountain1.turnvalue = 2*WIDTH

mountain2 = Layer("mountain_full")
mountain2.bottomleft = 1279, HEIGHT - 75
mountain2.speed = 2
mountain2.turnvalue = 2*WIDTH

ground1 = Layer("ground")
ground1.bottomleft = 0, HEIGHT + 10
ground1.speed = 4
ground1.turnvalue = WIDTH

ground2 = Layer("ground")
ground2.bottomleft = 1270, HEIGHT + 10
ground2.speed = 4
ground2.turnvalue = WIDTH

spikes1 = Obstacle("spikes", (WIDTH + 100, HEIGHT - 75))
cactus1 = Obstacle("cactus", (WIDTH + 600, HEIGHT - 75))
cactus2 = Obstacle("cactus", (WIDTH + 640, HEIGHT - 75))
cactus3 = Obstacle("cactus", (WIDTH + 670, HEIGHT - 75))
spikes2 = Obstacle("spikesbottomalt", (WIDTH + 1050, HEIGHT - 75))
spikes3 = Obstacle("spikesbottom", (WIDTH + 1150, HEIGHT - 75))
fence1  = Obstacle("fence", (WIDTH + 1620, HEIGHT - 75))
fence2  = Obstacle("fencebroken", (WIDTH + 1680, HEIGHT - 75))
fence3  = Obstacle("fence", (WIDTH + 1740, HEIGHT - 75))

layers = [bg, sun, clouds, mountain1, mountain2, ground1, ground2, spikes1, spikes2, spikes3,
          cactus1, cactus2, cactus3, fence1, fence2, fence3]

obstacles = [spikes1, cactus1, cactus2, cactus3, spikes2, spikes3, fence1, fence2, fence3]

alien = Player("alienwalk1", (50, HEIGHT - 115))

def on_key_down():
    global alien_status, alien_vely
    if keyboard[keys.SPACE] and alien.status == "walking":
       alien.status = "jumping"
       alien.vely += -5

def update():
    for layer in layers:
        layer.move()
    alien.move()
    for obstacle in obstacles:
        if alien.colliderect(obstacle):
            alien.score -= 1

def draw():
    for layer in layers:
        layer.show()
    alien.show()
    screen.draw.textbox("Score: " + str(alien.score), (0, 440, 100, 20))

pgzrun.go()

Obwohl das Spiel sicher schon eine gewisse Komplexität aufweist, ist der Quellcode mit 141 Zeilen noch recht kurz geraten. Das ist eines der Vorteile von Pygame Zero, das vieles von dem, was in anderen Frameworks noch mühselig ausformuliert werden muß, automatisch im Hintergrund erledigt. Allerdings gibt man dafür auch einiges an Autonomie ab – etliche komplexere Aktionen, die Pygame selbstverständlich erlaubt, kann man in Pygame Zero nur mühsam oder gar nicht ausführen. Ich weiß momentan noch zu wenig über Pygame Zero, aber ich denke, man muß sich im Vorfeld schon überlegen, für welche Projekte man Pygame Zero einsetzt und für welche man doch lieber direkt auf Pygame zurückgreift.

Das Programm selber ist unter den Namen sidescroller2.py in meinem GitLab-Repositorium zufinden. Im Verzeichnis images findet Ihr dort auch sämtliche verwendeten Bilder.

In diesem Sinne: Happy Coding!

image


(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