Python Tutorial
- jetzt Python programmieren lernen

Grafiken rotieren mit Pygame und Python

Grafiken rotieren (auch um Mittelpunkt)

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:

Propeller für unsere Beispiel
https://www.python-lernen.de/bilder/propeller.png

Propellerflieger ohne Propeller für unsere Beispiel
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 letzten 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 genutzte Farbe hat die Wert: HIMMELBLAU = (120, 210, 255)

Flieger und Propeller als getrennte Grafikelemente
Flieger und Propeller als getrennte Grafikelemente

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:

Propeller proper positioniert
Propeller proper positioniert

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 die zu rotierende Ursprungsbild an und den Winkel (ohne Größenangabe).

Wir erzeugen eine zweite Grafik um das Verständnis bei Rotationen schnell zu erhalten:

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))
rotiert nicht wie gewünscht
rotiert nicht wie gewünscht

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 Propellerstellung. 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.

Propeller wird um die linke obere Ecke rotiert
Propeller wird um die linke obere Ecke rotiert

Bild gegen den Urzeigersinn permanent rotieren lassen

Für unsere Bewegung des Propellers erweitern wir den Bereich „# Spiellogik“ um die Rotation. Sprich 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.

Propeller rotiert um Mittelpunkt
Propeller rotiert um Mittelpunkt

Und als Animation:

Animation Propeller zentriert und mit falschen Nullpunkt
Animation Propeller zentriert und mit falschen Nullpunkt

Man sieht schön anhand des Vierecks des 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)
Rotation des Propellers um dessen Mittelpunkt
Rotation des Propellers um dessen Mittelpunkt

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 kennen gelernt.

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)