Text: in Bewegung

Übersicht: Python & Blender


Lernziel

Jeder Vor- oder Abspann eines Films zeigt Text zu den Machern des Films. Oft ist es ein sich langsam bewegendes Band, seltener eine Animation. Wir zeigen hier eine interessante Variante für die Animation von Text.

Handlungsanweisungen

Aufgaben:
  1. Erstelle aus einem eigenen Text einzelne Text-Objekte für jeden Buchstaben.
  2. Variiere die im Beispiel demonstrierten Zufalls-Kombinationen
  3. Gib dem Text ein Material.
  4. Rendere die Animation.

Text Animieren

Text kann, wie jedes Objekt in Blender auf die unterschiedlichste Art & Weise animiert werden. Für ganze Worte und Textzeilen ist das kein Problem. Für einzelne Buchstaben ist der Aufwand schon etwas größer, deshalb soll ein Python-Script uns helfen, schneller zum Ziel zu kommen.

Wir stellen hier die Bewegung einzelner Buchstaben zu einer vordefinierten Endposition vor, an der der Text in Ruhe gelesen werden kann. Der Weg der Buchstaben zu Endposition soll von einem »zufälligen« Startpunkt aus erfolgen. Das per Hand zu steuern, wäre etwas aufwendig, deshalb lohn sich die Entwicklung eines Python-Skripts, das uns schneller zum Ziel führt und viel Raum für Varianten und Experimente gibt.

Das Skript enthält vier Funktionen:

  • Aus einem Text werden Einzelbuchstaben erzeugt und provisorisch an der Endposition platziert.
  • Die zweite Funktion speichert die End-Positionen in einem Dictionary.
  • Die dritte Funktion erzeugt per Zufall die Start-Positionen und speichert die Werte ebenfalls in einem Dictionary
  • Die vierte Funktion setzt die Keyframes für Start und Ende einer jeden Buchstaben-Bewegung, damit kann dann die Animation erzeugt werden.

Die Vorbereitungen:

  • die Kamera soll in der Draufsicht von oben nach unten auf die x- und y-Achse gerichtet sein.
    • Dazu wähle die Kamera aus. Aktiviere mit »N« (der Mauszeiger befindet sich im 3D-Fenster) das Eigenschaften-Panel.
    • Setze dort für Location und Rotation alle Werte auf »0«
    • Zum Schluss setze für den Z-Wert der "Location" eine 7 ein.
    • Wechsle dann in die Kamera-Perspektive (0 auf der Numerischen Tastatur)

Das Startskript:

Umbau der Vorlage:

blender-moves/text-move/script000.py (Source)

 1 #!bpy
 2 """
 3 Name: 'Flying Letters'
 4 Blender: 2.7x
 5 Group: 'Animation'
 6 Tooltip: 'Helperscripts to animate letters'
 7 """
 8 import bpy
 9 
10 
11 def textToSingleLetters(text="Hallo", x_start=0, y_start=0):
12     """ A given text line is divided in single letters (objects). """
13     pass
14 
15 
16 def defineEndPosition(d=None):
17     """ Register endposition of all text objects. """
18     pass
19 
20 
21 def calcStartPosition(d=None):
22     """ Calculate random start position of all text objects. """
23     pass
24 
25 
26 def setkeyframes(d=None):
27     """ One line describing the task of this function  """
28     pass
29 
30 
31 if __name__ == '__main__':
32     pass

Wir haben nun die vier wichtigsten Funktionen benannt, das Skript vorbereitet und die Dokumentation angepasst. Nun kann die Umsetzung des ersten Arbeitschrittes beginnen.

Textobjekte erzeugen

blender-moves/text-move/script001.py (Source)

 1 #!bpy
 2 """
 3 Name: 'Flying Letters'
 4 Blender: 2.7x
 5 Group: 'Animation'
 6 Tooltip: 'Helperscripts to animate letters'
 7 """
 8 import bpy
 9 
10 
11 def textToSingleLetters(text="Hallo", x_start=0, y_start=0):
12     """ A given text line is divided in single letters (objects). """
13 
14     counter = 1
15     kerning = .1
16     pos_next_x = 0
17     for letter in text:
18         name = "row_%s_%s" % (y_start, counter)
19         myFontCurve = bpy.data.curves.new(type="FONT", name=name)
20         myFontOb = bpy.data.objects.new(name, myFontCurve)
21         myFontOb.location = (x_start + pos_next_x, y_start, 0)
22         myFontOb.data.body = letter
23         bpy.context.scene.objects.link(myFontOb)
24         bpy.context.scene.update()
25         counter += 1
26         pos_next_x = pos_next_x + myFontOb.dimensions.x + kerning
27 
28 
29 def defineEndPosition(d=None):
30     """ Register endposition of all text objects. """
31     pass
32 
33 
34 def calcStartPosition(d=None):
35     """ Calculate random start position of all text objects. """
36     pass
37 
38 
39 def setkeyframes(d=None):
40     """ One line describing the task of this function  """
41     pass
42 
43 
44 if __name__ == '__main__':
45 
46     bpy.ops.object.select_by_type(type='FONT')
47     bpy.ops.object.delete()
48     textToSingleLetters("Python", y_start=-1, x_start=-2)
49     textToSingleLetters("Blender", y_start=0, x_start=-2)
50     textToSingleLetters("Blend4Web", y_start=1, x_start=-2)

Was passiert hier im einzelnen:

Zeile(n): Anmerkung
46-47: Alle vorhandenen Font-Objekte werden gelöscht...
48-50: drei Zeilen werden verarbeitet...
14-16: Startwerte werden vorbereitet
17: Für jeden Buchstaben in der Zeile, wiederholen wir:
18: Ein Name wird konstruiert
20-23: Ein Buchstabe wird als Objekt angelegt
24: die Szene wird aktualisiert und der Buchstabe damit sichtbar
25-26: die Parameter für den nächsten Buchstaben werden berechnet

Sollten die Abstände nicht gefallen, kann manuell nachjustiert werden oder man entwickelt ein Mapping, das für jeden Buchstaben ein eigenes Kerning [2] definiert. In diesem Tutorial verzichten wir auf diese Feinheit...

Koordinaten speichern

blender-moves/text-move/script002.py (Source)

 1 #!bpy
 2 """
 3 Name: 'Flying Letters'
 4 Blender: 2.7x
 5 Group: 'Animation'
 6 Tooltip: 'Helperscripts to animate letters'
 7 """
 8 import bpy
 9 import random
10 
11 def textToSingleLetters(text="Hallo", x_start=0, y_start=0):
12     """ A given text line is divided in single letters (objects). """
13     counter = 1
14     kerning = .1
15     pos_next_x = 0
16     for letter in text:
17         name = "row_%s_%s" % (y_start, counter)
18         myFontCurve = bpy.data.curves.new(type="FONT", name=name)
19         myFontOb = bpy.data.objects.new(name, myFontCurve)
20         myFontOb.location = (x_start + pos_next_x, y_start, 0)       
21         myFontOb.data.body = letter
22         bpy.context.scene.objects.link(myFontOb)
23         bpy.context.scene.update()
24         counter += 1
25         pos_next_x = pos_next_x + myFontOb.dimensions.x + kerning
26  
27 def defineEndPosition():
28     """ Register endposition of all text objects. 
29 
30         This is the Position of each letter after creation.
31     """
32     data = {}
33     all = [item.name for item in bpy.data.objects]
34     for name in all:
35         if name[0:3] == "row":
36             obj = bpy.data.objects[name]
37             data[name] = [(obj.location.x,
38                            obj.location.y,
39                            obj.location.z)]
40     return data
41 
42 def calcStartPosition(d=None):
43     """ Calculate random start position of all text objects. """
44 
45     all = [item.name for item in bpy.data.objects]
46     for name in all:
47         if name[0:3] == "row":                
48             obj = bpy.data.objects[name]
49             randX = round(random.uniform(-4, 4),3)
50             randY = round(random.uniform(10, 30),3)
51             randZ = round(random.uniform(10, 30),3)
52             d[name].append((randX, randY, randZ))
53     return d
54 
55 def calcStartEndFrames(d=None, startFrame=10, endFrame=190):
56     """ Calculate random startframe for each object. """
57     pass
58 
59 
60 def setkeyframes(d=None):
61     """ One line describing the task of this function  """
62     pass
63 
64 if __name__ == '__main__':
65     bpy.ops.object.select_by_type(type='FONT')
66     bpy.ops.object.delete()
67     textToSingleLetters("Python", y_start=-1, x_start=-2)
68     textToSingleLetters("Blender", y_start=0, x_start=-2)
69     textToSingleLetters("Blend4Web", y_start=1, x_start=-2)
70 
71     d = defineEndPosition()
72     d = calcStartPosition(d)
73     print(d)
Zeile(n): Anmerkung
27-40: Die eben platzierten Buchstaben-Objekte haben Ihre Endpostion und wir lesen die x-, y- und z-Koordinaten aus. Im Dictionary d gespeichert, geben wir die Werte zurück
42-53: Die Startposition muss nun festgelegt werden. Die Werte lassen wir durch den Zufall erzeugen. Weil die vielen Nachkommastelln nicht benötigt werden, wird abschließend noch gerundet, bevor die Werte auch im Daten-Dictionary gespeichert werden.
71-72: hier werden die Funktionen aufgrufen...
73: Eine Ausgabe zeigt den aktuellen Inhalt des Dictionary.

Start und Ende der Animation

In welchem Frame (Bild) ein Objekt zu finden ist, wird durch einen Key-Frame festgelegt. Wenn für ein Objekt zwei Key-Frames definiert sind, berechnet Blender für eine Animation die Position zwischen den Key-Frames. Die Start- und Ende-Position wird wiederum durch den Zufall bestimmt. Die Werte sollten im Bereich der gesamtlänge der Animation liegen.

blender-moves/text-move/script003.py (Source)

  1 #!bpy
  2 """
  3 Name: 'Flying Letters'
  4 Blender: 2.7x
  5 Group: 'Animation'
  6 Tooltip: 'Helperscripts to animate letters'
  7 """
  8 import bpy
  9 import random
 10 
 11 def textToSingleLetters(text="Hallo", x_start=0, y_start=0):
 12     """ A given text line is divided in single letters (objects). """
 13     counter = 1
 14     kerning = .1
 15     pos_next_x = 0
 16     for letter in text:
 17         name = "row_%s_%s" % (y_start, counter)
 18         myFontCurve = bpy.data.curves.new(type="FONT", name=name)
 19         myFontOb = bpy.data.objects.new(name, myFontCurve)
 20         myFontOb.location = (x_start + pos_next_x, y_start, 0)       
 21         myFontOb.data.body = letter
 22         bpy.context.scene.objects.link(myFontOb)
 23         bpy.context.scene.update()
 24         counter += 1
 25         pos_next_x = pos_next_x + myFontOb.dimensions.x + kerning
 26  
 27 def defineEndPosition():
 28     """ Register endposition of all text objects. 
 29 
 30         This is the Position of each letter after creation.
 31     """
 32     data = {}
 33     all = [item.name for item in bpy.data.objects]
 34     for name in all:
 35         if name[0:3] == "row":
 36             obj = bpy.data.objects[name]
 37             data[name] = [(obj.location.x,
 38                            obj.location.y,
 39                            obj.location.z)]
 40     return data
 41 
 42 def calcStartPosition(d=None):
 43     """ Calculate random start position of all text objects. """
 44 
 45     all = [item.name for item in bpy.data.objects]
 46     for name in all:
 47         if name[0:3] == "row":                
 48             obj = bpy.data.objects[name]
 49             randX = round(random.uniform(-4, 4),3)
 50             randY = round(random.uniform(10, 30),3)
 51             randZ = round(random.uniform(10, 30),3)
 52             d[name].append((randX, randY, randZ))
 53     return d
 54 
 55 def calcStartEndFrames(d=None, startFrame=10, endFrame=190):
 56     """ Calculate random startframe for each object. """
 57 
 58     all = [item.name for item in bpy.data.objects]
 59     for name in all:
 60         if name[0:3] == "row":
 61             x1 = int(random.uniform(10, 190))
 62             x2 = int(random.uniform(10, 190))
 63             if x1 > x2:
 64                 startFrame = x2
 65                 endFrame = x1
 66             else:
 67                 startFrame = x1
 68                 endFrame = x2
 69                 
 70             obj = bpy.data.objects[name]
 71             d[name].append(startFrame)
 72             d[name].append(endFrame)
 73 
 74     return d
 75 
 76 def setkeyframes(d=None):
 77     """ One line describing the task of this function  """
 78     all = [item.name for item in bpy.data.objects]
 79     for name in all:
 80         if name[0:3] == "row":
 81             obj = bpy.data.objects[name]
 82             bpy.context.scene.objects.active = obj
 83             obj.location = d[name][0]
 84             obj.keyframe_insert(data_path='location', frame=d[name][3])
 85             obj.location = d[name][1]
 86             obj.keyframe_insert(data_path='location', frame=d[name][2])
 87            
 88 if __name__ == '__main__':
 89     bpy.ops.object.select_by_type(type='FONT')
 90     bpy.ops.object.delete()
 91     textToSingleLetters("Python", y_start=-1, x_start=-2)
 92     textToSingleLetters("Blender", y_start=0, x_start=-2)
 93     textToSingleLetters("Blend4Web", y_start=1, x_start=-2)
 94 
 95     d = defineEndPosition()
 96     d = calcStartPosition(d)
 97     d = calcStartEndFrames(d)
 98     setkeyframes(d)
 99     #print(d)
Zeile(n): Anmerkung
55-54: Die Start- und Ende-Frames berechnen.
63-68: Damit der Start-Wert größer als der Ende-Wert ist, wird noch ein Vergleich durchgeführt.
72-72: Beide Werte werden im Dictionary gespeichert.
76-86: Alle Werte aus dem Dictionary werden nun verwendet um die Start- und Ende-Postionen der Objekte zu definieren.

Animation testen

Die Animation kann nun getestet werden. Wenn alles zufriedenstellend läuft, kann daraus ein Film erstellt werden. Aber das ist schon wieder ein anderes Thema ...

[2] Als Kerning bezeichnet in der Typografie den Vorgang, den horizontalen Abstand (den Weißraum) zwischen mehreren Buchstaben (Standarddickte) durch optischen Ausgleich so zu verringern, dass er gleichmäßig erscheint und so vom Betrachter als angenehmer empfunden wird. (Quelle: https://de.wikipedia.org/wiki/Unterschneidung_%28Typografie%29)

Comments