Python exemplarisch
deutsch     english

ANIMATIONEN



 

Prinzip der Computeranimation (senkrechter Wurf)

   

Mit einer Computeranimation können zeitlich veränderliche Inhalte dargestellt werden. Im ersten Beispiel wird ein vertikaler Wurf ohne Luftwiederstand mit einer Echtzeit-Simulation animiert dargestellt. Ausgehend von der Startgeschwindigkeit v = 20 m/s und der Beschleunigung g = 9.81, wird in Zeitintervallen dt aus der aktuellen Geschwindigkeit die neue Geschwindigkeit v = v - g * dt und aus dem aktuellen Ort der neue Ort y = y + v * dt berechnet und der Gegenstand als als Figur im Grafikfenster dargestellt.

 

Das Prinzip der Animation:
  • Man zeichnet die Figur an der ersten Position und zeigt das Bild währen des Zeitintervalls dt an (hier 0.1 s)
  • Dann berechnet man die neue Position, löscht den ganzen Bildschirm, zeichnet die Figur an der neuen Position und zeigt das neue Bild während des Zeitintervalls dt an
  • Der Schritt 2 wird in einer Animationsschleife wiederholt
 

Programm: [►]

#AnimEx1.py

from gpanel import *
import time

makeGPanel(-6, 6, -220, 70)
setColor("red")
enableRepaint(False)

g = 9.81
dt= 0.05

t = 0; y = 0
v = 25 

while t < 10: 
    v = v - g * dt 
    y = y + v * dt 
    t = t + dt
    drawGrid(-5, 5, -200, 50, "gray")
    pos(0, y)  
    fillCircle(0.3)
    repaint()  
    time.sleep(dt)
    clear()   
keep()
Progammcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

  Erklärungen zum Programmcode
 

Für ein ruckelfreie Computeranimation ist es wichtig, dass das Zeichnen in einem Bildbuffer erfolgt und das Bild erst nachher als Ganzes auf dem Bildschirm dargestellt ("gerendert") wird. Mit enableRepaint(False) wird das automatische Rendern deaktiviert und mit repaint() gerendert, nachdem das Bild vollständig aufgebaut ist.

 

 

Bilder animieren (schiefer Wurf)

 

Anstelle von selbst gezeichneten Figuren kann man Bilder im gif-, oder png-Format verwenden.

img = getImage("sprites/football.gif")
lädt das Bild von der Disk.
image(img, x, y)
zeigt das Bild an der Position (x, y) an.

 

Im Beispiel wird ein schiefer Wurf (ohne Luftwiderstand) mit einem Fussball mit einer Animation dargestellt.

Programm: [►]

#AminEx2.py

from gpanel import *
import time
from math import sin, cos, radians

makeGPanel(Size(800, 400))
window(-1, 100, -1, 40)
bgColor("darkgreen")
enableRepaint(False)

g = 9.81
dt = 0.1
v = 32 
alpha = 50

t = 0; x = 0; y = 0
vx = v * math.cos(radians(alpha)) 
vy = v * math.sin(radians(alpha))

img = getImage("sprites/football.gif")
while t < 10: 
    vy = vy - g * dt 
    x = x + vx * dt
    y = y + vy * dt 
    image(img, x, y)
    repaint()
    t = t + dt        
    time.sleep(dt) 
    clear()   
keep()
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

  Erklärungen zum Programmcode
 

Das Laden des Bildes mit img = getImage("sprites/football.git") sollte nicht in der Animationsschleife erfolgen, sondern vorher. In der while-Schleife wird das bereits geladene Bild nur angezeigt. Am einfachsten speichert man die Bilddatei football.gif im gleichen Verzeichnis, in dem sich das Programm befindet, oder in einem Unterverzeichnis davon (hier sprites). Falls man mit der TigerJython-IDE arbeitet, können Bilder direkt aus der eingebauten Bildbibliothek verwendet werden.

 

 

Zwei Grafik-Fenster verwenden

 

Bei den Simulationsexperimenten ist es oft vorteilhaft das zeitliche Verhalten und die Animation gleichzeitig zu beobachten. Im folgenden Beispiel wird die gedämpte harmonische Schwingung mit zwei Grafikfenstern dargestellt. Das linke Fenster stellt das Koordinaten-Zeit Diagramm dar. Im rechten Fenster bewegt sich eine Kugel so, als ob sie an einer Feder oder einem elastischen Faden angebracht wäre.

Programm: [►]

from gpanel import *
import time

dt = 0.01     # Zeitschritt (s)
m = 0.5       # Masse (kg)
k = 4         # Federkonstante (N/kg)
r = 0.1       # Reibungskoeffizient in N/m/s
t = 0; y = 0.8; v = 0 # Anfangsbedingungen

p = GPanel(-1, 11, -1.2, 1.2)
drawPanelGrid(p, 0, 10.0, -1.0, 1.0, "gray")
# p.drawGrid(0, 10.0, -1.0, 1.0, "gray") # python
pSim = GPanel(-1, 1, -1, 1)
pSim.enableRepaint(False)
img = pSim.getImage("sprites/marble.png") 
s = pSim.toWindowWidth(31) # Pixel radius of ball
p.pos(t, y) 
p.lineWidth(2)
while True:
    p.draw(t, y) 
    pSim.clear()
    F = -k*y - r*v   
    a = F/m          
    v = v + a*dt     
    y = y + v*dt     
    pSim.bgColor("yellow") # tigerjython
    # pSim.setBgColor("yellow") # python
    pSim.line(0, 1, 0, y)
    pSim.image(img, - s, y-s) # tigerjython
    # pSim.showImage(img, - s, y-s) # python
    pSim.repaint()
    t = t + dt
    time.sleep(dt)    
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

  Erklärungen zum Programmcode
 

Die beiden Grafikfenster werden als Objekte p und pSim der Klasse GPanel erzeugt. Alle Funktionsaufrufe erhalten das Präfix p bzw. pSim. mit nachfolgendem Punktoperator. Die Syntax für einige Funktionen ist im TigerJython und Standard-Python nicht ganz identisch. Im oben stehenden Programmcode ist die TigerJython-Version aktiviert.

 

 

Mehrere Figuren gleichzeitig animieren

 

30 Aliens erscheinen an zufällig gewählten Positionen im Grafikfenster mit einem hellblauen Hintergrund und bewegen sich nach unten. Ihre Koordinaten werden in der Liste sky gespeichert.

Die neuen Koordinaten werden für alle Aliens in der Animationsschleife berechnet. Man geht die Liste sky durch verkleinert für jedes Element die y-Koordinate um 1. Gleichzeitig wird überprüft, ob ein Alien im Grafikfenster noch sichtbar ist.

Die neuen Koordinaten werden in der Liste skyTemp gespeichert.

 

Sobald die Umrechnung für alle Elemente abgeschlossen ist, wird die Liste sky durch skyTemp ersetzt, das ganze Bild gelöscht und alle Aliens an der neuen Position gezeichnet.

Um das Flackern zu vermeiden, wird auch hier das automatische Rendern deaktiviert und erst nachdem das Bild vollständig aufgebaut ist, mit repaint() gerendert.

Programm: [►]

#AnimEx4.py

from gpanel import *
from random import randint

def drawSky():    
    for (x, y) in sky:     
        move(x, y)
        image(img, x, y)  
                                
makeGPanel(0, 600, 0, 600) 
bgColor("deepskyblue") 
sky = []
img = getImage("sprites/alien.png")  
enableRepaint(False)

for i in range(30):
    x = randint (10, 590)
    y = randint (-20, 620)  
    sky.append((x, y))
    
while True:
    clear()
    drawSky()
    repaint()
    skyTemp = []
    for (x, y) in sky:
        if y < -40:
            y = 620
        else:    
            y -= 1
        skyTemp.append((x, y))
    sky = skyTemp    
    delay(25)
Progammcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

  Erklärungen zum Programmcode
 

if y < -40: überprüft, ob ein Alien im Grafikfenster noch sichtbar ist. Im Prinzip würde die Überprüfung y < 0 genügen, dann verschwinden die Figuren aber vorzeitig, wenn sie mit ihrem unteren Rand die Koordinate y = 0 erreicht haben. Da mit clear() das ganze Grafikfenster gelöscht wird, muss das Hintergrundbild in der While-Schleife jeweils neu anzezeigt werden.

Auch in diesem Beispiel ist es vorteilhaft, das Bild nur einmal mit img = getImage("alien.png") von der Disk zu laden und dann mit image(img, x, y) wiederholt anzeigen. Falls Sie mit der TigerJython-IDE arbeiten, so können Sie die Bilder direkt aus der eingebauten Bildbibliothek verwenden. Sonst können Sie alien.png und town.jpeg hier downloaden und im gleichen Verzeichnis, in dem sich Ihr Programmbefindet, oder in einem Unterverzeichnis davon speichern.

 

 

Mehrere Objekte animiert bewegen

 

Weihnachtssterne in verschiedenen Farben werden an zufällig gewählten Positionen erzeugt und bewegen sich rotierend nach unten.

Es ist naheliegend, einen Stern als ein Objekt (eine Instanz einer Klasse Star) mit Eigenschaften und Verhalten zu definieren. Der Stern wird mit zwei gespiegelten gleichseitigen Dreiecken  mit je der Seitenlänge size konstruiert, die im Konstruktor als Punktlisten festgelegt werden. Die Eigenschaft Füllfarbe wird mit der Methode setColor(color) festgelegt.

 
Mit der Methode rotate(angle) werden die zwei Dreiecke um angle (im Gegenuhrzeigersinn) gedreht, aber erst der Aufruf der Funktion draw(position) stellt den Stern im GPanel an der Stelle position mit seinen aktuellen Eigenschaften (Grösse, Farbe, Richtung) dar.

 

Programm:

# Star.py

from gpanel import *
from math import sqrt, radians, sin, cos

class Star():
    def __init__(self, size):
        self.size = size
        self.color = "yellow"
        self.direction = 0
        self.triangle1 = [[-sqrt(3)/2 * self.size, -self.size/2],
                     [sqrt(3)/2 * self.size, -self.size/2],
                     [0, self.size]]
        self.triangle2 = [[-sqrt(3)/2 * self.size, self.size/2],
                     [sqrt(3)/2 * self.size, self.size/2],
                     [0, -self.size]]
 
    def setColor(self, color):
        self.color = color

    def getDirection(self):
        return self.direction

    def rotate(self, angle):
        self.direction += angle
        self.triangle1 = self._rotatePolygon(self.triangle1, angle)  
        self.triangle2 = self._rotatePolygon(self.triangle2, angle)    
        
    def draw(self, position):
        setColor(self.color)
        fillPolygon(self._translatePolygon(self.triangle1, 
                    position[0], position[1]))
        fillPolygon(self._translatePolygon(self.triangle2, 
                    position[0], position[1]))
                  
    def _translatePolygon(self, polygon, x, y):
        translatedPolygon = []
        for corner in polygon:
            translatedPolygon.append([corner[0] + x, corner[1] + y])
        return translatedPolygon

    def _rotatePolygon(self, polygon, theta):
        theta = radians(theta)
        rotatedPolygon = []
        for corner in polygon :
            rotatedPolygon.append(
            [corner[0] * cos(theta) - corner[1] * sin(theta), 
             corner[0] * sin(theta) + corner[1] * cos(theta)])
        return rotatedPolygon                 
Progammcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

  Erklärungen zum Programmcode
 

In _rotatePolynom(polygon, theta) werden alle Ecken eines gegebenen Polygons um denselben Winkel theta bezüglich des Ursprungs des Koordinatensystems gedreht (Anwendung der 2-D Rotationsmatrix). Die neue Eckpunktliste wird zurück gegeben.

In _translatePolygon(polygon, x, y)  werden alle Ecken des gegebenen Polygons um x, y verschoben und die neue Eckpunktliste zurück gegeben.

 

 

Im Hauptprogramm werden mehrere Listen verwaltet: stars enthält die Sternobjekte, positions die aktuellen Positionen, rotIncrement die Veränderung der Richtungen pro Simulationsschritt (positiv oder negativ).

In der Simulationsschleife wird zuerst (im Bildbuffer) das ganze Bild gelöscht und dann werden die neuen Sterne gezeichnet. Nachfolgend werden in der Hilfsliste positionTemp die nach unten geschobenen Positionen gespeichert und anschliessend die Listenreferenz position = positionTemp zugewiesen. Mit repaint() wird der Bildbuffer gerendert und das Programm um den Zeitschritt dt angehalten.


 

Programm:

# AnimEx5.py

from Star import Star
from gpanel import *
import time
from random import randint, random

dt = 0.01
makeGPanel(0, 100, 0, 100)
bgColor("blue")
clear()
nbStars = 50
stars = [0] * nbStars
for n in range(nbStars):
    stars[n] = Star(randint(3, 8))
for n in range(nbStars):
    stars[n].setColor(makeColor(randint(0, 255), 
            randint(0, 255),
        randint(0, 255)))
positions = [0] * nbStars
for n in range(nbStars):
    positions[n] = [randint(0, 100), randint(0, 100)]
rotIncrement = [0] * nbStars
for n in range(nbStars):
    rotIncrement[n] = (-2 + 4 * random())
enableRepaint(False)
while True:
    clear()
    positionTemp = []
    for n in range(nbStars):
        stars[n].rotate(rotIncrement[n])
        stars[n].draw(positions[n])
        if positions[n][1] < -5:
            positions[n][1] = 105
        else:    
            positions[n][1] -= 0.1
        positionTemp.append([positions[n][0], 
            positions[n][1]])
    position = positionTemp    
    repaint()
    time.sleep(dt)
Progammcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

  Erklärungen zum Programmcode
 

Mit from Star import Star wird die Klasse Star aus dem Modul Star.py in den Namensraum geholt. Das Modul muss sich im gleichen Verzeichnis wie das Hauptprogramm befinden.