2.12 COMPUTERANIMATION

 

 

EINFÜHRUNG

 

Die Computergrafik kann ähnlich wie in einem Film zur Darstellung von zeitlich veränderlichen Inhalten verwendet werden. Man nennt ein so eingesetztes Programm eine Computeranimation. Um den zeitlichen Ablauf sichtbar zu achten, wird in einer Animationsschleife immer nach einem gleich grossen Zeitschritt  ein neues Bild gezeichnet, das sich nur wenig vom vorhergehenden unterscheidet. Werden mehr als 25 Bilder pro Sekunde erzeugt, so ergibt sich für das Auge eine fliessende Bewegung.


PROGRAMMIERKONZEPTE: Computeranimation, Animationsschleife, Doppelbufferung, Sprite

 

 

FIGUREN BEWEGEN

  Das Prinzip der Animation kannst du an einer sich scheinbar bewegenden Figur ausprobieren. Dabei zeichnest du die Figur in kurzen Zeitschritten an leicht verschobenen Positionen. Die vorangehende Figur musst du immer wieder löschen.


Im ersten Beispiel lässt du einen Propeller rotieren. Für die Wiederholung verwendest du eine endlose while-Schleife. In dieser Animationsschleife zeichnest du zuerst den Propeller. Danach wartest du 100 ms. Diese Wartezeit bestimmt, wie häufig der Propeller pro Sekunde neu gezeichnet wird, also in diesem Fall rund 10 Mal. Dann löschst du den Bildschirm mit clear() und drehst die Turtle um 20 Grad, damit der nächste Propeller in leicht gedrehter Lage gezeichnet wird.

 

 


from gturtle import *

def drawFigure():
    repeat 3:
        fillToPoint(0, 0)    
        rightArc(100, 90)
        right(90)
        rightArc(100, 90)
        right(90)
        left(120)       
    
makeTurtle()
hideTurtle()
setPenColor("black")

while True:    
    drawFigure() 
    delay(100)
    clear() 
    right(20)  
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

In einer Animationsschleife zeichnest du die Figur periodisch an leicht verschobenen Positionen. Um die vorangehende Figur zu löschen, löschst du den ganzen Bildschirm.

 

 

BILDSCHIRMFLACKERN VERMEIDEN

 

Üblicherweise ist jeder Zeichnungsbefehl sofort auf dem Bildschirm sichtbar. Daher erscheint beim Löschen des Bildschirmes mit clear() kurz ein leeres Grafikfenster, was zu einem unschönen Bildschirmflackern führen kann. Um dies zu vermeiden, verwendest du die sogenannte Doppelbufferung. Du rufst den Befehl enableRepaint(False) auf, der bewirkt, dass die Grafikbefehle in einem unsichtbaren Bildbuffer ausgeführt werden und nicht mehr automatisch im Grafikfenster erscheinen. Erst wenn das neue Bild vollständig im Buffer aufgebaut ist, stellst du mit repaint() den ganzen Buffer auf einmal auf dem Bildschirm dar.

In deinem Beispiel zeigst du die Flügelbewegungen eines Vogels. Dieser besteht der Einfachheit halber aus einem runden Körper und zwei Kreisbögen, die links und rechts vom Körper gezeichnet werden. Um die Lage der Flügel einzustellen,  drehst du die Turtle vor dem Zeichnen des Flügels in die passende Richtung. Die Funktion bird(angle) hat daher einen Parameter angle, der die Turtle-Richtung vor dem Zeichnen der Flügel angibt. Dieser Winkel steigt bei der Abwärtsbewegung von 5° bis 55° und fällt bei der Aufwärtsbewegung von 55° bis 5°.

 

Für die Winkeländerung kannst du eine for-Schleife mit step = 2° verwenden.

Da du enableRepaint(False) verwendest, musst du jedesmal nach dem Zeichnen repaint() aufrufen, damit der Inhalt des Bildbuffers auf dem Bildschirm sichtbar wird. Zum Löschen des Bildschirmes verwendest du den Befehl clear("lightSkyBlue"). Dadurch erhältst du eine hellblaue Hintergrundfarbe.

from gturtle import *

def drawBird(angle):
    dot(20)
    right(angle)
    rightArc(100, 90)
    home()
    left(angle)
    leftArc(100, 90)
    home()
    
def move(start, end, step):
    for a in range(start, end, step):
        clear("lightSkyBlue")   
        drawBird(a)
        repaint()
        delay(40) 
    
makeTurtle()
hideTurtle()
setLineWidth(5)
setPenColor("black")
enableRepaint(False)

while True:
    move(55, 5, -2)
    move(5, 55, 2)    
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Der Befehl enableRepaint(False) bewirkt, dass die Grafikbefehle nicht automatisch im Turtlefenster sichtbar sind, sondern nur im Bildbuffer ausgeführt werden. Damit das neue Bild angezeigt wird, musst du aber an geeigneter Stelle repaint() aufrufen.

 

 

GLEICHMÄSSIGE BEWEGUNG

 

Die Animationsschleife sollte in möglichst gleichen Zeitticks durchlaufen werden, da sonst die Bewegung ruckelt. Die Operationen in der Animationsschleife können aber unterschiedlich viel Zeit in Anspruch nehmen, selbst wenn der Code immer derselbe ist, der Computer im Hintergrund noch mit anderen Aufgaben beschäftigt ist. Um die verschieden lange Ausführung der Animationsschleife auszugleichen, wird daher folgender Trick angewendet: Man merkt sich in der Variable startTime vor den Zeichnungsoperationen die aktuelle Systemzeit, die du als Dezimalzahl mit time.time() erhältst.

Nach dem Zeichnen wartest du in einer Warteschleife so lange, bis die Differenz der neuen Systemzeit und der Startzeit die gewünschte Animationsperiode erreicht. (Damit der Computer dabei nicht unnütz viel Rechenzeit verschwendet, hältst du das Programm mit delay(1) kurz an.) Dieser Trick funktioniert natürlich nur, falls die Zeit für das Zeichnen kürzer als die Animationsperiode ist.

In einem Ping-Pong-Spiel willst du den roten Ball möglichst gleichmässig zwischen den grünen Balken hin und her bewegen.

 


from gturtle import *
import time

def wall():
    setPenColor("green")
    setLineWidth(10)
    setPos(-200, -50)
    forward(100)
    setPos(200, -50)
    forward(100)    

makeTurtle()
hideTurtle()
enableRepaint(False)

x = -170
v = 10

while True:
    startTime = time.time()
    clear()
    wall()
    setPos(x, 0)
    setPenColor("red")
    dot(60)
    repaint()
    x = x + v
    if x > 165 or x < -165:
        v = -v      
    while (time.time() - startTime)  < 0.020:
        delay(1)  
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Ein gleichmässig bewegtes Bild erreichst du, indem du in gleichen zeitlichen Abständen die Figur an der neuen Position anzeigst. Dazu taktest du die Animation unter Verwendung der Systemzeit. Auch  in diesem Beispiel wendest du das Prinzip der Doppelbufferung an, um das Flackern zu vermeiden.

 

 

TURTLE ALS LEITFIGUR FÜR SPRITES VERWENDEN

 

Statt das Bild mit der Turtle zu zeichnen, kannst du auch ein Computerimage verwenden, das als png-, gif- oder jpg-Datei vorliegt. In der Gameprogrammierung nennt man ein solches Bild ein Sprite und das bewegte Bildschirmobjekt einen Aktor. Um ein Sprite zu verwenden, rufst du drawImage() auf und gibst dabei den Pfad zur Bilddatei an (als absoluter Dateipfad, als Dateipfad relativ zum Verzeichnis, in dem sich dein Programm befindet, oder als Internet URL).

drawImage() stellt das Bild an der Position der Turtle mit ihrer Blickrichtung dar. Du kannst also sozusagen mit der Turtle als "Leitfigur" das Bild positionieren und drehen, da sich das Sprite zusammen mit der Turtle mitbewegt. Damit man weder die Turtle noch die Spur sieht, rufst du hideTurtle() und mit penUp() auf.

Im Beispiel verwendest du das Bild car0.png eines Autos aus der TigerJython-Distribution und bewegst es auf einem Kreis. Alle im Verzeichnis sprites verfügbaren Bilder kannst du aus der TigerJython Hilfe (APLU Dokumentation)  entnehmen.

 


from gturtle import *
import time

makeTurtle()

hideTurtle()
penUp()
setPos(-180, 0)
enableRepaint(False)

while True:
    startTime = time.time()
    clear()
    drawImage("sprites/car0.png")
    repaint()
    forward(3)
    right(1)
    while (time.time() - startTime)  < 0.04:
        delay(1)    
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Bewegte Figuren, Aktoren genannt, spielen in Computergames eine zentrale Rolle. Mit der Turtle kannst das Spritebild auf dem Bildschirm bewegen und rotieren.

 

 

ANIMIERTE AKTOREN

 

Eine Turtle kann als Leitfigur sogar mehrere verschiedene Spritebilder herumbewegen. Dieses Verfahren kommt dann zum Tragen, wenn du das Spritebild selbst animieren willst. Typisch ist eine laufende Person, die aus mehreren Bewegungsformen besteht. In der Animationsschleife bewegst du einerseits die Turtle vorwärts und änderst laufend das zugehörige, vorher geladene Sprite.

Damit die Turtleposition ausserhalb des Fensters automatisch wieder auf der anderen Seite ins Fenster zurückgesetzt wird, verwendest du den Befehl wrap().

from gturtle import *
import time

makeTurtle()
hideTurtle()
penUp()
wrap()
enableRepaint(False)

right(90)
i = 0
while True:
    startTime = time.time()
    clear("lightblue")
    drawImage("sprites/person_" + str(i) + ".png")
    repaint()
    forward(10)
    i += 1
    if i == 5:
        i = 0
    while (time.time() - startTime)  < 0.1:
        delay(1)    
   
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Du kannst eine Computeranimation auf verschiedene Arten programmieren. Am einfachsten ist, es, das Bild der Turtle zu ersetzen, indem du in makeTurtle() einen Bildpfad angibst. Du kannst sodann mit Grafikbefehlen ein sich veränderndes Bild zeichnen oder schliesslich die Turtle als Leitfigur dazu verwenden, geladene Bilder zu bewegen.

Willst du eigene Spritebilder einbinden, so müssen sie in der gewünschten Pixelgrösse im png-, gif- oder jpg-Format erstellt sein. Sie sollten meinst einen transparenten Hintergrund haben, damit die rechteckige Bildumgebung nicht sichtbar ist, wenn du sie in einem Fenster mit farbigem Hintergrund verwendest oder die Bilder sich überlappen. Die Dateinamen der Bilder person_0.png, .... person_4.png werden mit der Variablen i zusammengesetzt.

 

 

AUFGABEN

 

1.

Zeichne eine Figur, die aus zwei Rauten und einem Kreis besteht und lasse sie wie ein Ventilator um den Punkt (0, 0) rotieren. Verwende enableRepaint(False) und repaint(), um das Flackern zu beseitigen.

 

2.

Zeichne eine Fahne und schwenke sie um den untersten Punkt der Fahnenstange hinauf und hinunter.

 

3.

Eine Billardkugelbewegt sich im Turtlefensters so, dass sie an den Rändern jeweils unter der Berücksichtigung der Regel Einfallswinkel = Ausfallswinkel reflektiert wird.

Anleitung: Mit heading() kannst du die aktuelle Turtlerichtung zurückzuholen und sie mit setHeading(winkel) neu setzen.

 

 

4.

Ergänze das Programm mit dem fahrenden Auto so, dass dieses mit den Cursotasten up, down, left und. right gesteuert werden kann. Als Sprite kannst du den gelben Ferrari (car3.png) aus der TigerJython-Distribution verwenden.


 

5.

Das reizende Pony soll immer wieder von rechts nach links über das Turtlefenster laufen. Verwende dazu die 8 Spritbilder pony_0.gif,...,pony_7.gif aus der TigerJython-Distribution.

Erstelle ähnliche lustige Animationn mit eigenen Bildern.



 

 

   

ZUSATZSTOFF

 

ERSTELLEN EINES VIDEO-FILMS

 

In fast allen modernen Kinofilmen werden einzelne Sequenzen oder sogar der ganze Film aus computererzeugten Bildern dargestellt. In TigerJython kannst du mit wenig Aufwand eine Animationssequenz mit kleiner bis hoher Auflösung in einer Video-Datei im hoch-komprimierten MP4-Format abspeichern. Die dabei erzeugte Video-Datei kannst du mit einem beliebigen MP4-Player abspielen, der das H.264/AVC-Format unterstützt, beispielsweise mit dem VLC Media Player, dem Quick-Time-Player oder mit Video-Apps auf praktisch allen modernen Smartphones. Du kannst sogar deinen Film im HD-Format auf einem modernen Fernsehgerät oder Beamer wiedergeben.

Die Anzahl der Bilder, die pro Sekunden abgespielt werden, ist von der gewählten Auflösung abhängig, beträgt aber üblicherweise 25 Bilder/Sekunde. Beim Erzeugen der Bilder spielt es aber keine Rolle, wie lange die Berechnung eines einzelnen Bildes mit dem Computeer dauert, du brauchst die Bilder nicht einmal auf dem Bildschirm zu rendern. Darum brauchst du dich auch nicht um Flackern und Ruckeln zu kümmern. Es liegt vollständig in der Verantwortung des Video-Players,  Bild um Bild flacker- und ruckelfrei wiederzugeben.

Im ersten Beispiel nimmt du die oben gezeigte Animation des Vogelfluges. Zuerst erzeugst du mit

rec = VideoRecorder(t, "bird.mp4", "640x480")
ein VideoRecorder-Objekt, wobei du die Turtle t übergibst, die du mit makeTurtle() erzeugst. Zudem wählst du die Ausgabedatei und die Video-Auflösung.  Am einfachsten ist es, wenn Grösse des Turtlefensters und  Video-Auflösung übereinstimmen. Du kannst die Grösse des Grafikfensters entweder in den Einstellungen von TigerJython oder mit einem Aufruf von Options wählen (nach einer Änderung musst du TigerJython neu starten).  

Die Graphikelemente (Turtles, Spuren, Bilder) des Turtlefensters kopierst du mit rec.captureImage() in die Video-Datei. Am Schluss musst du diese Datei mit rec.finish() schliessen.

from gturtle import *

def drawBird(angle):
    dot(20)
    right(angle)
    rightArc(100, 90)
    home()
    left(angle)
    leftArc(100, 90)
    home()
    
def move(start, end, step):
    for a in range(start, end, step):
        clear("lightSkyBlue")   
        drawBird(a)
        rec.captureImage()
    
Options.setPlaygroundSize(640, 480)
t = makeTurtle()
rec = VideoRecorder(t, "bird.mp4", "640x480")
hideTurtle()
setLineWidth(5)
setPenColor("black")
nbSwings = 5
n = 0
while n < nbSwings:
    move(55, 5, -2)
    move(5, 55, 2) 
    n += 1
rec.finish()
print("all done")   
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

MEMO

 

Manchmal ist es nötig, dass ein einzelnes Bild mehrmals abgespeichert wird. Willst du beispielsweise eine Sequenz von Bewegungen erstellen, die nur jede 1/5 Sekunde ändern, so muss du bei einer Bildrate von 25 Bildern/Sekunde genau 5 gleiche Bilder hintereinander abspeichern. Statt einer eigenen Wiederholschleife kannst du dies einfacher mit rec.captureImage(5) programmieren.

 

 

Manchmal ist es nötig, dass ein einzelnes Bild mehrmals abgespeichert wird. Willst du beispielsweise eine Sequenz von Bewegungen erstellen, die nur jede 1/5 Sekunde ändern, so muss du bei einer Bildrate von 25 Bildern/Sekunde genau 5 gleiche Bilder hintereinander abspeichern. Statt einer eigenen Wiederholschleife kannst du dies einfacher mit rec.captureImage(5) programmieren.

 

 

 

from gturtle import *
from random import randint

Options.setPlaygroundSize(1280, 1024)
t = makeTurtle()
hideTurtle()
clear("lightcyan")
rec = VideoRecorder(t, "painting.mp4", "1280x1024")

for i in range(50):
    x = randint(-300, 300)
    y = randint(-300, 300)
    setPos(x, y)
    r = randint(0, 255)
    g = randint(0, 255)
    b = randint(0, 255)
    c = makeColor(r, g, b)
    setPenColor(c)
    d = randint(50, 400)
    dot(d)
    rec.captureImage(5)
rec.finish()
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

Um die Szene schöner zu gestalten, kannst du auch ein Hintergrundbild verwenden. Da du mit animierten Aktoren in der Animationsschleife den Hintergrund immer wieder mit clear() löschst,  musst du auch das Hintergrundbild mit drawBkImage() immer wieder neu zeichnen. Im folgenden Beispiel wird das oben gezeigte Programm mit der wandernden Person nur leicht angepasst, um einen Film zu erzeugen.

from gturtle import *

Options.setPlaygroundSize(640, 480)
t = makeTurtle()
rec = VideoRecorder(t, "person.mp4", "640x480")
hideTurtle()
penUp()
setPos(-320, -30)
right(90)
i = 0
nbFrames = 140
n = 0
while n < nbFrames:
    clear("grey")
    drawBkImage("sprites/catbg.png")
    drawImage("sprites/person_" + str(i) + ".png")
    rec.captureImage()
    forward(5)
    i += 1
    if i == 5:
        i = 0
    n += 1    
rec.finish()
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)