Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Verwendung des AnimationTree

Einführung

Mit AnimationPlayer verfügt Godot über eines der flexibelsten Animationssysteme, die man in einer Game Engine finden kann. Die Fähigkeit, fast jede Property in jedem Node oder jeder Ressource zu animieren, sowie dedizierte Transform-, Bezier-, Funktionsaufruf-, Audio- und Sub-Animations-Tracks zu haben, ist ziemlich einzigartig.

Die Unterstützung für Blending dieser Animationen mittels AnimationPlayer ist jedoch relativ begrenzt, da nur eine feste Crossfade-Zeit eingestellt werden kann.

AnimationTree ist ein neuer Node, der in Godot 3.1 eingeführt wurde, um fortgeschrittene Übergänge zu behandeln. Er ersetzt den alten AnimationTreePlayer und bietet gleichzeitig eine große Menge an Funktionen und Flexibilität.

Erstellen eines AnimationTree

Bevor wir beginnen, muß klargestellt werden, daß ein AnimationTree-Node keine eigenen Animationen enthält. Stattdessen verwendet er Animationen, die in einem AnimationPlayer-Node enthalten sind. Auf diese Weise können Sie Ihre Animationen wie gewohnt bearbeiten (oder sie aus einer 3D-Szene importieren) und dann diesen zusätzlichen Node zur Steuerung der Wiedergabe verwenden.

Die gebräuchlichste Art, AnimationTree zu verwenden, ist in einer 3D-Szene. Wenn Sie Ihre Szenen aus einem 3D-Austauschformat importieren, werden sie normalerweise mit integrierten Animationen geliefert (entweder mehrere oder von einer großen Szene beim Import getrennt). Am Ende wird die importierte Godot-Szene die Animationen in einem AnimationPlayer Node enthalten.

Da man importierte Szenen in Godot selten direkt verwendet (sie werden entweder instanziiert oder geerbt), kann man den AnimationTree-Node in der neuen Szene platzieren, in der die importierte Szene vorkommt. Anschließend zeigen Sie den AnimationTree-Node auf den AnimationPlayer, der in der importierten Szene erstellt wurde.

Als Referenz hier noch einmal wie es in der Third Person Shooter Demo gemacht wird:

../../_images/animtree1.png

Eine neue Szene wurde für den Spieler mit einem CharacterBody3D als Root erstellt. Innerhalb dieser Szene wurde die ursprüngliche .dae (Collada) Datei instanziiert und ein AnimationTree-Node erstellt.

Erstellen eines Baums

Es gibt drei Haupttypen von Nodes, die in AnimationTree verwendet werden können:

  1. Animation-Nodes, die auf eine Animation aus dem verknüpften AnimationPlayer verweisen.

  2. Animations-Root-Nodes, die zum Blending von Unter-Nodes verwendet werden.

  3. Animation Blend-Nodes, die innerhalb des AnimationNodeBlendTree als Single-Graph-Blending über mehrere Eingangsports verwendet werden.

Um einen Root-Node in AnimationTree festzulegen, sind einige Typen verfügbar:

../../_images/animtree2.png
  • AnimationNodeAnimation: Wählt eine Animation aus der Liste aus und spielt sie ab. Dies ist der einfachste Root-Node und wird im Allgemeinen nicht direkt als Root verwendet.

  • AnimationNodeBlendTree: Enthält viele Nodes vom Typ blend, z. B. mix, blend2, blend3, one shot usw. Dies ist einer der am häufigsten verwendeten Nodes.

  • AnimationNodeStateMachine: Enthält mehrere Root-Nodes als untergeordnete Nodes in einem Diagramm. Jeder Node wird als Zustand verwendet und bietet mehrere Funktionen, um zwischen den Zuständen zu wechseln.

  • AnimationNodeBlendSpace2D: Ermöglicht die Platzierung von Root-Nodes in einem 2D-Blending-Space. Steuern Sie die Blending-Position in 2D, um zwischen mehreren Animationen zu überblenden.

  • AnimationNodeBlendSpace1D: Vereinfachte Version des oben genannten (1D).

Blending-Baum

Ein AnimationNodeBlendTree kann sowohl Root- als auch reguläre Nodes enthalten, die zum Blending verwendet werden. Nodes werden dem Graphen über ein Menü hinzugefügt:

../../_images/animtree3.webp

Alle Blending-Bäume enthalten standardmäßig einen Output-Node, an den etwas angeschlossen werden muß, damit die Animationen abgespielt werden können.

Der einfachste Weg diese Funktionalität zu testen besteht darin, einen Animation-Node direkt mit ihm zu verbinden:

../../_images/animtree4.png

Damit wird die Animation einfach abgespielt. Stellen Sie sicher, dass der AnimationTree aktiv ist, damit tatsächlich etwas passiert.

Es folgt eine kurze Beschreibung der verfügbaren Nodes:

Blend2 / Blend3

Diese Nodes überblenden zwischen zwei oder drei Eingängen um einen vom Benutzer festgelegten Blending-Wert:

../../_images/animtree5.gif

Für komplexeres Blending wird empfohlen, stattdessen Blending-Spaces zu verwenden.

Beim Blending können auch Filter verwendet werden, d. h. Sie können individuell steuern, welche Tracks die Blending-Funktion durchlaufen. Dies ist sehr nützlich, um Animationen übereinander zu schichten.

../../_images/animtree6.png

OneShot

Dieser Node führt eine Sub-Animation aus und kehrt zurück, sobald sie beendet ist. Die Blending-Zeiten für das Ein- und Ausblenden können ebenso wie die Filter angepasst werden.

../../_images/animtree6b.gif

Nach dem Setzen der Anforderung und dem Wechsel der Animationswiedergabe, löscht der One-Shot-Node die Anforderung automatisch im nächsten Prozessbild, indem er seinen Request-Wert auf AnimationNodeOneShot.ONE_SHOT_REQUEST_NONE setzt.

# Play child animation connected to "shot" port.
animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE)
# Alternative syntax (same result as above).
animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE

# Abort child animation connected to "shot" port.
animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT)
# Alternative syntax (same result as above).
animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT

# Get current state (read-only).
animation_tree.get("parameters/OneShot/active"))
# Alternative syntax (same result as above).
animation_tree["parameters/OneShot/active"]

TimeSeek

Dieser Node kann verwendet werden, um einen Suchbefehl für beliebige untergeordnete Nodes des Animationsgraphen zu veranlassen. Verwenden Sie diesen Node-Typ, um eine Animation ab dem Start oder einer bestimmten Abspielposition innerhalb des AnimationNodeBlendTree abzuspielen.

Nach dem Einstellen der Zeit und dem Ändern der Animationswiedergabe geht der Node beim nächsten Prozessbild automatisch in den Schlafmodus über, indem er seinen seek_request-Wert auf -1.0 setzt.

# Play child animation from the start.
animation_tree.set("parameters/TimeSeek/seek_request", 0.0)
# Alternative syntax (same result as above).
animation_tree["parameters/TimeSeek/seek_request"] = 0.0

# Play child animation from 12 second timestamp.
animation_tree.set("parameters/TimeSeek/seek_request", 12.0)
# Alternative syntax (same result as above).
animation_tree["parameters/TimeSeek/seek_request"] = 12.0

TimeScale

Ermöglicht die Skalierung der Geschwindigkeit der Animation (oder deren Umkehrung), die über den Parameter Skalierung mit dem Eingang in verbunden ist. Setzt man Skalierung auf 0, wird die Animation angehalten.

Übergang

Sehr einfache State Machine (wenn man sich nicht mit einem StateMachine-Node herumschlagen will). Animationen können mit den Ausgängen verbunden werden und es können Übergangszeiten angegeben werden. Nach dem Setzen der Anforderung und dem Wechsel der Animationswiedergabe löscht der Node die Anforderung automatisch beim nächsten Prozessframe, indem er seinen transition_request-Wert auf einen leeren String ("") setzt.

# Play child animation connected to "state_2" port.
animation_tree.set("parameters/Transition/transition_request", "state_2")
# Alternative syntax (same result as above).
animation_tree["parameters/Transition/transition_request"] = "state_2"

# Get current state name (read-only).
animation_tree.get("parameters/Transition/current_state")
# Alternative syntax (same result as above).
animation_tree["parameters/Transition/current_state"]

# Get current state index (read-only).
animation_tree.get("parameters/Transition/current_index"))
# Alternative syntax (same result as above).
animation_tree["parameters/Transition/current_index"]

BlendSpace2D

BlendSpace2D ist ein Node, der fortgeschrittenes Blending in zwei Dimensionen ermöglicht. Punkte werden zu einem zweidimensionalen Raum hinzugefügt und dann kann eine Position gesteuert werden, um das Blending zu bestimmen:

../../_images/animtree7.gif

Die Bereiche in X und Y können gesteuert (und der Einfachheit halber beschriftet) werden. Standardmäßig können Punkte an beliebiger Stelle platziert werden (klicken Sie mit der rechten Maustaste auf das Koordinatensystem oder verwenden Sie den Button Punkt hinzufügen) und Dreiecke werden automatisch unter Verwendung von Delaunay erzeugt.

../../_images/animtree8.gif

Sie können die Dreiecke auch manuell zeichnen, indem Sie die Option Automatisches Dreieck deaktivieren, obwohl dies nur selten notwendig ist:

../../_images/animtree9.png

Schließlich ist es möglich, den Blending-Modus zu ändern. Standardmäßig erfolgt Blending durch Interpolation von Punkten innerhalb des nächstgelegenen Dreiecks. Wenn Sie mit 2D-Animationen (Bild für Bild) arbeiten, sollten Sie in den Modus Diskret wechseln. Wenn Sie die aktuelle Abspielposition beibehalten möchten, wenn Sie zwischen diskreten Animationen wechseln, gibt es alternativ den Modus Übernehmen. Dieser Modus kann im Menü Blending geändert werden:

../../_images/animtree10.png

BlendSpace1D

Dies ist ähnlich wie bei 2D-Blending-Räumen, aber in einer Dimension (daher werden keine Dreiecke benötigt).

StateMachine

Dieser Node fungiert als State Machine mit Root-Nodes als Zustände. Root-Nodes können erstellt und über Linien verbunden werden. Zustände werden über Übergänge verbunden, die Verbindungen mit speziellen Eigenschaften sind. Übergänge sind unidirektional, aber zwei können verwendet werden, um in beide Richtungen zu verbinden.

../../_images/animtree11.gif

Es gibt viele Arten von Übergängen:

../../_images/animtree12.png
  • Sofort: Wechselt sofort in den nächsten Zustand. Der aktuelle Zustand endet und geht in den Beginn des neuen Zustands über.

  • Sync: Wechselt sofort zum nächsten Zustand, sucht aber den neuen Zustand an der Abspielposition des alten Zustands.

  • Am Ende: Wartet auf das Ende der Wiedergabe des aktuellen Zustands und schaltet dann auf den Beginn der Animation des nächsten Zustands um.

Übergänge haben auch einige Propertys. Klicken Sie auf einen beliebigen Übergang und er wird im Inspektordock angezeigt:

../../_images/animtree13.png
  • Switch-Modus ist der Übergangstyp (siehe oben), der nach der Erstellung hier geändert werden kann.

  • Auto-Advance schaltet den Übergang automatisch ein, wenn dieser Zustand erreicht wird. Dies funktioniert am besten mit dem Schaltermodus Am Ende.

  • Advance-Bedingung schaltet den Auto-Advance ein, wenn diese Bedingung erfüllt ist. Dies ist ein benutzerdefiniertes Textfeld, das mit einem Variablennamen gefüllt werden kann. Die Variable kann über den Code geändert werden (mehr dazu später).

  • Crossfade Time ist die Zeit zum Cross-Fade zwischen diesem und dem nächsten Status.

  • Priorität wird zusammen mit der Funktion travel() aus dem Code verwendet (mehr dazu später). Übergänge mit niedrigerer Priorität werden beim Traveling des Baums bevorzugt.

  • Deaktiviert schaltet diesen Übergang aus (wenn er deaktiviert ist, wird er während des Traveling oder während Auto Advance nicht verwendet).

Für besseres Blending

Damit die Blending-Ergebnisse in Godot 4.0+ deterministisch (reproduzierbar und stets konsistent) sind, müssen die überblendeten Property-Werte einen bestimmten Anfangswert haben. Wenn z.B. zwei Animationen überblendet werden sollen und die eine Animation einen Property-Track hat und die andere nicht, wird die überblendete Animation so berechnet, als ob die letztere Animation einen Property-Track mit dem Anfangswert hätte.

Bei der Verwendung von Positions/Rotations/Skalierungs-3D-Tracks für Skeleton3D-Knochen ist der Anfangswert Knochen-Standardpose. Für andere Propertys ist der Anfangswert 0 und wenn der Track in der RESET-Animation vorhanden ist, wird stattdessen der Wert seines ersten Keyframes verwendet.

Der folgende AnimationPlayer hat z.B. zwei Animationen, aber in einer von ihnen fehlt ein Property-Track für Position.

../../_images/blending1.webp

Das bedeutet, dass die Animation ohne diese Positionen als Vector2(0, 0) behandelt wird.

../../_images/blending2.webp

Dieses Problem kann gelöst werden, indem ein Property-Track für Position als Anfangswert der RESET-Animation hinzugefügt wird.

../../_images/blending3.webp ../../_images/blending4.webp

Bemerkung

Beachten Sie, dass die RESET-Animation existiert, um die Standard-Pose beim Laden des Objekts zu definieren. Es wird angenommen, dass diese nur einen Frame lang ist und nicht dazu gedacht ist, auf der Zeitachse abgespielt zu werden.

Beachten Sie auch, dass die Rotations-3D-Tracks, sowie Rotations-2D-Property-Tracks, deren Interpolationstyp auf Lineare Winkel oder Kubische Winkel eingestellt ist, eine Rotation um mehr als 180 Grad gegenüber dem Ausgangswert als Blending-Animation verhindern.

Dies kann für Skeleton3Ds nützlich sein, um zu verhindern, dass die Knochen beim Blending von Animationen in den Körper eindringen. Daher sollten die Skeleton3D-Werte für Knochen-Standardpose so nah wie möglich am Mittelpunkt des beweglichen Bereichs liegen. Das bedeutet, dass es bei humanoiden Modellen besser ist, sie in einer T-Position zu importieren.

../../_images/blending5.webp

Sie können sehen, dass der kürzeste Rotationspfad von Knochen-Standardposen gegenüber dem kürzesten Rotationspfad zwischen Animationen bevorzugt wird.

Wenn Sie Skeleton3D selbst um mehr als 180 Grad rotieren müssen, indem Sie Animationen für die Bewegung überblenden, können Sie Root-Bewegung verwenden.

Root-Bewegung

Bei der Arbeit mit 3D-Animationen ist es eine beliebte Technik, den Root-Skelettknochen zu verwenden, um dem Rest des Skeletts Bewegung zu verleihen. Auf diese Weise können Charaktere so animiert werden, dass die Schritte tatsächlich dem Boden darunter entsprechen. Außerdem ermöglicht es eine präzise Interaktion mit Objekten während der Zwischensequenzen.

Bei der Wiedergabe der Animation in Godot ist es möglich, diesen Knochen als Root-Bewegungstrack auszuwählen. Dadurch wird die Knochen-Transformation visuell aufgehoben (die Animation bleibt an ihrem Platz).

../../_images/animtree14.png

Anschließend kann die eigentliche Bewegung über die AnimationTree-API als Transformation abgerufen werden:

# Get the motion delta.
animation_tree.get_root_motion_position()
animation_tree.get_root_motion_rotation()
animation_tree.get_root_motion_scale()

# Get the actual blended value of the animation.
animation_tree.get_root_motion_position_accumulator()
animation_tree.get_root_motion_rotation_accumulator()
animation_tree.get_root_motion_scale_accumulator()

Dies kann an Funktionen wie CharacterBody3D.move_and_slide weitergegeben werden, um die Bewegung des Charakters zu steuern.

Es gibt auch einen Tool-Node, RootMotionView, der in einer Szene platziert werden kann und als benutzerdefinierter Boden für Ihren Charakter und Ihre Animationen fungiert (dieser Node ist während des Spiels standardmäßig deaktiviert).

../../_images/animtree15.gif

Steuern aus dem Code heraus

Nachdem der Baum erstellt und in der Vorschau angezeigt wurde, bleibt nur noch die Frage: "Wie wird das alles vom Code aus gesteuert?".

Beachten Sie, dass die Animation-Nodes nur Ressourcen sind und als solche von allen Instanzen, die sie benutzen, gemeinsam genutzt werden. Das direkte Setzen von Werten in den Nodes wirkt sich auf alle Instanzen der Szene aus, die diesen AnimationTree verwenden. Dies ist im Allgemeinen nicht wünschenswert, hat aber einige coole Anwendungsfälle, z.B. können Sie Teile Ihres Animationsbaums kopieren und einfügen, oder Nodes mit einem komplexen Layout (wie z.B. eine State Machine oder ein Blending-Space) in verschiedenen Animationsbäumen wiederverwenden.

Die eigentlichen Animationsdaten sind im AnimationTree-Node enthalten und werden über Propertys angesprochen. Im Abschnitt "Parameter" des Nodes AnimationTree finden Sie alle Parameter, die in Echtzeit geändert werden können:

../../_images/animtree16.png

Das ist praktisch, weil es möglich ist, sie von einem AnimationPlayer aus zu animieren, oder sogar vom AnimationTree selbst, was die Realisierung sehr komplexer Animationslogik erlaubt.

Um diese Werte vom Code aus zu ändern, muss der Property-Pfad ermittelt werden. Dies geschieht ganz einfach, indem Sie den Mauszeiger über einen der Parameter bewegen:

../../_images/animtree17.png

Dadurch können sie eingestellt oder gelesen werden:

animation_tree.set("parameters/eye_blend/blend_amount", 1.0)
# Simpler alternative form:
animation_tree["parameters/eye_blend/blend_amount"] = 1.0

Traveling der State Machine

Eines der netten Features in Godots StateMachine-Implementierung ist die Fähigkeit zu durchlaufen (traveling). Der Graph kann angewiesen werden, vom aktuellen Zustand in einen anderen zu gehen und dabei alle Zwischenzustände zu besuchen. Dies geschieht über den A*-Algorithmus. Wenn es keinen Pfad von Übergängen gibt, der im aktuellen Zustand beginnt und im Zielzustand endet, teleportiert der Graph in den Zielzustand.

Um die Traveling-Fähigkeit zu nutzen, sollten Sie zuerst das AnimationNodeStateMachinePlayback-Objekt vom Node AnimationTree abrufen (es wird als Property exportiert).

var state_machine = animation_tree["parameters/playback"]

Einmal abgerufen, kann es verwendet werden, indem Sie eine der vielen angebotenen Funktionen aufrufen:

state_machine.travel("SomeState")

Die State Machine muss laufen, bevor Sie Traveling verwenden können. Stellen Sie sicher, dass Sie entweder start() aufrufen oder einen Node für Autoplay beim Laden auswählen.

../../_images/animtree18.png