Programmieren ist noch toller, wenn man damit wunderschöne Grafiken/Animation/Simulationen erstellen kann. Dafür gibt es seit langem mit Processing (Java) und p5js (JavaScript) starke Tools. Mit py5 und Strive können wir diese endlich auch in Python verwenden. Die Dokumentation für p5js findet ihr hier,
Für die Motivation: The Coding Train bietet Videos zu spannenden Projekten für p5js an. Eine Übertragung des Codes nach Python ist nicht schwierig.
Los gehts.
Öffnet Strive und seht euch das erste Beispiel an.
Analysiert folgenden Code und stellt eine Vermutung auf, wie das Ergebnis aussieht.
Überprüft eure Hypothese.
def setup():
createCanvas(500, 500)
background(240)
def draw():
fill(255, 0, 0) # Tipp: RGB-Code
rect(50, 50, 100, 100)
fill(0, 255, 0)
ellipse(300, 100, 100, 100)
fill(0, 0, 255)
triangle(150, 300, 250, 150, 350, 300)
stroke(0)
strokeWeight(3)
line(100, 400, 400, 400)
Analysiert folgenden Code und stellt eine Vermutung auf, wie das Ergebnis aussieht.
Überprüft eure Hypothese.
def setup():
createCanvas(500,500)
background("black")
def draw():
fill(255,0,255)
circle(mouseX,mouseY,50)
Zeichnet einen Schneemann.
Zeichnet das Haus vom Nikolaus.
Zeichnet ein Auto.
Erstellt ein eigenes Kunstwerk.
Die Objektorientierung wird uns im Folgenden die Implementierung unserer Projekte deutlich vereinfachen. Zur Wiederholung findet ihr hier die wichtigsten Konzepte.
Hätten wir den Schneemann, das Haus vom Nikolaus oder das Auto als Klasse implementiert, so könnten wir mit einer Zeile Code beliebig viele Objekte (Instanzen genannt) von den jeweiligen Grafiken erzeugen. Zudem steigt die Codequalität (Reminder: Kürze, Lesbarkeit, Wiederverwendbarkeit, Testbarkeit usw.).
Zu objektorientierten Modellierung fragen wir uns, welchen Eigenschaften und Methoden die Objekte unserer Software besitzen.
Als erstes Beispiel wollen wir eine Grafik erstellen, die aus 1000 bunten Kreisen besteht.

Wir überlegen uns, welche Attribute Kreise besitzen. Einfacher gefragt: worin unterscheiden sich Kreise:
Welche Methoden haben die Kreise? Noch nicht viel, aber sie können auf der Zeichenfläche dargestellt werden. Dies übernimmt die Methode
Es ergibt sich folgendes UML-Klassendiagram.

Implementiert die Klasse Kreis. Die Attribute color_r, color_g und color_b sollen bei der Erzeugung des Objekts zufällige Werte zwischen 0 und 255 erhalten. (Dafür steht ohne Import die Funktion random(maximale_zahl) zur Verfügung.)
Definiert eine Varibale graphics vom Typ Liste. In der setup-Methode soll diese mit 1000 Circle-Objekten gefüllt werden, die eine zufällige x-Koordinate, y-Koordinate und Größe haben. Die Methode draw() soll alle Kreise darstellen.
Hier die Lösung.
Um aus der Grafik eine Animation zu machen, sollen sich die Kreise bewegen.
Erweitern Sie das UML-Klassendiagramm und die Implementierung. Jeder Kreis hat eine eigene Bewegungsgeschwindigkeit in x- und in y-Richtung (zufällige Werte zwischen -5 und 5). Beim Aufruf der Methode move soll die Position entsprechend angepasst werden.
Zusatz: Ein Kreis, der das Fenster verlässt, soll auf der gegenüberliegenden Seite wieder erscheinen.
Hier die Lösung.
Um die Grafik spannender zu machen, sollen Dreiecke, Ellipsen und Rechtecke hinzugefügt werden. Alle diese Objekte haben viele Gemeinsamkeiten und unterscheiden sich nur in der Darstellung. Hier bietet sich die Vererbung für die Implementierung an.
Erweitern Sie das UML-Klassendiagramm. Benennen Sie dazu die Klasse Kreis in Graphic um und erzeugen Sie dien Klassen Dreieck, Ellipse und Rechteck, die von ihr erben.
Implementieren Sie die neuen Klassen.
Implementieren Sie ein Kunstwerk, dass aus 300 Kreisen, Ellipsen und Rechtecken besteht.
Hier die Lösung.
Hier ein sehr einfaches Auto. Es besteht nur aus einem Rechteck.

Und die Implementierung
class Auto():
# Construtor
def __init__(self, pos_x, pos_y):
# Füge Attribute hinzu
self.pos_x = pos_x
self.pos_y = pos_y
def draw(self):
rect(self.pos_x, self.pos_y, 100, 50)
def move(self):
self.pos_x += 1
auto = Auto(0, 20)
def setup():
createCanvas(200, 200)
def draw():
background(200)
auto.draw()
auto.move()
Spannender wird es, wenn das Auto fahren lernt.
Erweitert das UML-Klassendiagramm. Das Auto soll durch die Methoden accelerate und brake seine Geschwindigkeit ändern können. Zudem soll jedes Auto eine eigene Breite (width) und Höhe (height) erhalten.
Implementiert die erweiterte Klasse. Nutzt den folgenden Code-Snipsel, um in der draw()-Methode auf Tastatureingaben zu reagieren.
if keyIsDown(RIGHT_ARROW):
auto.accelerate()
elif keyIsDown(LEFT_ARROW):
auto.brake()
Hier die Lösung.
Hier unser (noch sehr schlecht dokumentierter) Code aus dem Unterricht zu Pong.
class Ball():
def __init__(self):
self.diameter = 20
self.dx = int(random(-5,5))
self.dy = int(random(-5,5))
self.set_middle_position()
def draw(self):
circle(self.pos_x, self.pos_y, self.diameter)
def move(self):
self.pos_x += self.dx
self.pos_y += self.dy
# redirect, if touches upper or lower wall
if self.pos_y <= 0 or self.pos_y >= height:
self.dy *= -1
def set_middle_position(self):
self.pos_x = int(width / 2)
self.pos_y = int(height / 2)
class Paddle():
def __init__(self, pos_x):
self.width = 20
self.height = 80
# movement speed in y-direction
self.dy = 0
self.pos_x = pos_x
# position in the middle of the field
self.pos_y = int(height / 2 - self.height / 2)
def moveUp(self):
self.pos_y -= 20
def moveDown(self):
self.pos_y += 20
def draw(self):
rect(self.pos_x, self.pos_y, self.width, self.height)
# Create objects
paddle1 = Paddle(20)
paddle2 = Paddle(width - 20 - paddle1.width)
ball = Ball()
points_player1 = 0
points_player2 = 0
# react to key events
def keyPressed(keyEvent):
if key == "w":
paddle1.moveUp()
elif key == "s":
paddle1.moveDown()
def setup():
createCanvas(300, 300)
def draw():
background(200)
# move right paddle automatic to ball position ("ai")
paddle2.pos_y = ball.pos_y - int(paddle2.width/2)
# draw objects
paddle1.draw()
paddle2.draw()
# check paddle collision
ball_leftside_pos_x = ball.pos_x - ball.diameter / 2
ball_rightside_pos_x = ball.pos_x + ball.diameter / 2
# touch left paddle
if ball_leftside_pos_x <= paddle1.pos_x + paddle1.width and ball_leftside_pos_x >= paddle1.pos_x and ball.pos_y >= paddle1.pos_y and ball.pos_y <= paddle1.pos_y + paddle1.height:
# new direction
ball.dx = int(random(1,5))
ball.dy = int(random(-5,5))
fill("red")
# touch left paddle
elif ball_rightside_pos_x >= paddle2.pos_x and ball.pos_y >= paddle2.pos_y and ball.pos_y <= paddle2.pos_y + paddle2.height:
ball.dx = int(random(-1,-5))
ball.dy = int(random(-5,5))
fill("red")
ball.move()
ball.draw()
fill("white")
# if ball left field -> points for players
if ball_leftside_pos_x < 0:
global points_player1
points_player1 += 1
ball.set_middle_position()
elif ball_rightside_pos_x > width:
global points_player2
points_player2 += 1
ball.set_middle_position()
# draw points
textSize(20);
text(str(points_player1) + " : " + str(points_player2),120,20)
class GameObject:
def __init__(self):
self.x = random(400)
self.y = random(400)
self.diameter = random(20)
self.dx = random(-10,10)
self.dy = random(-10,10)
def draw(self):
pass
def move(self):
self.y = self.y + self.dy
self.x = self.x + self.dx
if self.x > 400:
self.x = 0
elif self.x < 0:
self.x = 400
if self.y > 400:
self.y = 0
elif self.y < 0:
self.y = 400
class Ball(GameObject):
def draw(self):
fill(random(255),random(255),random(255))
ellipse(self.x, self.y, self.diameter, self.diameter)
class Square(GameObject):
def draw(self):
fill(random(255),random(255),random(255))
square(self.x, self.y, self.diameter)
game_objects = []
def setup():
createCanvas(400, 400)
for _ in range(100):
game_objects.append(Ball())
game_objects.append(Square())
def draw():
background(200)
for go in game_objects:
go.draw()
go.move()
def haus(pos_x, pos_y):
translate(pos_x, pos_y)
fill(161, 35, 43)
# Dach
triangle(10, 150, 70, 150, 40, 100)
fill(226, 114, 91)
# Haus
square(10, 150, 60)
# Schornstein
quad(60, 125, 65, 125, 65, 142, 60, 134)
fill("brown")
# Tür
quad(40, 180, 50, 180, 50, 200, 40, 200)
fill(52, 129, 184)
# Fesnter
quad(25, 160, 35, 160, 35, 175, 25, 175)
fill(100)
# Rauch
diameter = sin(frameCount / 60) * 2 + 5
ellipse(63, 120, diameter, diameter)
ellipse(66, 115, diameter, diameter)
ellipse(69, 110, diameter, diameter)
translate(-pos_x, -pos_y)
def setup():
createCanvas(300,220)
def draw():
background(200)
for i in range(0,200,70):
haus(i, 0)