image image


image

Was ist unser Vektor, Viktor? »Nature of Code«, Kapitel 1 in Pygame Zero (Teil 1)

Nachdem ich alle Vorarbeiten abgeschlossen hatte, konnte ich mein Vorhaben umsetzen, die Programme aus Daniel Shiffmans Buch »The Nature of Code« nach Python, genauer nach Pygame Zero zu portieren. Shiffman selber erfindet sein Buch ja auch gerade neu, indem er es in einer Video-Serie auf YouTube von Processing (Java) auf P5.js, dem JavaScript-Mode von Processing, umschreibt. Ich muß mich ein wenig beeilen, denn Daniel Shiffman ist schon bei Kapitel 2 und ich fange gerade mit Kapitel 1 an.

Ich habe mir aber auch vorgenommen – auch wenn Pygame Zero dagegen sich manchmal ein wenig ziert – alles objektorientiert wie in Java zu programmieren, und so habe ich als erstes die Klasse Mover implementiert:

class Mover(Actor):
    
    def __init__(self, x, y, im, rotspeed):
        super().__init__(im, (x, y))
        self.pos = (x, y)
        self.im = im
        self.rotspeed = rotspeed
        self.angle = 90
        self.location = PVector(x, y)
        self.velocity = PVector(random.uniform(-2, 2), random.uniform(-2, 2))
        # self.acceleration = PVector(-0.001, 0.01)
        self.topspeed = 10
    
    def update(self):
        self.acceleration = PVector.random2D()
        self.acceleration.mult(0.5)
        # self.acceleration.mult(random.uniform(0, 2))
        self.velocity.add(self.acceleration)
        self.velocity.limit(self.topspeed)
        self.location.add(self.velocity)
        self.pos = (self.location.x, self.location.y)
        self.angle += self.rotspeed
    
    def check_edges(self):
        if (self.location.x > WIDTH - RADIUS):
            self.location.x = RADIUS
        elif (self.location.x < RADIUS):
            self.location.x = WIDTH - RADIUS
        if (self.location.y > HEIGHT - RADIUS):
            self.location.y = RADIUS
        elif (self.location.y < RADIUS):
            self.location.y = HEIGHT - RADIUS
    
    def bounce(self):
        if (self.location.x > WIDTH - RADIUS) or (self.location.x < RADIUS):
            self.velocity.x *= -1
        if (self.location.y > HEIGHT - RADIUS) or (self.location.y < RADIUS):
            self.velocity.y *= -1

Die Klasse Mover erbt von Actor und Actor ist in Pygame Zero eine sehr mächtige Klasse, die fast alle Methoden besitzt, die Sprites benötigen. Daher konnte ich auf die Implementierung der draw()-Methode verzichten, das macht Actor schon für mich. Die Methoden check_edges() und bounce() sollten nie gleichzeitig aufgerufen werden, denn die erste macht aus dem Fenster eine Toruswelt, das heißt, wenn der Mover an ein Fensterende geraten ist, taucht er auf der gegenüberliegenden Seite wieder auf. Dagegen läßt bounce() den Mover von den Enden der Fenster abprallen und die Richtung wechseln – wie eine Billardkugel.

Die Zeile self.pos = (x, y) ist der Besonderheit von Pygames Actor geschuldet, der die Position als Tupel verlangt. Ebenfalls bei Shiffman nicht vorhanden ist die Variable rotspeed, er verwendet Kreise in seinen Beispielprogrammen, da spielt es keine Rolle, ob sie sich auch noch um die eigene Achse drehen, aber da ich Bilder von Bällen verwende, sieht es einfach besser aus, wenn sie auch noch rotieren.

Der Vektor acceleration kann entweder statisch im Konstruktor definiert werden (siehe auskommentierte Zeile dort) oder dynamisch in update(). Falls Ihr diese Möglichkeit wählt, könnt Ihr entweder noch die acceleration mit einer Konstante multiplizieren oder aber auch dafür einen zufälligen Faktor wählen (auskommentiert). Wenn Ihr die letzte Möglichkeit nutzt und dann die Randbedingung bounce() wählt, dann sieht das so aus, als ob ein völlig betrunkener Ball über das Spielfeld torkelt.

Das Hauptprogramm ist dann wieder entsprechend kurz geraten:

mover = Mover(100, 200, "ball1", 5)

def draw():
    screen.fill((100, 200, 0))
    mover.draw()

def update():
    mover.update()
    # mover.check_edges()
    mover.bounce()

Die Methode draw() erbt Mover von Actor, sie mußte also – wie oben schon geschrieben – in der Klasse nicht extra implementiert werden.

Das Bild des Balles stammt wieder aus der freien (CC-BY-4.0) Emoji-Implementierung von Twitter (Twemojis), Ihr könnt aber auch jedes andere, annähernd kreisförmige Bildchen dafür benutzen. Gegebenenfalls müßt Ihr dann aber die Konstante RADIUS anpassen.

So, und hier nun für alle Neugierigen und Experimentierfreudigen unter Euch das komplette Programm:

import pgzrun
from pvector import PVector
import random

WIDTH = 400
HEIGHT = 400
TITLE = "Mover 1"
RADIUS = 16

class Mover(Actor):
    
    def __init__(self, x, y, im, rotspeed):
        super().__init__(im, (x, y))
        self.pos = (x, y)
        self.im = im
        self.rotspeed = rotspeed
        self.angle = 90
        self.location = PVector(x, y)
        self.velocity = PVector(random.uniform(-2, 2), random.uniform(-2, 2))
        # self.acceleration = PVector(-0.001, 0.01)
        self.topspeed = 10
    
    def update(self):
        self.acceleration = PVector.random2D()
        self.acceleration.mult(0.5)
        # self.acceleration.mult(random.uniform(0, 2))
        self.velocity.add(self.acceleration)
        self.velocity.limit(self.topspeed)
        self.location.add(self.velocity)
        self.pos = (self.location.x, self.location.y)
        self.angle += self.rotspeed
    
    def check_edges(self):
        if (self.location.x > WIDTH - RADIUS):
            self.location.x = RADIUS
        elif (self.location.x < RADIUS):
            self.location.x = WIDTH - RADIUS
        if (self.location.y > HEIGHT - RADIUS):
            self.location.y = RADIUS
        elif (self.location.y < RADIUS):
            self.location.y = HEIGHT - RADIUS
    
    def bounce(self):
        if (self.location.x > WIDTH - RADIUS) or (self.location.x < RADIUS):
            self.velocity.x *= -1
        if (self.location.y > HEIGHT - RADIUS) or (self.location.y < RADIUS):
            self.velocity.y *= -1

mover = Mover(100, 200, "ball1", 5)

def draw():
    screen.fill((100, 200, 0))
    mover.draw()

def update():
    mover.update()
    # mover.check_edges()
    mover.bounce()

pgzrun.go()

Die Klasse PVector hatte ich vor langer Zeit schon einmal als Ergänzung für die Nodebox 1 geschrieben. Sie folgt weitestgehend (aber nur für 2D-Vektoren) der Klasse PVector in Processing (und Processing.py), hat aber einige Python-spezifische Besonderheiten wie Operator-Überladung anstelle von statischen Methoden. Sie steht unter einer freien (MIT) Lizenz und Ihr könnt sie von meinem Repositorium auf GitHub herunterladen. Dort findet Ihr auch eine kurze Dokumentation/Auflistung der einzelnen Methoden. Sie ist in pure Python geschrieben und läuft sowohl unter Python 2.7 als auch unter Python 3. Dadurch sollte sie mit allem funktionieren, was den Namen Python verdient, zum Beispiel auch mit Jython und TigerJython. Das habe ich aber noch nicht getestet.

Ich muß sagen, die Arbeit mit Pygame Zero macht mir mehr und mehr Spaß. Es ist eine ähnliche »Null-Konfigurations«-Umgebung wie Processing, läuft aber ohne daß eine spezielle IDE notwendig ist.

Jetzt muß ich »nur noch« das Programm implementieren, in dem der Mover den Mauszeiger umtanzt, dann habe ich das erste Kapitel abgeschlossen und kann Daniel Shiffman ins zweite Kapitel hinterherhecheln. Still digging!

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

Diese Spalte wurde absichtlich leergelassen!


Werbung


image  image  image
image  image  image


image