
Grafiken rotieren um Mittelpunkt
Animationen mit Python erstellen
Im letzten Kapitel haben wir uns das „einbauen“ von Grafiken und Bildern in Python über Pygame angesehen. Jetzt wollen wir mehr Spaß mit den Grafiken haben und diese rotieren lassen. Darüber lässt sich bereits einiges an Animation umsetzen. Besonders wichtig ist der Rotationspunkt … dazu mehr Schritt für Schritt.
Das im Teaser gezeigte Flugzeug wollen wir in Einzelteile zerlegen – genaugenommen in 2 Einzelteile: Den Propeller (der rotieren soll) und den Rest (also Rumpf mit Flügel und Fahrwerk).
Die beiden Grafiken können über die jeweilige URL heruntergeladen werden oder einfach mit der Maus auf die Grafik klicken und „speichern unter“ wählen:
https://www.python-lernen.de/bilder/propeller.png
https://www.python-lernen.de/bilder/propellerflieger.png
Zum Einbauen der 2 Grafiken nutzen wir unseren bisherigen Code aus dem letzten Kapitel:
# Importieren u. initialisieren der Pygame-Bibliothek
import pygame
from pygame.locals import *
pygame.init()
# Variablen/KONSTANTEN setzen
W, H = 800, 600
FPS = 60
SCHWARZ = ( 0, 0, 0)
WEISS = ( 255, 255, 255)
GRAU = ( 155, 155, 155)
spielaktiv = True
spielerfigur = pygame.image.load("bilder/biene.png").convert_alpha()
bildgroessen = spielerfigur.get_rect()
# Definieren und Öffnen eines neuen Fensters
fenster = pygame.display.set_mode((W, H))
pygame.display.set_caption("Grafiken nutzen")
clock = pygame.time.Clock()
# Schleife Hauptprogramm
while spielaktiv:
# Überprüfen, ob Nutzer eine Aktion durchgeführt hat
for event in pygame.event.get():
# Beenden bei [ESC] oder [X]
if event.type==QUIT or (event.type==KEYDOWN and event.key==K_ESCAPE):
spielaktiv = False
# Spiellogik
# Spielfeld löschen
fenster.fill(GRAU)
# Spielfeld/figuren zeichnen
fenster.blit(spielerfigur, (200, 100))
# Fenster aktualisieren
pygame.display.flip()
clock.tick(FPS)
Anstelle unserer Biene aus dem Kapitel Space Invaders war gestern – Varroa Invaders importieren wir nun unsere 2 Grafiken und vergeben die Namen propeller
und flugzeugrumpf
:
Folgender Code wird an den entsprechenden Stellen in unserem Python-Programm eingebaut:
propeller = pygame.image.load("bilder/propeller.png")
flugzeugrumpf = pygame.image.load("bilder/propellerflieger.png")
Und zum Platzieren unseres Fliegers:
# Spielfeld/figuren zeichnen
fenster.blit(flugzeugrumpf, (0, 0))
fenster.blit(propeller, (0, 0))
Allerdings haben wir noch einen grauen Himmel – wir sind aber Schönwetterflieger und wollen deshalb einen schönen blauen Himmel!
Also die Hintergrundfarbe auf Hellblau ändern. Meine benutzte Farbe hat den Wert: HIMMELBLAU = (120, 210, 255)

Bitte umsetzen, bevor man in den folgenden Code schaut.
Unser bisheriger Code:
# Importieren u. initialisieren der Pygame-Bibliothek
import pygame
from pygame.locals import *
pygame.init()
# Variablen/KONSTANTEN setzen
W, H = 800, 600
FPS = 60
SCHWARZ = ( 0, 0, 0)
WEISS = (255, 255, 255)
GRAU = (155, 155, 155)
HIMMELBLAU = (120, 210, 255)
spielaktiv = True
propeller = pygame.image.load("bilder/propeller.png")
flugzeugrumpf = pygame.image.load("bilder/propellerflieger.png")
# Definieren und Öffnen eines neuen Fensters
fenster = pygame.display.set_mode((W, H))
pygame.display.set_caption("www.Python-lernen.de - Grafiken rotieren und skalieren")
clock = pygame.time.Clock()
# Schleife Hauptprogramm
while spielaktiv:
# Überprüfen, ob Nutzer eine Aktion durchgeführt hat
for event in pygame.event.get():
# Beenden bei [ESC] oder [X]
if event.type==QUIT or (event.type==KEYDOWN and event.key==K_ESCAPE):
spielaktiv = False
# Spiellogik
# Spielfeld löschen
fenster.fill(HIMMELBLAU)
# Spielfeld/figuren zeichnen
fenster.blit(flugzeugrumpf, (0, 0))
fenster.blit(propeller, (0, 0))
# Fenster aktualisieren
pygame.display.flip()
clock.tick(FPS)
Grafiken an gewünschten Punkt platzieren
Unsere Grafiken kleben nun im Eck bei x=0 und y=0. Allerdings braucht unser Propeller nach oben Platz und sollte auf der Flugzeugnase sitzen.
Unsere Werte entsprechend ändern auf:
# Spielfeld/figuren zeichnen
fenster.blit(flugzeugrumpf, (20, 30))
fenster.blit(propeller, (118, 32))
Nun sitzt der Propeller am gewünschten Fleck auf der Flugzeugnase:

Grafik rotieren mit Pygame
Pygame biete einen Befehl zum Rotieren von Grafiken. Dieser rotiert eine Grafik gegen den Uhrzeigersinn.
Bei dem Rotationsbefehl gibt man in der Klammer das zu rotierende Ursprungsbild an und den Winkel (ohne Größenangabe).
Wir erzeugen eine zweite Grafik, um das Verständnis bei Rotationen schnell zu bekommen:
propeller2 = pygame.transform.rotate(propeller, 20)
Nach dem Platzieren passt da allerdings etwas nicht bzw. ist nicht wie erwartet:
# Spielfeld/figuren zeichnen
fenster.blit(flugzeugrumpf, (20, 30))
fenster.blit(propeller, (118, 32))
fenster.blit(propeller2, (118, 32))

Unser Propeller rotiert nicht wie gewünscht. Der rote Punkt (der aus didaktischen Gründen rot ist und sofort sichtbar) ist der Mittelpunkt unseres Propellers. Nach unserer ersten Rotation scheint der Propeller vom Flieger abzufallen. Nichts was man bei einem gemütlichen Flug gerne hätte.
Was ist passiert?
Die Rotation hat nicht um das Zentrum unserer Grafik stattgefunden. Zum Testen erstellen wir uns eine Funktion mit dem Namen rotieren_zentrieren()
, der wir neben der X und Y-Angabe noch unser Bild und den Rotationsgrad angeben:
def rotieren(x, y, bild, gradangabe):
rotiert = pygame.transform.rotate(bild, gradangabe)
fenster.blit(rotiert, (x, y))
Im Hauptprogramm beim Spiel erweitern wir um den Aufruf der Funktion rotieren()
anstelle von fenster.blit(propeller2, (118, 32))
# Spielfeld/figuren zeichnen
fenster.blit(flugzeugrumpf, (20, 30))
fenster.blit(propeller, (118, 32))
# fenster.blit(propeller2, (118, 32))
rotieren(118, 32, propeller, 20)
Damit wir besser sehen, in welchem Bereich die Berechnung erfolgt, zeichnen wir ein rotes Viereck um die aktuelle Stellung des Propellers. Dies geschieht in unserer neuen Funktion:
def rotieren(x, y, bild, gradangabe):
rotiert = pygame.transform.rotate(bild, gradangabe)
fenster.blit(rotiert, (x, y))
# Viereck zeichnen - erst Größe ermitteln
viereck = rotiert.get_rect()
pygame.draw.rect(fenster, (200, 0, 0), (x, y, viereck.width, viereck.height), 1)
Wenn wir die Propeller in der Grundstellung ohne Rotation mit Viereck zeichnen lassen und dann rotiert, sieht man schön das nach unten rechts wachsendem Quadrat:
rotieren(118, 32, propeller, 0)
rotieren(118, 32, propeller, 20)
Für die Rotation wird also der Nullpunkt links oben gesetzt – daher „wächst“ das Quadrat nach rechts und unten und unser Propeller eiert durch die Gegend.

Bild gegen den Uhrzeigersinn permanent rotieren lassen
Für unsere Bewegung des Propellers erweitern wir den Bereich „# Spiellogik“ um die Rotation. Wir lassen eine Zahl von 0 bis 359 pro Durchgang ansteigen. Überschreitet die Zahl unseren Kreis mit 360 Grad, fängt diese wieder bei 0 an.
# Spiellogik
if gradzahl <= 359:
gradzahl += 5
else:
gradzahl = 0
Zur Erinnerung: Unsere Rotation erfolgt gegen den Uhrzeigersinn. Wollte man nun eine Rotation im Uhrzeigersinn, würde man von 359 nach 0 zählen lassen.
Unsere Variable gradzahl
können wir nun unserer Rotationsfunktion mitgeben.
# Spielfeld/figuren zeichnen
fenster.blit(flugzeugrumpf, (20, 30))
rotieren(118, 32, propeller, gradzahl)
Grafiken um Mittelpunkt rotieren
Wir wollen also, dass unsere Grafik um den Mittelpunkt rotiert – bei unserem Propeller sitzt dieser exakt auf dem roten Punkt.
Daher erstellen wir uns im nächsten Schritt eine Funktion mit dem Namen rotieren_zentrieren()
.
Auch dieser Funktion wird neben der X und Y-Angabe noch unser Bild und den Rotationsgrad mitgegeben:
def rotieren_zentrieren(x, y, bild, gradangabe):
# rotieren und in einem neuen "surface" speichern
rotiert = pygame.transform.rotate(bild, gradangabe)
# Bestimmen der neuen Abmessungen (nach Rotation ändern sich diese!)
groesse = rotiert.get_rect()
# Ausgabe
fenster.blit(rotiert, (x - groesse.center[0],y - groesse.center[1]))
Wenn man sich den Programmcode anschaut, sieht man den Trick. Es wird nach der Rotation die Größen der Grafik bestimmt und dann diese mittig ausgegeben. Über center[0] und center[1] haben wir den Mittelpunkt.

Und als Animation:

Man sieht schön anhand des Vierecks der alten Rotationsfunktion, dass die Rotation exakt auf dem Nullpunkt der x- und y-Angabe sitzt. Diese verschieben wir später noch.
Jetzt lassen wir uns einfach zur Kontrolle noch ein Viereck ausgeben:
def rotieren_zentrieren(x, y, bild, gradangabe):
# rotieren und in einem neuen "surface" speichern
rotiert = pygame.transform.rotate(bild, gradangabe)
# Bestimmen der neuen Abmessungen (nach Rotation ändern sich diese!)
groesse = rotiert.get_rect()
# Ausgabe
fenster.blit(rotiert, (x - groesse.center[0],y - groesse.center[1]))
pygame.draw.rect(fenster, (255, 255, 255), (x - groesse.center[0], y - groesse.center[1], groesse.width, groesse.height), 1)
Wir wissen, dass unser Propeller eine Breite von 100 und eine Höhe von 63 hat. Also geben wir die beim Aufruf die Hälfe jeweils mit (die Zahl 118 und 32 stammt aus dem ursprünglichen Platzieren des Flugzeugrumpfs):
rotieren_zentrieren(118+50, 32+31, propeller, gradzahl)

Somit haben wir eine saubere Rotation um den Mittelpunkt (was man dann doch öfters benötigt) und haben alle Möglichkeiten von Rotationen von Grafiken über Pygame und Python kennengelernt.
Hier der komplette Quellcode zur Sicherheit:
# Importieren u. initialisieren der Pygame-Bibliothek
import pygame
from pygame.locals import *
pygame.init()
# Variablen/KONSTANTEN setzen
W, H = 800, 600
W_HALBE = W / 2
H_HALBE = H / 2
FPS = 60
SCHWARZ = ( 0, 0, 0)
WEISS = (255, 255, 255)
GRAU = (155, 155, 155)
HIMMELBLAU = (120, 210, 255)
spielaktiv = True
gradzahl = 0
propeller = pygame.image.load("bilder/propeller.png")
flugzeugrumpf = pygame.image.load("bilder/propellerflieger.png")
# Definieren und Öffnen eines neuen Fensters
fenster = pygame.display.set_mode((W, H))
pygame.display.set_caption("www.Python-lernen.de - Grafiken rotieren")
clock = pygame.time.Clock()
# Funktion zum Grafiken rotieren und zentrieren
# def rotieren_zentrieren(ausgabe, x, y, bild, gradangabe):
def rotieren_zentrieren(x, y, bild, gradangabe):
# rotieren und in einem neuen "surface" speichern
rotiert = pygame.transform.rotate(bild, gradangabe)
# Bestimmen der neuen Abmessungen (nach Rotation ändern sich diese!)
groesse = rotiert.get_rect()
# Ausgabe
fenster.blit(rotiert, (x - groesse.center[0],y - groesse.center[1]))
# Viereck zur Kontrolle zeichnen
pygame.draw.rect(fenster, (255, 255, 255), (x - groesse.center[0], y - groesse.center[1], groesse.width, groesse.height), 1)
def rotieren(x, y, bild, gradangabe):
rotiert = pygame.transform.rotate(bild, gradangabe)
fenster.blit(rotiert, (x, y))
# Viereck zeichnen - erst Größe ermitteln
viereck = rotiert.get_rect()
pygame.draw.rect(fenster, (200, 0, 0), (x, y, viereck.width, viereck.height), 1)
# Schleife Hauptprogramm
while spielaktiv:
# Überprüfen, ob Nutzer eine Aktion durchgeführt hat
for event in pygame.event.get():
# Beenden bei [ESC] oder [X]
if event.type==QUIT or (event.type==KEYDOWN and event.key==K_ESCAPE):
spielaktiv = False
# Spiellogik
if gradzahl <= 359:
gradzahl += 5
else:
gradzahl = 0
# Spielfeld löschen
fenster.fill(HIMMELBLAU)
# Spielfeld/figuren zeichnen
fenster.blit(flugzeugrumpf, (20, 30))
# rotieren(118, 32, propeller, gradzahl)
rotieren_zentrieren(118+50, 32+31, propeller, gradzahl)
# Fenster aktualisieren
pygame.display.flip()
clock.tick(FPS)