image image


image

Processing.py am Sonntag: Bubble Busters (Tutorial)

Für mein heutiges, sonntägliches Processing.py-Experiment hatte ich mir eine Idee aus dem wunderbaren Buch »Processing for Visual Artists« von Andrew Glassner vorgenommen, die ich aber im Laufe meiner Beschäftigung damit bis zur Unkenntlichkeit verändert hatte, so daß im Endeffekt nur noch die Fabpalette und der Dateiname ghost davon übrigblieb. Denn während Andrew Glassner mit seinem Beispiel in die objektorientierte Programmierung von Processing (Java) einführen wollte, hatte ich ein Beispiel für Kollisionserkennung im Sinn. Und so wurden aus den einfachen »Geistern« explodierende »Bubble Busters«.

Die Grundidee ist geblieben, es ist (mal wieder) eine »Bouncing Ball«-Implementierung, in der sich zufällige Kreise im Graphikfenster bewegen und an den Fensterrändern zurückgeworfen werden. Nur sollten dieses Mal die einzigen Kreise sich nicht überlappen, sondern es sollte eine Kollisionserkennung stattfinden.

Für die Kollisionserkennung habe ich auf mein Tutorial vom November 2019 zurückgegriffen, aber das war nur die halbe Miete. Denn wenn eine Kollision erkannt ist, muß sie ja auch irgendwie aus dem Weg geräumt werden. Ich bin der Einfachheit halber von der Idee ausgegangen, daß diese Kreise Seifenblasen sind, die bei einer Kollision einfach zerplatzen. Da es jedoch langweilig wäre, wenn nicht neue Seifenblasen hinzukämen, habe ich anstelle der zerplatzten Seifenblasen jeweils zwei neue mit neuen Farben und neuen Radii zufällig im Fenster platzieren lassen (Methode reset()). Dadurch bleibt die Anzahl der Seifenblasen im Sketch immer gleich.

Den Seifenblasen habe ich eine eigene Klasse, die Klasse Disk, spendiert, in der neben dem reset() auch die Kollisionserkennung (circle_collision()) abgehandelt wird. Hier der Quellcode dazu:

from random import randint

MIN_R = 10
MAX_R = 25

class Disk():
    
    def __init__(self, _x, _y, _ax, _ay, _r, _clr):
        self.pos = PVector(_x, _y)
        self.dir = PVector(_ax, _ay)
        self.radius = _r
        self.clr = _clr
    
    def update(self):
        self.pos.add(self.dir)
        if (self.pos.x - 2*self.radius <= 0):
            self.pos.x = 2*self.radius
            self.dir.x *= -1
        if (self.pos.x + 2*self.radius >= width):
             self.pos.x = width - 2*self.radius
             self.dir.x *= -1
        if (self.pos.y - 2*self.radius <= 0):
            self.pos.y = 2*self.radius
            self.dir.y *= -1
        if (self.pos.y + 2*self.radius >= height):
             self.pos.y = height - 2*self.radius
             self.dir.y *= -1
    
    def show(self):
        fill(self.clr)
        circle(self.pos.x, self.pos.y, self.radius*2)
    
    def circle_collision(self, other):
        if other != self:
            distance = abs(dist(self.pos.x, self.pos.y, other.pos.x, other.pos.y))
            if distance <= self.radius + other.radius:
                self.reset()
                other.reset()
       
    def reset(self):
        self.pos = PVector(randint(2*self.radius, width - 2*self.radius),
                           randint(2*self.radius, height - 2*self.radius))
        self.dir *= -1
        self.radius = randint(MIN_R, MAX_R)
        self.clr = color(randint(20, 250), randint(20, 250), randint(20, 250), 180)

Da fast alle Aktionen in der Klasse Disk behandelt werden, ist der Hauptsketch – wie so oft – ziemlich langweilig geraten. Lediglich die doppelte Schleife

    for disk in disks:
        disk.update()
        for other_disk in disks:
            disk.circle_collision(other_disk)

verdient Beachtung. Denn alle Blasen sollen ja nicht nur ein Positions-Update erfahren, sondern sie sollen auch eine Kollisionsbehandlung durchführen. Dazu müssen sie aber sicherstellen, daß sie nicht eine Kollision mit sich selber überprüfen, sondern nur mit den anderen Blasen (other_disk). Dazu überprüft die Methode circle_collision() als ersten, daß other auch ja ungleich self ist.

Hier erst einmal der vollständige Code des Hauptsketches:

from random import randint
from disk import Disk

WIDTH = 640
HEIGHT = 480
BORDER = 50
N_DISKS = 30
SPEED = 4
MIN_R = 10
MAX_R = 25

FPS = 60

def setup():
    size(WIDTH, HEIGHT)
    this.surface.setTitle("Bubble Busters")
    bubble_factory()
    frameRate(FPS)
    
def draw():
    global disks
    background(132, 144, 163)
    for disk in disks:
        disk.update()
        for other_disk in disks:
            disk.circle_collision(other_disk)
        disk.show()

def bubble_factory():
    global disks
    disks = []
    for _ in range(N_DISKS):
        x_pos = randint(BORDER, width - BORDER)
        y_pos = randint(BORDER, height - BORDER)
        x_dir = randint(-SPEED, SPEED)
        if x_dir == 0:
            x_dir = 1
        y_dir = randint(-SPEED, SPEED)
        if y_dir == 0:
            y_dir = -1
        radius = randint(MIN_R, MAX_R)
        clr = color(randint(20, 250), randint(20, 250), randint(20, 250), 180)
        disks.append(Disk(x_pos, y_pos, x_dir, y_dir, radius, clr))

Auffällig ist höchstens noch die Funktion bubble_factory(), die ich ausgelagert habe, damit die Funktion setup() nicht so aufgeblasen scheint. Denn hier werden schlicht und einfach nur die Variablen mit Hilfe des Zufallszahlengenerators randint() erzeugt, die der Konstruktor von Disk benötigt.

Die Werte sind ziemlich artifiziell, daher habe ich sie als Konstanten im Kopf des Sketches deklariert, damit Ihr mit ihnen experimentieren könnt.

Wenn Ihr den Sketch laufen laßt, entsteht ein (für mich überraschender) psychedelischer Eindruck. Daher habe ich – wie schon bei meinen letzten Experimenten – mithilfe von Processings Movie Makers ein Video erzeugt, damit auch Nichtprogrammierer einen Eindruck von dem Ergebnis gewinnen können.

Denn Quellcode habe ich wie immer auf GitHub hochgeladen und unter die MIT-Lizenz gestellt. Macht daher mit ihm, was Ihr wollt, Hauptsache, Ihr habt Spaß dabei.


(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