1. Einsatz & Möglichkeiten von Animationen
Mit Animationen lassen sich in OMSI diverse Elemete eines Fahrzeuges oder eines Szenerieobjekts bewegen oder rotieren. Dabei kann die Geschwindigkeit, die Dauer sowie die Stärke der Animation eingestellt werden. Auch weiche Animationen sind möglich.
Animationen sind ein durchaus schweres Thema - vor allem für Anfänger in diesem Gebiet. Daher wird dieser Eintrag mit einem Beispiel begleitet.
Allgemein können nur Meshs animiert werden. Allerdings gibt es auch die Möglichkeit, Lichtpunkte, Partikelsysteme etc. zu animieren, sofern sie sich innerhalb eines Mesh-Eintrags befinden. Kollisions-Meshes können nicht animiert werden.
2. Erstellen einer Animation
Animationen werden immer unter dem zu animierenden Element in der .sco- bzw. model.cfg-Datei geschrieben.
2.1. Überblick
Syntax⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | Beschreibung |
---|---|
[newanim] | Definieren einer Animation |
origin_from_mesh | Übernahme der Objektursprungsdaten, die in der 3D-Modellierungssoftware festgelegt wurden (Standardwert, muss daher nicht geschrieben werden) |
origin_rot_x <Rotation um x-Achse> |
Drehung des Objektursprungs um die x-Achse |
origin_rot_y <Rotation um y-Achse> |
Drehung des Objektursprungs um die y-Achse |
origin_rot_z <Rotation um z-Achse> |
Drehung des Objektursprungs um die z-Achse |
origin_trans <Position x-Achse> <Position y-Achse> <Position z-Achse> |
Manuelles Festlegen des Objektursprungs |
anim_trans <Variable> <Wert in Metern> |
Bewegung des Meshs |
anim_rot <Variable> <Wert in °> |
Rotation des Meshs |
maxspeed <1/Animationsdauer> |
Optionale Geschwindigkeit / Dauer der Animation |
delay <1/Dämpfungsstart in m> |
Dämpfung der Animation |
offset <Wert in Metern> |
Start-Offset der Animation |
2.2. Einleitendes Schlüsselwort
Folgendes Schlüsselwort leitet eine Animationsdefinition ein:
Damit wird angegeben, dass nun Methoden und Werte für eine Animation folgen. Dieses muss sich immer unter dem [mesh]-Eintrag befinden, das animiert werden soll.
2.3. Bestimmung des Objektursprungs
Der Objektursprung (auch Rotationspunkt, Origin oder Pivot Point genannt) ist ein nicht-geometischer Mittelpunkt eines Objekts. Er stellt den Rotationspunkt (und in z.B. 3D-Modellierungssoftware auch Skalierungspunkt) für das Objekt dar. Normalerweise wird er in der jeweiligen Software gesetzt, man kann ihn über eine Methode der Animation aber auch verschieben.
Syntax | Details |
---|---|
origin_from_mesh |
Der Objektursprung, der in Blender (oder anderer Software) am Objekt gesetzt wurde, wird benutzt. Hierzu werden keine weiteren Werte benötigt. |
origin_rot _x <Wert in °> |
Drehung des Objektursprungs um die x-Achse um den Objektursprung Drehungen nach rechts sind positiv, Drehungen nach links negativ. |
origin_rot _y <Wert in °> |
Drehung des Objektursprungs um die y-Achse um den Objektursprung Achtung: Drehungen nach rechts sind negativ, Drehungen nach links positiv. |
origin_rot _z <Wert in °> |
Drehung des Objektursprungs um die z-Achse um den Objektursprung Drehungen nach rechts sind positiv, Drehungen nach links negativ. |
origin_trans <x-Position> <y-Position> <z-Position> |
Hiermit kann der Objektursprung manuell mit den x-, y- und z-Werten bestimmt werden (Meter-Einheit). Dabei sind auch Gleitkommawerte wie 2.413 möglich. Jedoch muss als Trennzeichen kein Komma (,), sondern einen Punkt (.) benutzt werden. |
Von diesen Möglichkeiten können auch mehrere untereinander verwendet werden.
2.4. Konstante Animationen
In OMSI laufen Animationen generell nur über x-Achse des Objektursprungs ab. Das bedeutet jedoch nicht, dass man Meshs nicht über andere Achsen animieren kann. Dazu muss man den Objektursprung bei der Animation drehen. Dazu nimmt man sich am besten ein dreidimensionales Koordinatensystem zur Veranschaulichung:
Rot: x-Achse (rechts / links)
Grün: y-Achse (vorne / hinten)
Blau: z-Achse (hoch / herunter)
Möchte man ein Objekt um n Meter nach oben (also auf der tatsächlichen z-Achse) bewegen, so ist eine Drehung der y-Achse um -90° (also nach links) nötig. Somit wäre die x-Achse des Objektursprungs auf der tatsächlichen z-Achse:
Über die folgenden Methoden kann animiert werden:
Syntax | Details |
---|---|
anim_trans <Multiplikatorvariable> <Wert in Metern> |
Bewegung des Meshs |
anim_rot |
Rotation des Meshs |
Es gibt keine Möglichkeit, Meshes über eine Animation zu skalieren.
Die eigentliche Animation wird immer von einem Skript ausgeführt, dass die Variable Var verändert.
Der Variablenwert (vom Skript gesetzt) wird immer mit dem Animationswert (Meter / Grad) multipliziert. Wird die Variable auf 1 gesetzt, so wird das Mesh um den angegebenen Wert rotiert / bewegt. Wird die Variable auf 2 gesetzt, so wird das Mesh 2 Mal um den angegebenen Wert rotiert / bewegt.
Beispiel:
- Aufnahme des Variablennamens animation_wert in eine varlist
- Bewegung des Meshs (anim_trans) um 15 Meter nach oben (Wert 15).
[newanim]
origin_rot_y // Rotation des Objektursprungs,
90 // damit die x-Achse zur z-Achse wird.
anim_trans // Bewegungsanimation.
animation_wert // Variable, die die Animation steuert.
15 // Multiplikationswert.
Wert der Variable animation_wert | Tatsächliche Bewegung |
---|---|
-1 | animation_wert × 15 = -1 × 15 = -15 |
0 | animation_wert × 15 = 0 × 15 = 0 |
1 | animation_wert × 15 = 1 × 15 = 15 |
2 | animation_wert × 15 = 2 × 15 = 30 |
5 | animation_wert × 15 = 5 × 15 = 75 |
2.4.1. Animationsdauer
Wird bei einer Animation keine Animationsdauer festgelegt, so läuft diese Animation in der Zeit eines Frames ab. Dabei ist keine wirkliche Bewegung sichtbar, sondern eher ein "Teleportieren" des Meshs von der ursprünglichen Position und ein Erscheinen an einer anderen Position.
Syntax | Details |
---|---|
maxspeed <Geschindigkeit / Dauer> |
Festlegen der Geschwindigkeit der Animation, sofern diese Methode unter einer Animationsmethode steht. Die maximale Geschwindigkeit lässt sich mit der Formel 1/x berechnen, wobei x durch die Zeit in Sekunden ersetzt werden muss. 1 = 1 Sekunde 2 = 0,5 Sekunden 4 = 0,25 Sekunden 0.5 = 2 Sekunden 0.25 = 4 Sekunden … |
2.4.2. Animationsdämpfung
Es ist möglich, Animationen zu dämpfen. Dies ist nur am Animationsende möglich.
Syntax | Details |
---|---|
delay <Dämpfungswert> |
Bestimmt, ab wie viel Metern vor Animationsende die Animation gedämpft wird. Auch hier gilt: Je größer der Wert, desto kleiner wird die Animation abgedämpft - die Formel hierfür ist ebenfalls 1/x: 1 = 1 Meter 2 = 0,5 Meter 4 = 0,25 Meter 0.5 = 2 Meter 0.25 = 4 Meter Ist der resultierende Meterwert größer als der der Animation, wird eine Maximalgeschwindigkeit nie erreicht, die Animationsgeschwindigkeit wird dann vom Anfang bis zum Ende gedämpft. |
2.4.3. Animationsoffset
Ein Offset versetzt die Startposition und ihren Verlauf um einen bestimmten Wert.
Syntax | Details |
---|---|
offset <Wert in Metern> |
Modifiziert den Startwert der Animation. Verläuft eine Animation z.B. hoch, so kann man mit einem offset-Wert von 1 den Startpunkt der Animation um einen Meter nach oben versetzen (orientiert sich ebenfalls an der Position des Objektursprungs). |
2.5. Exkurs: Funktionen
Für das folgende Kapitel, wo nicht-lineare Animationen erklärt werden, braucht es etwas Wissen im Thema der Funktionen (auch Kurven oder Curves genannt).
In OMSI ist eine Darstellung von stückweise liniearen Funktionen möglich (im folgenden nur "Funktionen" genannt). Sie werden immer in Constfile-Dateien deklariert und definiert; dementsprechend sind Funktionen in ihren einzelnen Werten immer konstant.
Eingeleitet wird eine Funktion mit folgendem Schlüsselwort:
animCurve kann / muss hierbei durch einen selbst gewählten Funktionsnamen (ohne Leerzeichen!) ersetzt werden, welcher innerhalb des Fahrzeuges / des Szenerieobjektes einzigartig sein muss.
Es gibt keine feste Regel, wo die Funktionen in den Constfile-Dateien definiert werden. Sie können oben, unten oder auch zwischen 2 Konstanten stehen.
Als Beispiel soll diese Funktion dargestellt werden:
Hierbei sind die Wertepaare: (0 | 0), (0,2 | 0,3), (0,3 | 0,4) und (1 | 1).
Um ein Wertepaar zu erstellen wird das folgende Schlüsselwort benötigt. Diese Wertepaare zu einer Funktion müssen direkt unter der jeweiligen Funktionsdeklaration befinden. Darauf folgen die beiden Wertepaare:
Bei diesen Punkten gibt der erste Wert die x-Koordinate und der zweite Wert die y-Koordinate an. Diese Koordinaten sind jedoch in keiner Weise auf Animationen bezogen, sondern auf ein 2D-Koordinatensystem.
Das gleiche wird nun für die restlichen Wertepaare erledigt. Wichtig ist, dass die Punkte nach den x-Werten aufsteigend sortiert sind. Am Ende sollte der Abschnitt zu der Funktion wie folgt aussehen:
[newcurve]
animCurve
[pnt]
0
0
[pnt]
0.2
0.3
[pnt]
0.6
0.4
[pnt]
1
1
Alles anzeigen
Eine Funktion wird dazu benutzt, um aus einem gegebenen Wert einen anderen zu erhalten.
Wird ein Wert gegeben, der kleiner als der erste oder größer als der letzte Punkt ist, so wird stets der Wert des letzten Punktes zurückgegeben. Bei dieser Funktion erhält man minimal den Wert 0 und maximal dem Wert 1.
Bezieht man dieses wieder auf das Thema dieses Eintrags und würde man einen y-Wert auf 0 setzen, dann würde die Animation an diesem Punkt stehenbleiben - der y-Wert gibt die Animationsgeschwindigkeit an.
2.6. Animationen mit Funktionen
Lässt man eine Animation mit einer Funktion ablaufen, so kann die Animation mit einer variablen Geschwindigkeit ausgeführt werden.
Dafür wird ein normaler Animationseintrag sowie eine Funktion benötigt, sowie eine constfile-Datei, welche in dem jeweiligen Szenerieobjekt / Fahrzeug eingebunden ist.
Es gibt keinen "direkten Weg", die Funktion mit der Animation zu verbinden. Dazu sind mehrere Schritte im Skript notwendig, in welchem der Animationswert an die Funktion gebunden wird.
Im folgendem wird als Beispiel ein Objekt animiert, welches um 5 Meter in die Höhe steigt. Nach 3 Sekunden sinkt es wieder um 5 Meter bzw. auf den ursprünglichen Nullpunkt. Pro Animation sollen 2 Sekunden vergehen.
Dazu wird im Objekt zunächst eine Animation definiert:
Die Variable animVar ist dabei alleinig (direkt) mit der Animation verbunden.
Das verbinden mit der Funktion mit der Variable animVar geschieht im frame-Abschnitt des Skripts:
{frame}
(L.L.Timer) (L.S.Timegap) + (S.L.Timer) s0 3 <
{if}
'Hier startet die Animation
{else}
l0 6 <
{if}
'Hier geht sie dann wieder zurück
{else}
0 (S.L.Timer)
{endif}
{endif}
{end}
Alles anzeigen
Im frame-Bereich wird diese so lange mit der Timegap-Variable addiert und anschließend als sich wieder gespeichert, solange sie kleiner als 3 ist. In dieser Zeit wird Code abgespielt. Danach wird überprüft, ob der Wert kleiner als 6 ist. Ist er das, so wird ein weiterer Code abgespielt. Trifft diese Bedingung nicht mehr zu, wird die Variable Timer zurückgesetzt (speichern der Zahl 0 in der Variable). Dieser Timer kann mit verschiedensten Zahlen so fortgeführt werden.
'Animation nach oben:
(L.L.movingTemporary) (L.S.Timegap) 0.5 * + 1 min (S.L.movingTemporary)
In diesem Abschnitt wird zuerst eine leere Variable in den Stack geladen - wenn die Animation noch nicht gestartet wurde, besitzt sie den Wert 0 bzw. ist leer.
Danach wird die Systemvariable eingelesen und mit 0.5 (Dauer von 2 Sekunden: 1/0,5 = 2) multipliziert. Letzteres ist dabei die Zahl in Sekunden, wie lange die Animation insgesamt dauern soll bzw. auf welche Dauer die Funktion gestreckt / gestaucht wird.
Nun werden die beiden oberen Stackwerte zusammengerechnet und eine 1 in den obersten Stack geladen.
Zuletzt wird mit der Skriptfunktion min der kleinere Wert der ersten beiden Stacks gewählt und in die Variable movingTemporary geladen. Der Zweck dahinter wird weiter unten bei der Zusammenführung dieses und der folgenden Skriptabschnitte erläutert.
'Animation wieder nach unten:
(L.L.movingTemporary) (L.S.Timegap) 0.5 * - 0 max (S.L.movingTemporary)
In diesem Abschnitt passiert genau das selbe wie im obigen Code - nur wird anstatt eine 1 eine 0 in den obersten Stack geladen und mit der Skriptfunktion max der größere der beiden oberen Stacks gewählt.
'Einbindung der Funktion:
(L.L.movingTemporary) (F.L.animCurve) (S.L.animVar)
Hier wird die Funktion eingebunden. Das ist die "finale" Variable, die mit dem Animationseintrag verbunden ist.
Als Funktion wird die Beispielfunktion aus dem vorherigen Kapitel genutzt.
Setzt man diese Teile nun zusammen, sollte man folgendes Ergebnis haben:
{frame}
(L.L.Timer) (L.S.Timegap) + (S.L.Timer) s0 3 <
{if}
'Animation nach oben:
(L.L.movingTemporary) (L.S.Timegap) 0.5 * + 1 min (S.L.movingTemporary)
{else}
l0 6 <
{if}
'Animation wieder nach unten:
(L.L.movingTemporary) (L.S.Timegap) 0.5 * - 0 max (S.L.movingTemporary)
{else}
0 (S.L.Timer)
{endif}
{endif}
'Einbindung der Funktion:
(L.L.movingTemporary) (F.L.animCurve) (S.L.animVar)
{end}
Alles anzeigen
- Zeile 2: Timer: Abfrage, ob weniger als 5 Sekunden (2 Sekunden Animationslaufzeit + 3 Sekunden Pause) vergangen sind
- Zeile 5: Animation nach oben: Die Variable movingTemporary wird so lange mit Timegap und der Animationsdauer addiert, bis sie kleiner gleich 1 ist. Das stellt eine Schutz dar, dass die Animation nicht höher als 1 bewegt wird. Wäre diese Variable größer als 1, so wird der kleinere der beiden Stackwerte, also genau 1 genommen.
- Zeile 8: Timer: Abfrage, ob weniger als 10 Sekunden (Zeitsumme aus Zeile 2 + 2 Sekunden Animationslaufzeit + 3 Sekunden Pause) vergangen sind
- Zeile 11: Animation nach unten: Die Variable movingTemporary wird so lange mit Timegap und der Animationsdauer subtrahiert, bis sie kleiner gleich 0 ist. Das stellt eine Schutz dar, dass die Animation nicht tiefer als 0 bewegt wird. Wäre diese Variable kleiner als 0, so wird der größere der beiden Stackwerte, also genau 0 genommen.[/tt]
- Zeile 14: Sind mehr als 6 Sekunden vergangen, wird die Timer-Variable wieder zurückgesetzt, indem der Wert 0 in diese geladen wird. Dan wiederholt sich dieser Animationsablauf.
- Zeile 19: Diese Zeile wird bei jedem Durchlauf einmal durchgangen, da sie sich in keiner If-Schleife befindet. Hier wird der Wert, welcher sich in den oberen Zeilen pro Frame langsam zu 1 erhöht, der Funktion animCurve übergeben. Der Rückgabewert dieser Funktion wird in die Variable animVar gespeichert, welche mit der Animation verbunden ist.
Das Ergebnis dieses Beispiels sieht dann so aus:
Das Beispiel kann sich auch unten im Anhang heruntergeladen werden.
3. Daueranimationen
In diesem Kapitel soll die Funktionsweise der im Fachjargon genannten "Daueranimation" behandelt werden. Bei einer Daueranimation handelt es sich rein technisch um eine Animation, die ein Mesh (z.B. eine zusätzliche Klimaanlage eines Fahrzeuges) beim Platzieren / Laden verschiebt oder rotiert (Multiplikator-Variable wird auf 1 gesetzt). Das Prinzip wird oft angewendet, wenn o3d-Dateien verwendet werden, die nicht in einer 3D-Modellierungssoftware geöffnet und auf "saubere" Art und Weise verschoben werden können.
3.1. Animationseintrag anlegen
Wie auch bei normalen Animationen, benötigt man zunächst einen Animationseintrag unter einem Mesh, der wie folgt aussehen kann:
[mesh]
Klimaanlage.o3d
[newanim]
origin_rot_z
90
anim_trans
Daueranimation
3.725
Dieser Eintrag verschiebt das Mesh um 3,725 Meter nach vorne (x-Achse).
[newanim] anim_trans Daueranimation 3.725 (keine Verdrehung) |
Bewegung auf der x-Achse (rechts oder links) |
[newanim] origin_rot_y 90 anim_trans Daueranimation 3.725 |
Bewegung auf der z-Achse (herunter / hoch) |
[newanim] origin_rot_z 90 anim_trans Daueranimation 3.725 |
Bewegung auf der y-Achse (zurück / vor) |
3.2. Variable der Animation deklarieren
Anschließend muss die Variable Daueranimation in einer varlist.txt deklariert werden. Es genügt, in einer neuen Zeile einer Varlist den Wert Daueranimation einzufügen. Bei mehreren Fahrzeugtypen in einem gemeinsamen Ordner sollte man darauf achten, dass die Varlist von der jeweiligen bus-Datei verwendet wird.
3.3. Animation auslösen
Damit die Daueranimation ausgelöst wird, muss die zuvor eingetragene Variable Daueranimation beschrieben werden. Dazu kann man in den meisten Fällen das Skript main.osc (Name ähnlich) verwendet werden, wo sich folgender Abschnitt vorfinden sollte:
'######################
' Hauptteil
'######################
{init}
(M.L.engine_Init)
(M.L.Antrieb_Init)
(M.L.Elec_Init)
(M.L.Door_Init)
(M.L.wiper_init)
(M.L.lights_init)
(M.L.bremse_init)
(M.L.cockpit_init)
(M.L.heizung_init)
(M.L.Matrix_init)
(M.L.Collision_Init)
(M.L.VDV_init)
(M.L.IBIS_init)
(M.L.ticketprinter_init)
(M.L.DTCO_init)
{end}
Alles anzeigen
Dort fügt man nun folgende Zeile ein: 1 (S.L.Daueranimation)
'######################
' Hauptteil
'######################
{init}
1 (S.L.Daueranimation)
(M.L.engine_Init)
(M.L.Antrieb_Init)
(M.L.Elec_Init)
(M.L.Door_Init)
(M.L.wiper_init)
(M.L.lights_init)
(M.L.bremse_init)
(M.L.cockpit_init)
(M.L.heizung_init)
(M.L.Matrix_init)
(M.L.Collision_Init)
(M.L.VDV_init)
(M.L.IBIS_init)
(M.L.ticketprinter_init)
(M.L.DTCO_init)
{end}
Alles anzeigen
Damit wird die erstelle Daueranimation ausgelöst. Erstellt man weitere Daueranimationen, kann die Variable Daueranimation auch dort verwendet werden. Es müssen für weitere Daueranimationen also keine weiteren Variablen deklariert oder ausgelöst werden.