Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Utilizzo di AnimationTree
Introduzione
Con AnimationPlayer, Godot possiede uno dei sistemi di animazione più flessibili che si possano trovare in qualsiasi motore di gioco. È praticamente unico nella sua capacità di animare quasi ogni proprietà in qualsiasi nodo o risorsa, e dispone di tracce dedicate per trasformazione, curve di Bézier, chiamate di funzioni, audio e sotto-animazione.
Tuttavia, il supporto per la fusione di queste animazioni tramite AnimationPlayer è limitato, poiché è possibile impostare solo un tempo fisso per le transizioni di dissolvenza incrociata.
AnimationTree è un nodo progettato per gestire transizioni avanzate.
AnimationTree e AnimationPlayer
Prima di cominciare, tieni presente che un nodo AnimationTree non contiene animazioni proprie. Utilizza invece animazioni contenute in un nodo AnimationPlayer. Puoi creare, modificare o importare le tue animazioni in un AnimationPlayer e poi utilizzare un AnimationTree per controllarne la riproduzione.
AnimationPlayer e AnimationTree si possono entrambi usare sia in scene 2D sia in 3D. Quando si importano scene 3D e le loro animazioni, è possibile utilizzare i suffissi di nome per semplificare il processo e importare con le proprietà corrette. Alla fine, la scena Godot importata conterrà le animazioni in un nodo AnimationPlayer. Poiché raramente si utilizzano le scene importate direttamente in Godot (vengono istanziate o ereditate), è possibile inserire il nodo AnimationTree nella nuova scena che contiene quella importata. Successivamente, puntare il nodo AnimationTree all'AnimationPlayer creato nella scena importata.
This is how it's done in the Third Person Shooter demo, for reference:
È stata creata una nuova scena per il giocatore con un CharacterBody3D come radice. Dentro questa scena, è stato istanziato il file .dae (Collada) originale ed è stato creato un nodo AnimationTree.
Creazione di un albero
Per utilizzare un AnimationTree, è necessario impostare un nodo radice. Un nodo radice di animazione è una classe che contiene e valuta più sotto-nodi e produce un'animazione. Esistono 3 tipi di sotto-nodi:
Nodi di animazione, che fanno riferimento a un'animazione proveniente dall'oggetto
AnimationPlayerassociato.Nodi radice di animazione, utilizzati per fondere i sotto-nodi e si possono annidare.
I nodi Animation Blend (letteralmente "fusione di animazione"), utilizzati in un
AnimationNodeBlendTree, un grafo 2D di nodi. I nodi di fusione accettano più porte di input e forniscono una sola porta di output.
Sono disponibili alcuni tipi di nodi radice:
AnimationNodeAnimation: seleziona un'animazione dall'elenco e la riproduce. Questo è il nodo radice più semplice e generalmente non è utilizzato come radice.AnimationNodeBlendTree: contiene più nodi come figli in un grafo. Sono disponibili molti nodi di fusione, come mix, blend2, blend3, one shot, ecc.AnimationNodeBlendSpace1D: consente di fondere linearmente due nodi di animazione. Controlla la posizione di fusione in uno spazio di fusione 1D per miscelare le animazioni.AnimationNodeBlendSpace2D: consente di fondere linearmente tre nodi di animazione. Controlla la posizione di fusione in uno spazio di fusione 2D per miscelare le animazioni.AnimationNodeStateMachine: contiene più nodi come figli in un grafo. Ogni nodo funge da stato, con diverse funzioni utilizzate per alternare tra gli stati.
Blend tree
Quando si crea un AnimationNodeBlendTree, si ottiene un grafico 2D vuoto nel pannello inferiore, sotto la scheda AnimationTree. Inizialmente contiene solo un nodo Output.
Affinché le animazioni siano riprodotte, è necessario collegare un nodo all'output. È possibile aggiungere nodi dal menu Aggiungi nodo... oppure facendo clic destro in uno spazio vuoto:
Il collegamento più semplice da realizzare è quello di collegare un nodo Animation direttamente all'output, che riprodurrà l'animazione e basta.
A seguire è riportata una descrizione degli altri nodi disponibili:
Blend2 / Blend3
Questi nodi effettueranno una fusione tra due o tre input in base a un valore di fusione specificato dall'utente:
La fusione può utilizzare filtri per controllare individualmente quali tracce sono fuse e quali no. Questo può servire per sovrapporre animazioni l'una sopra l'altra.
Per fusioni più complesse, si consiglia invece di utilizzare gli spazi di fusione.
OneShot
Questo nodo eseguirà un'animazione una sola volta e restituirà un valore al termine. È possibile personalizzare i tempi di fusione per dissolvere in entrata e in uscita, nonché i filtri.
# Play child animation connected to "shot" port.
animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE)
# Alternative syntax (same result).
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).
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).
animation_tree["parameters/OneShot/active"]
// Play child animation connected to "shot" port.
animationTree.Set("parameters/OneShot/request", (int)AnimationNodeOneShot.OneShotRequest.Fire);
// Abort child animation connected to "shot" port.
animationTree.Set("parameters/OneShot/request", (int)AnimationNodeOneShot.OneShotRequest.Abort);
// Get current state (read-only).
animationTree.Get("parameters/OneShot/active");
TimeSeek
Questo nodo consente di spostarsi in un punto specifico dell'animazione collegata al suo ingresso in. Utilizza questo nodo per riprodurre un'animazione (Animation) a partire da una determinata posizione. Si noti che il valore della richiesta di posizionamento è espresso in secondi, quindi se desideri riprodurre l'animazione dall'inizio, imposta il valore su 0.0, oppure, se desideri riprodurla a partire da 3 secondi, imposta il valore su 3.0.
# Play child animation from the start.
animation_tree.set("parameters/TimeSeek/seek_request", 0.0)
# Alternative syntax (same result).
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).
animation_tree["parameters/TimeSeek/seek_request"] = 12.0
// Play child animation from the start.
animationTree.Set("parameters/TimeSeek/seek_request", 0.0);
// Play child animation from 12 second timestamp.
animationTree.Set("parameters/TimeSeek/seek_request", 12.0);
TimeScale
Questo nodo permette di scalare la velocità dell'animazione collegata al suo input in. La velocità dell'animazione sarà moltiplicata per il valore specificato nel parametro scale. Impostando scale a 0, l'animazione sarà messa in pausa. Impostando scale su un valore negativo, l'animazione sarà riprodotta al contrario.
Transizione
Questo nodo è una versione semplificata di una macchina a stati. È possibile collegare le animazioni agli input e l'indice dello attuale corrente determina quale animazione riprodurre. È possibile specificare un tempo di transizione per la dissolvenza incrociata. Nell'Ispettore è possibile cambiare il numero di porte di input, riorganizzare gli input o eliminarli.
# Play child animation connected to "state_2" port.
animation_tree.set("parameters/Transition/transition_request", "state_2")
# Alternative syntax (same result).
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).
animation_tree["parameters/Transition/current_state"]
# Get current state index (read-only).
animation_tree.get("parameters/Transition/current_index"))
# Alternative syntax (same result).
animation_tree["parameters/Transition/current_index"]
// Play child animation connected to "state_2" port.
animationTree.Set("parameters/Transition/transition_request", "state_2");
// Get current state name (read-only).
animationTree.Get("parameters/Transition/current_state");
// Get current state index (read-only).
animationTree.Get("parameters/Transition/current_index");
StateMachine
Quando si crea un AnimationNodeStateMachine, si ottiene un grafico 2D vuoto nel pannello inferiore, sotto la scheda AnimationTree. Inizialmente, contiene uno stato Start e uno stato End.
Per aggiungere stati, fai clic destro o premi il pulsante Crea nuovi nodi, la cui icona è un segno più in un riquadro. Puoi aggiungere animazioni, blend space, blend tree o persino un'altra StateMachine. Per modificare uno di questi sotto-nodi più complessi, clicca sull'icona della matita a destra dello stato. Per tornare alla StateMachine originale, clicca su Root in alto a sinistra del pannello.
Prima che la StateMachine possa fare qualcosa di utile, gli stati si devono collegare tramite transizioni. Per aggiungere una transizione, clicca sul pulsante collega nodi, rappresentato da una linea con una freccia rivolta a destra, e trascinalo tra due stati. Puoi creare 2 transizioni tra gli stati, una per ogni direzione.
Esistono 3 tipi di transizioni:
Immediato: passerà immediatamente allo stato successivo.
Sincronizza: passerà immediatamente allo stato successivo, ma cercherà di ripristinare il nuovo stato nella posizione di riproduzione del precedente.
Alla fine: attenderà il termine della riproduzione dello stato attuale, quindi passerà all'inizio dell'animazione dello stato successivo.
Anche le transizioni hanno alcune proprietà. Clicca su una transizione e sarà visualizzata nell'ispettore:
Xfade Time is the time to cross-fade between this state and the next.
Xfade Curve is a cross-fade following a curve rather than a linear blend.
Reset determina se lo stato in cui si sta passando è riprodotto dall'inizio (true) o meno (false).
Priority is used together with the
travel()function from code (more on this later). Lower priority transitions are preferred when travelling through the tree.Switch Mode è il tipo di transizione (vedi sopra). Si può cambiare dopo la creazione qui.
Advance Mode determina la modalità di avanzamento. Se
Disabled, la transizione non sarà utilizzata. SeEnabled, la transizione sarà utilizzata solo durantetravel(). SeAuto, la transizione sarà utilizzata se la condizione e l'espressione di avanzamento sono vere, oppure se non ci sono condizioni/espressioni di avanzamento.
Condizione di avanzamento ed espressione di avanzamento
Le ultime due proprietà in una transizione di una StateMachine sono Advance Condition e Advance Expression. Quando la modalità di avanzamento è impostata su Auto, queste determinano se la transizione avanzerà o meno.
La condizione di avanzamento è una verifica vero/falso. È possibile inserire il nome di una variabile personalizzata nel campo di testo e, quando la macchina a stati raggiunge questa transizione, verificherà se la variabile è true. Se sì, la transizione continua. Si noti che la condizione di avanzamento controlla soltanto se una variabile è true e non può verificarne la falsità.
Questo rende la condizione di avanzamento molto limitata. Se si volesse effettuare una transizione avanti e indietro in base a una proprietà, sarebbe necessario creare due variabili con valori opposti e verificare se una delle due è vera. Ecco perché, in Godot 4, è stata aggiunta l'espressione di avanzamento.
L'espressione di avanzamento funziona in modo simile alla condizione di avanzamento, ma invece di verificare se una variabile è true, valuta qualsiasi espressione. Un'espressione è qualsiasi cosa che si potrebbe inserire in un'istruzione if. I seguenti sono tutti esempi di espressioni che funzionerebbero nell'espressione di avanzamento:
is_walkingis_walking == true(si comporta allo stesso modo di quella precedente)is_walking && !is_idlevelocity > 0player.is_on_floor()
Avvertimento
L'espressione è sensibile alle maiuscole/minuscole. Se si fa riferimento a proprietà del motore, come velocity su un nodo CharacterBody3D, è necessario utilizzare la convenzione di denominazione snake_case. Se si fa riferimento a proprietà di uno script, è necessario utilizzare lo stile usato nello script, che in genere è snake_case in GDScript e PascalCase in C#.
Ecco un esempio di una transizione di una StateMachine configurata male utilizzando la condizione di avanzamento:
Questa non funziona perché nella condizione di avanzamento è presente una variabile ! che non può essere verificata.
Ecco lo stesso esempio, configurato correttamente, utilizzando due variabili opposte:
Ecco lo stesso esempio, ma utilizzando un'espressione di avanzamento anziché una condizione, il che elimina la necessità di due variabili:
Per utilizzare le espressioni di avanzamento, è necessario impostare il nodo base delle espressioni dall'Inspettore del nodo AnimationTree. Come predefinito, è impostato sul nodo AnimationTree stesso, ma deve puntare al nodo che contiene lo script con le proprie variabili di animazione.
Vedi anche
L'espressione di avanzamento è valutata attraverso la classe Expression di Godot. Consulta Valutazione delle espressioni per ulteriori informazioni su come scrivere espressioni.
StateMachine travel
Una delle funzionalità più interessanti dell'implementazione di StateMachine in Godot è la possibilità di viaggiare. È possibile istruire il grafo a passare dallo stato attuale a un altro, visitando tutti gli stati intermedi. Questo avviene tramite l'algoritmo A*. Se non esiste un percorso di transizione che parta dallo stato attuale e termini allo stato di destinazione, il grafo teletrasporta allo stato di destinazione.
Per utilizzare la capacità di viaggio, è necessario prima recuperare l'oggetto AnimationNodeStateMachinePlayback dal nodo AnimationTree (è esportato come proprietà) e poi chiamare una delle sue numerose funzioni:
var state_machine = animation_tree["parameters/playback"]
state_machine.travel("SomeState")
AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)animationTree.Get("parameters/playback");
stateMachine.Travel("SomeState");
La StateMachine deve essere in esecuzione prima di poter viaggiare. Assicurati di chiamare start() o di collegare un nodo a Start.
BlendSpace2D e BlendSpace1D
BlendSpace2D è un nodo per realizzare fusioni avanzate in due dimensioni. I punti che rappresentano le animazioni sono aggiunti a uno spazio 2D e poi viene controllata una posizione tra di essi per determinare la fusione:
È possibile posizionare questi punti in qualsiasi punto del grafico facendo clic destro o premendo il pulsante aggiungi punto, la cui icona è una penna con una punta. Ovunque siano posizionati i punti, il triangolo che li unisce sarà generato automaticamente attraverso il metodo di Delaunay. È inoltre possibile controllare ed etichettare gli intervalli sugli assi X e Y.
Finally, you may also change the blend mode. By default, blending happens by interpolating points inside the closest triangle. When dealing with 2D animations (frame by frame), you may want to switch to Discrete mode. Alternatively, if you want to keep the current play position when switching between discrete animations, there is a Carry mode. This mode can be changed in the Blend menu:
BlendSpace1D funziona esattamente come BlendSpace2D, ma in una dimensione (una linea). I triangoli non sono utilizzati.
Sync mode
Both BlendSpace1D and BlendSpace2D have a Sync Mode property that controls how animations advance when they are blended.
This replaces the older boolean sync property and offers more precise control.
There are four modes available:
None (default): Inactive animations are frozen and do not advance. Only the currently active (highest-weight) animation moves forward.
Independent: Inactive animations advance with a weight of
0. This matches the behavior of the oldsync = truesetting.Cyclic Mutable: All animations are time-scaled so their phases stay aligned. The shared cycle length is computed dynamically from the active blend weights, which means a single unblended animation plays at its normal speed. This is useful when all your animations have the same logical cycle (e.g. locomotion loops) but may differ slightly in length.
Cyclic Constant: All animations are time-scaled to complete one full cycle in exactly Cyclic Length seconds, regardless of their individual lengths. Set the
cyclic_lengthproperty to your desired cycle duration (must be greater than0).
Avvertimento
Cyclic sync modes require that all blend points use AnimationNodeAnimation with a finite, immutable length. If any blend point uses a different node type, a warning will be shown and cyclic sync will not take effect:
Nota
When using either cyclic mode with animations of different lengths, applying an AnimationNodeTimeSeek to the output will break synchronization. In that case, use AnimationNodeAnimation.use_custom_timeline to normalize animation lengths before syncing.
Per una fusione migliore
Affinché i risultati delle fusioni siano deterministici (riproducibili e sempre coerenti), i valori delle proprietà fuse devono avere un valore iniziale specifico. Ad esempio, nel caso di due animazioni da fondere, se una animazione ha una traccia di proprietà e l'altra no, l'animazione fusa è calcolata come se quest'ultima avesse una traccia di proprietà con il valore iniziale.
Quando si utilizzano tracce 3D di posizione/rotazione/scala per le ossa di uno Skeleton3D, il valore iniziale è Bone Rest. Per le altre proprietà, il valore iniziale è 0 e, se la traccia è presente nell'animazione RESET, è invece utilizzato il valore del suo primo fotogramma chiave.
Ad esempio, il seguente AnimationPlayer ha due animazioni, ma una di esse non ha una traccia di proprietà per la posizione.
Ciò significa che l'animazione a cui manca tratterà quelle posizioni come Vector2(0, 0).
Questo problema si può risolvere aggiungendo una traccia di proprietà per la posizione come valore iniziale all'animazione RESET.
Nota
Tieni presente che l'animazione RESET serve a definire la posa predefinita al momento del caricamento iniziale di un oggetto. Si presume che abbia un solo fotogramma e non è previsto che sia riprodotta tramite la timeline.
Tieni inoltre presente che le tracce di rotazione 3D e le tracce di proprietà per la rotazione 2D con tipo di interpolazione impostato su angolo lineare o angolo cubico impediranno rotazioni superiori a 180 gradi dal valore iniziale come animazione di fusione.
Questo può essere utile per gli Skeleton3D per evitare che le ossa penetrino nel corpo durante la fusione delle animazioni. Pertanto, i valori di Bone Rest di Skeleton3D dovrebbero essere il più vicino possibile al punto medio dell'intervallo di movimento. Ciò significa che per i modelli umanoidi è preferibile importarli in posa a T.
Può notare che viene data priorità al percorso di rotazione più breve a partire dai Bone Rest, piuttosto che al percorso di rotazione più breve tra le animazioni.
Se è necessario ruotare Skeleton3D di oltre 180 gradi tramite animazioni di fusione per il movimento, è possibile utilizzare Root Motion.
Movimento della radice
Quando si lavora con le animazioni 3D, una tecnica popolare consiste nell'utilizzare l'osso radice dello scheletro per dare movimento al resto dello scheletro. Questo permette di animare i personaggi in modo che i passi corrispondano effettivamente al pavimento sottostante. Permette inoltre di interagire precisamente con gli oggetti durante le cinematiche.
Durante la riproduzione dell'animazione in Godot, è possibile selezionare quest'osso come traccia di movimento radice. Facendo ciò, la trasformazione dell'osso sarà annullata visivamente (l'animazione rimarrà sullo stesso posto).
In seguito, è possibile recuperare il movimento effettivo recuperato tramite l'API dell'AnimationTree come una trasformazione:
# 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()
// Get the motion delta.
animationTree.GetRootMotionPosition();
animationTree.GetRootMotionRotation();
animationTree.GetRootMotionScale();
// Get the actual blended value of the animation.
animationTree.GetRootMotionPositionAccumulator();
animationTree.GetRootMotionRotationAccumulator();
animationTree.GetRootMotionScaleAccumulator();
Questo può essere passato a funzioni come CharacterBody3D.move_and_slide per controllare il movimento dei personaggio.
È presente anche un nodo strumento, RootMotionView, che permette di posizionare una scena che fungerà da pavimento personalizzato per il personaggio e le animazioni (normalmente questo nodo è disabilitato durante il gioco).
Controllare da codice
Dopo aver costruito l'albero e averlo visualizzato in anteprima, l'unica domanda che rimane è: "Come si controlla tutto questo tramite codice?".
Keep in mind that the animation nodes are just resources, so they are shared between all instances using them.
Setting values in the nodes directly will affect all instances of the scene that uses this AnimationTree.
This is generally undesirable, but does have some cool use cases, e.g. you can copy and paste parts of your animation tree,
or reuse nodes with a complex layout (such as a StateMachine or blend space) in different animation trees.
I dati effettivi di animazione sono contenuti nel nodo AnimationTree e vi si accede tramite le proprietà. Consulta la sezione "Parametri" del nodo AnimationTree per visualizzare tutti i parametri che possono essere modificati in tempo reale:
This is handy because it makes it possible to animate them from an AnimationPlayer, or even the AnimationTree itself,
allowing very complex animation logic.
Per modificare questi valori tramite codice, è necessario ottenere il percorso della proprietà. È possibile trovarlo passando il mouse sopra uno qualsiasi dei parametri:
Poi puoi impostarli o leggerli:
animation_tree.set("parameters/eye_blend/blend_amount", 1.0)
# Alternate syntax (same result)
animation_tree["parameters/eye_blend/blend_amount"] = 1.0
animationTree.Set("parameters/eye_blend/blend_amount", 1.0);
Nota
Le espressioni di avanzamento di una StateMachine non saranno presenti nei parametri. Questo perché sono contenute in un altro script anziché nell'AnimationTree stesso. Le condizioni di avanzamento (Conditions) saranno invece presenti nei parametri.