index

Übersicht: Python & Blender


Lernziel

The Berlin-Uhr(R) wurde von Dieter Binninger erfunden. Sie wurde 1975 am Kurfürstendamm in Berlin-Charlottenburg nähe Uhlandstraße aufgestellt. Die Uhr arbeitet nach dem Prinzip eines Stellenwertsystems und ist die erste ihrer Art. Zeit wird durch Lampen angezeigt. Die oberste Lampe leuchtet bzw. erlischt im Sekundentakt. In der zweiten Reihe werden die Stunden mit jeweils 5 Stunden/Lampe angezeigt. Die zweite Reihe zeigt jeweils eine Stunde an. Wenn z. B. zwei Lampen in der ersten Reihe und eine in der zweiten Reihe leuchten ist es 13:00 Uhr (5+5+1). In der dritten Reihe mit den kleineren Lampen werden die Minuten mit ebenfalls 5 Minuten/Lampe angezeigt. Die dritte sechste und neunte Lampe markieren die Viertelstunden. In der letzten Reihe zeigt jede Lampe wie bei den Stunden jeweils eine Minute an.

Handlungsanweisungen

Aufgaben:
  1. Eine ausführliche Beschreibung findet man unter:

    http://www.surveyor.in-berlin.de/berlin/uhr/indexe.html

  2. Beginne Deine eigene Uhr zu bauen.

  3. Verwende drei Farben wie in der Origaluhr verwendet.

  4. Füge eine Welt und einen Himmel zur Szene hinzu. Verwende eine Funktion aus dem Kapitel »Blender erweitert, Kamera trac to Empty«

  5. Versuche eine Weiterentwicklung, wie mit den Bonus-Versionen vorgeschlagen.

Die Elemente der Uhr positionieren

Wir werden die Uhr nicht originalgetreu nachbauen, wie im Bild gezeigt, sondern mit einer abstrakten Version beginnen. Dafür reicht uns erst einmal ein Grundbaustein aus Blender, der Cube.

Für die Anordnung verwenden wir ein Array, in der jede Hashmarke (#) eine Lampe repräsentiert.

use-cases/informatics/berlin-clock/berlin_clock_001.py (Source)

1
2
3
4
5
clock = ["#",
         "####",
         "####",
         "##########",
         "####"]

Nun kann mit dieser Struktur, für jede Hashmarke ein Würfel erzeugt werden. Dafür verwenden wir eine for-Schleife und die jeweiligen index-Werte im Array.

use-cases/informatics/berlin-clock/berlin_clock_001.py (Source)

    rows = len(object)

    for row in range(rows):
        cols = len(object[row])
        for col in range(cols):
            bpy.ops.mesh.primitive_cube_add(location=(row, col, 0))

if __name__ == '__main__':
    bpy.ops.object.select_by_type(type='MESH')
    bpy.ops.object.delete()

Teste diese erste Version... (Arbeitsschritte):

  • starte mit einer neuen Blender-Datei
  • wechsle die Ansicht von Default auf Scripting
  • leg ein neues Script mit Namen "berlin_clock.py" an
  • drücke Alt+P
  • begutachte das Ergebnis und analysiere die Fehler siehe auch:
    • Blender in der Konsole starten (Linux, MacOS)
    • Menü »System Console« (Windows)
/use-cases/informatics/berlin-clock/berlin-clock10.png

Abstände korrigieren

Die Abstände zwischen den einzelnen Objekten sollen nun justiert werden. Dafür verwenden wir eine Liste mit Konstanten, die einen Wert für jeweils eine Zeile enthält. Nach der Berechung werden die Werte für x und y zugewiesen, z interessiert uns nicht und bleibt immer auf 0.

use-cases/informatics/berlin-clock/berlin_clock_002.py (Source)

1
2
3
4
5
6
7
8
    distances = [9.5, 3.75, 3.75, 1.6, 3.75]
    for row in range(rows):
        cols = len(object[row])
        for col in range(cols):
            distance = distances[row]
            x = row * 3
            y = col * distance + distance
            bpy.ops.mesh.primitive_cube_add(location=(x, y, 0))

Finale Anpassung der Größen

Um kürzere Zeilen zu erzeugen (pep8) berechnen wir die Werte in extra Zeilen und verwenden danach die Ergebnisse. So ändern wir die Größe der Objekte für die Minuten.

/use-cases/informatics/berlin-clock/berlin-clock20.png

use-cases/informatics/berlin-clock/berlin_clock_003.py (Source)

            if row == 3:
                bpy.ops.transform.resize(value=(0.5, 0.5, 0.5))

Namen für die Objekte

Später wollen wir die Farbe der Objekte ändern, deshalb ist es eine gute Idee, eindeutige Namen zu verwenden. Im ersten Schritt ersetzen wir die Hashmarken durch eindeutige Namen.

use-cases/informatics/berlin-clock/berlin_clock_004.py (Source)

1
2
3
4
5
6
7
clock = [["seconds"],
         ["hour_01", "hour_02", "hour_03", "hour_04"],
         ["hour_05", "hour_06", "hour_07", "hour_08"],
         ["minute_01", "minute_02", "minute_03", "minute_04"] +
         ["minute_05", "minute_06", "minute_07", "minute_08"] +
         ["minute_09", "minute_10", "minute_11"],
         ["minute_12", "minute_13", "minute_14", "minute_15"]]

Nun können wir, nachdem das Objekt erstellt worden ist auch den Namen an das Objekt vergeben.

use-cases/informatics/berlin-clock/berlin_clock_004.py (Source)

            obj = bpy.context.object
            obj.name = clock[row][col]

Wo kann ich die Änderungen sehen? In der rechten oberen Ecke findet man den Outliner der alle Objekte einer Szene auflistet und auch die Namen anzeigt.

/use-cases/informatics/berlin-clock/berlin-clock40.png

Material und Farbe

Für die Zeitanzeige werden Farben verwendet. Für die Farbänderung wird eine spezielle Funktion verwendet, die es später noch erlaubt, weitere Parameter zu setzen bzw. zu ändern.

use-cases/informatics/berlin-clock/berlin_clock_005.py (Source)

1
2
3
4
def setColor(obj, material, color):
    material.diffuse_color = color
    material.specular_hardness = 200
    obj.data.materials.append(material)

Eine Farbe ist immer auch eine Eigenschaft eines Materials, deshalb definieren wir ein Material.

use-cases/informatics/berlin-clock/berlin_clock_005.py (Source)

MATERIAL_001 = bpy.data.materials.new('Mat001')

Danach definieren wir eine Farbe (im Moment einheitlich für alle Objekte).

use-cases/informatics/berlin-clock/berlin_clock_005.py (Source)

            setColor(obj, MATERIAL_001, (1, 0, 1))
/use-cases/informatics/berlin-clock/berlin-clock50.png

Objekte für die aktuelle Zeit färben

Um die korrekte Zeitanzeige realisieren zu können, brauchen wir die aktuelle Zeit. Dafür erstellen und verwenden wir eine Funktion timing aus einem neuen Modul mit dem gleichen Namen also timing.py. Dort holen wir uns ein datetime-Objekt und rufen die einzelnen Teile, Stunden, Minuten und Sekunden ab. Gespeichert werden die Werte in einem Dictionary mit dem Wahrheitswert True bzw. False. Mit print(dir(t)) werden alle möglichen Bestandteile eines datetime-Objektes ausgegeben.

use-cases/informatics/berlin-clock/timing.py (Source)

"""
Name: 'berlin-clock.py'
Blender: 27x
Group: 'Example'
Tooltip: 'Creating the berlin-clock'
"""
import datetime


def timing():
    '''
    extrakt all parts of a timeobject
    '''
    result = {}
    t = datetime.datetime.now()

    # seconds
    result["seconds"] = t.second % 2 == 0

    # 5-hour parts
    result["hour_01"] = (t.hour > 4)
    result["hour_02"] = (t.hour > 9)
    result["hour_03"] = (t.hour > 14)
    result["hour_04"] = (t.hour > 19)

    # 1-hour parts
    h = t.hour % 5
    result["hour_05"] = (h > 0)
    result["hour_06"] = (h > 1)
    result["hour_07"] = (h > 2)
    result["hour_08"] = (h > 3)

    # 5-minute parts
    result["minute_01"] = (t.minute > 4)
    result["minute_02"] = (t.minute > 9)
    result["minute_03"] = (t.minute > 14)
    result["minute_04"] = (t.minute > 19)
    result["minute_05"] = (t.minute > 24)
    result["minute_06"] = (t.minute > 29)
    result["minute_07"] = (t.minute > 34)
    result["minute_08"] = (t.minute > 39)
    result["minute_09"] = (t.minute > 44)
    result["minute_10"] = (t.minute > 49)
    result["minute_11"] = (t.minute > 54)

    # 1-minute parts
    m = t.minute % 5
    result["minute_12"] = (m > 0)
    result["minute_13"] = (m > 1)
    result["minute_14"] = (m > 2)
    result["minute_15"] = (m > 3)

    return result


if __name__ == '__main__':
    timing()

Zurück zum Hauptskript importieren und verwenden wir die Funktion. Zuerst der Import:

use-cases/informatics/berlin-clock/berlin_clock_006.py (Source)

from timing import timing

Gebraucht wird auch die timing-Funktion und das Szene-Objekt. Für jeden Wert im Dictionary wird die Farbe gesetzt.

use-cases/informatics/berlin-clock/berlin_clock_006.py (Source)

1
2
3
4
5
6
7
    res = timing()
    scn = bpy.context.scene
    for name in res.keys():
        scn.objects.active = scn.objects[name]
        obj = bpy.context.object
        if res[name]:
            setColor(obj, MATERIAL_001, (1, 0, 1))

Test der neuen Funktionalität

  • lade dazu beide Skripte (timing.py and berlin-clock.py) in den Skriptbereich.
  • wechsle zum Skript: berlin-clock.py und starte das Script (Alt+P)
/use-cases/informatics/berlin-clock/berlin-clock60.png

Die Uhr in der Game-Engine

Damit die Uhr nicht nur mit der Run-Funktion aktualisiert wird, wechseln wir für die Ausführung zur Game-Engine. Die Konfiguration erfolgt über den Logic-Editor der Game-Engine und ein neues Skript update.py. Damit kann die »«Unruhe« der Uhr die Zeit in Sekundentakt aktualisieren. Hier das Script:

use-cases/informatics/berlin-clock/update001.py (Source)

from bge import logic
from timing import timing

res = timing()

# get the scene
scene = logic.getCurrentScene()

# coall the objects in the scene
for name in res.keys():
    scene.objects[name].color = (1, 0, 1, .7) \
                                if res[name] else (1, .5, 1, 1)

Letzter Schritt: das Script mit der BGE (Blender Game Engine) verbinden.

  • die Kamera auswählen (re. Mausklick)
  • den Fenstertyp von "Python console" zum "Logic Editor" umstellen
  • hinzufügen: Always sensor
  • hinzufügen Python controller
  • das Skript update.py dem controller zuordnen
  • sensor und controller verbinden (drag & drop)
  • einstellen: Level triggering auf True
  • umstellen: Game Engine im Menü (siehe Blender Render)
  • Optional das 3D-Window maximieren (Ctrl+Pfeil)
  • Start der Uhr (game) mit dem Menü oder der Taste P im 3D-Fenster
/use-cases/informatics/berlin-clock/berlin-clock70.png

Bonus I

Zufällige Farben mit jedem Neustart der Uhr:

use-cases/informatics/berlin-clock/berlin_clock_008.py (Source)

1
2
3
4
5
6
color_a = random.uniform(0.3, 1)
color_b = random.uniform(0.3, 1)
color_c = random.uniform(0.3, 1)
color_d = random.uniform(0.3, 1)
color_e = random.uniform(0.3, 1)
color_f = random.uniform(0.3, 1)

Jede Farbe setzt sich aus Anteilen der Farben Rot, Grün und Blau zusammen. Mit dem Zufallsmodul random können die Bestandteile der Uhr zufällig gefärbt werden. Hier das erweiterte Script:

use-cases/informatics/berlin-clock/update002.py (Source)

from bge import logic
from timing import timing

from berlin_clock_008 import color_a
from berlin_clock_008 import color_b
from berlin_clock_008 import color_c
from berlin_clock_008 import color_d
from berlin_clock_008 import color_e
from berlin_clock_008 import color_f

res = timing()

# get the scene
scene = logic.getCurrentScene()

# coall the objects in the scene
for name in res.keys():
    scene.objects[name].color = (color_a, color_b, color_c, .7) \
                                if res[name] else (color_d,
                                                   color_e,
                                                   color_f, 1)

Kommentare