image image


Getting Started With PyGame (3): Sprites und Klassen

Kaum irgendwo anders sind die Klassen der objektorientierten Programmierung (OOP) so nützlich wie in der Spieleprogrammierung, wo die einzelnen Akteure – speziell die computergesteuerten Spielfiguren – ja weitestgehend autonom handeln sollen. PyGame hat dafür eine eigene abstrakte Klasse pygame.sprite.Sprite, die als Blaupause (Template) für eigene Spieler- und Nichtspieler-Charaktere dienen kann (genauer gesagt handelt es sich dabei nicht um echte (Hardware-) Sprites, sondern um Software-Sprites). In diesem Sinne habe ich das Programm umgeschrieben, und der Heldin »Kitty« aus dem letzten Tutorial eine eigene Klasse verpaßt.

image

Diese Klasse erbt von pygame.sprite.Sprite vor allen Dingen etliche nützliche Attribute, die das den Sprites umgebene Rechteck betreffen und bringt auch Hilfen für die Kollisionserkennung mit, die ich in einem späteren Tutorial behandeln möchte. Erst einmal ist sie jedoch ziemlich einfach:

class Kitty(pygame.sprite.Sprite):
    
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("images/horngirl.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.centerx = 275
        self.rect.centery = 240
        self.dx = 5
        self.dy = 0
        
    def update(self):
        self.rect.centerx += self.dx
        if self.rect.left > screen.get_width():
            self.rect.right = 0
            self.rect.centery = random.randrange(50, screen.get_height()-85)

Wie fast alle Python-Klassen wird sie im »Constructor« __init__() initialisiert und auch die Superklasse aufgerufen. Dann wird das Bild geladen und noch einige Attribute vergeben. Im Beispielprogramm soll Kitty mit einer Geschwindigkeit von fünf Pixel per Frame von links nach rechts laufen, daher habe ich dx = 5 gesetzt (dy steht hier prophylaktisch für spätere Erweiterungen, wird aber momentan noch nicht genutzt).

Die eigentliche Arbeit passiert in der update()-Methode, hier wird das Verhalten des Sprites festgelegt. Wie leicht zu erkennen, ist dies noch nicht sonderlich intelligent. Kitty bewegt sich mit der Geschwindigkeit dx von links nach rechts und wenn sie den rechten Bildschirmrand erreicht hat wird sie mit einer zufällig ausgewählten Höhe wieder an den rechten Bildschirmrand versetzt. Das vollständige Programm sieht so aus:

#!python
# coding=utf-8

# PyGame: Walking Kitty!

import pygame, random
from pygame.locals import *

pygame.init()

screen = pygame.display.set_mode((640, 480))

class Kitty(pygame.sprite.Sprite):
    
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("images/horngirl.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.centerx = 275
        self.rect.centery = 240
        self.dx = 5
        self.dy = 0
        
    def update(self):
        self.rect.centerx += self.dx
        if self.rect.left > screen.get_width():
            self.rect.right = 0
            self.rect.centery = random.randrange(50, screen.get_height()-85)

def main():
    
    pygame.display.set_caption("Walking Kitty!")
    
    background = pygame.Surface(screen.get_size())
    background.fill((0, 80, 125))
    screen.blit(background, (0, 0))
    
    kitty = Kitty()
    allSprites = pygame.sprite.Group(kitty)
    
    clock = pygame.time.Clock()
    keepGoing = True

    while keepGoing:
        # Max Framerate 30 fps
        clock.tick(30)
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
        
        allSprites.clear(screen, background)
        allSprites.update()
        allSprites.draw(screen)
        
        pygame.display.flip()

if __name__ == "__main__":
    main()

Wie der geneigte Leser feststellen kann, bin ich einer Python-Konvention gefolgt und habe das Hauptprogramm in die Funktion main() verschoben. Das hat den Vorteil, daß ich die Klasse Kitty weiterverwenden kann, denn die main()-Funktion wird nur aufgerufen, wenn das Skript tatsächlich als Hauptprogramm läuft und nicht, wenn es in einem anderen Skript importiert wurde.

Klassen beginnen gemäß einer weiteren Konvention (fast) immer mit Großbuchstaben, Instanzen der Klasse werden wie alle Variablen gemäß dieser Konvention klein geschrieben. Daher habe ich mit

kitty = Kitty()

eine Instanz der Klasse Kitty erzeugt und diese mit

allSprites = pygame.sprite.Group(kitty)

einer spriteGroup, die ich allSprites genannt habe, zugefügt. Denn Sprite-Objekte funktionieren in PyGame nicht solo. Wenn ein Sprite irgendetwas tun soll, muß es Mitglied einer Sprite-Group sein, die im Zweifelsfalle auch nur aus einem einzigen Sprite bestehen kann. Wie das funktioniert seht Ihr im Weiteren:

allSprites.clear(screen, background)
allSprites.update()
allSprites.draw(screen)

Aus Performance-Gründen zeichnet PyGame nämlich nicht den gesamten Bildschirm inklusive Hintergrund neu (wie es zum Beispiel Processing macht), sondern entfernt nur die Sprites und zeichnet vom Hintergrund nur die leeren Stellen neu (allSprites.clear(screen, background)). Danach werden mit allSprites.update() die update()-Methoden aller Sprites der Sprite-Group aufgerufen und die neuen Sprites der Gruppe schließlich mit allSprites.draw(screen) in den Buffer gezeichnet.

Der Rest funktioniert wie gehabt: Mit pygame.display.flip() wird der Buffer auf den Bildschirm gebracht und sichtbar.

Kitty folgt der Maus

Die Arbeit mit Klassen erhöht die Wartbarkeit des Programm-Codes beträchtlich. Wenn ich nämlich jetzt möchte, daß Kitty nicht stur von rechts nach links läuft, sondern der Maus folgt, muß ich nur Kittys update()-Methode ändern, alles andere kann wie gehabt bleiben. Die Update Methode ist dann – dank PyGames integrierter Maus-Unterstützung – von ergreifender Schlichtheit.

def update(self):
    self.rect.center = pygame.mouse.get_pos()

Das ist alles, jetzt folgt Kity der Maus. (Ich habe im endgültigen Code ein paar überflüssige Kommandos herausgestrichen, aber notwendig wäre das nicht gewesen.)

Im Hauptprogramm habe ich noch mit

pygame.mouse.set_visible(False)

dafür gesorgt, daß im PyGame-Fenster der Mauszeiger nicht zu sehen ist. Natürlich sollte man der Ordnung halber dann nicht vergessen, zum Schluß mit

pygame.mouse.set_visible(True)

den Mauzeiger wieder sichtbar zu machen (in diesem Falle nicht wirklich erforderlich, aber man sollte sich solche Aufräumarbeiten von Anfang an angewöhnen, bei späteren Erweiterungen erspart man sich unter Umständen so mühevolles Suchen nach unverständlichen Fehlern).

Auch hier noch der vollständige Programmcode der mausfolgenden Kitty zum Nachvollziehen und Nachprogrammieren:

#!python
# coding=utf-8

# PyGame: Mousing Kitty!

import pygame
from pygame.locals import *

pygame.init()

screen = pygame.display.set_mode((640, 480))

class Kitty(pygame.sprite.Sprite):
    
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("images/horngirl.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.centerx = 275
        self.rect.centery = 240
        
    def update(self):
        self.rect.center = pygame.mouse.get_pos()

def main():
    
    pygame.display.set_caption("Mousing Kitty!")
    
    background = pygame.Surface(screen.get_size())
    background.fill((0, 80, 125))
    screen.blit(background, (0, 0))
    
    kitty = Kitty()
    allSprites = pygame.sprite.Group(kitty)
    
    pygame.mouse.set_visible(False)
    clock = pygame.time.Clock()
    keepGoing = True

    while keepGoing:
        # Max Framerate 30 fps
        clock.tick(30)
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
        
        allSprites.clear(screen, background)
        allSprites.update()
        allSprites.draw(screen)
        
        pygame.display.flip()
    
    pygame.mouse.set_visible(True)

if __name__ == "__main__":
    main()

Das war’s für heute. In einem nächsten Tutorial möchte ich Euch zeigen, wie man mit mehreren Sprites in einer Sprite-Group umgeht und welche Unterstützung für die Kollisionserkennung die Sprite-Klasse bietet. Still digging!

Die bisherigen PyGame-Tutorials im Schockwellenreiter

Wird (hoffentlich) fortgesetzt …


(Kommentieren)  Getting Started With PyGame (3) – 20160524 bitte flattrn

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