Up to date

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

使用 AnimationTree

前言

通過 AnimationPlayer,Godot 擁有你在所有遊戲引擎中能找到的最靈活的動畫系統之一。幾乎可以在任何節點或資源中對任何屬性進行動畫處理,以及專門的變換、貝塞爾、函式呼叫、音訊和子動畫軌道,這樣的能力相當獨特。

然而, 通過 AnimationPlayer 混合這些動畫的支援相對有限, 只能設定固定的交叉漸變過渡時間.

AnimationTree 是Godot 3.1中引入的一個新節點, 用於處理更高級的變換, 它取代了舊的 AnimationTreePlayer , 同時增加了大量的功能和靈活性.

建立內容

首先, 必須明確 AnimationTree 節點不包含它自己的動畫. 相反, 它使用包含在 AnimationPlayer 節點中的動畫. 通過這種形式, 您可以像往常一樣編輯動畫(或從3D場景匯入動畫), 然後使用這個額外節點來控制播放.

在3D場景中經常使用 AnimationTree . 當從3D交換格式匯入場景時, 它們通常自帶動畫(要麼是多個, 要麼是在匯入時從一個大的動畫中拆分出來). 最後, 匯入的Godot場景在 AnimationPlayer 節點中包含動畫.

很少在Godot中直接使用匯入的場景(它們要麼產生實體, 要麼來自繼承), 您可以將 AnimationTree 節點放置在包含匯入的新場景中. 然後, 將 AnimationTree 節點指嚮匯入場景內建立的 AnimationPlayer 節點.

這是在 第三人稱射擊遊戲演示, 中的設定, 參考下圖:

../../_images/animtree1.png

為玩家建立了一個以 KinematicBody 為根節點的新場景. 在這個場景中, 已產生實體原來的 .dae (Collada)檔案, 並建立 AnimationTree 節點.

建立內容

可以在 AnimationTree 中使用三種主要節點型別:

  1. 動畫節點, 從連結的 AnimationTree 中引用動畫.

  2. 動畫根節點, 用於混合子節點.

  3. 動畫混合節點,在 AnimationNodeBlendTree 中使用,通過多個輸入埠進行單圖混合。

AnimationTree 中設定根節點, 如下幾種型別可供選擇:

../../_images/animtree2.png
  • AnimationNodeAnimation:從列表中選擇一個動畫並播放它. 這是最簡單的根節點, 一般不直接用作根節點.

  • AnimationNodeBlendTree:包含許多*混合*型別的節點,如調配, 混合2, 混合3, 一對一等. 這是最常用的根節點之一.

  • AnimationNodeStateMachine:將多個根節點作為圖中的子節點. 每個節點作為一個 狀態 使用, 並提供多個函式在狀態之間進行切換.

  • AnimationNodeBlendSpace2D:允許在二維混合空間中放置根節點. 在二維中控制混合位置以混合多個動畫.

  • AnimationNodeBlendSpace1D:以上的簡化版本(一維)。

混合樹

AnimationNodeBlendTree 可包含用於混合的根節點和常規節點。節點從選單新增到圖中:

../../_images/animtree3.webp

所有混合樹預設都包含一個 ``Output``(輸出)節點,為了讓動畫播放,必須有個東西與其相連。

測試此功能最簡單的方法是直接連接一個 ``Animation``(動畫)節點:

../../_images/animtree4.png

這會簡單地重播動畫. 確保 AnimationTree 節點對實際發生的事情是啟動的.

以下是可用節點的簡短描述:

混合2/混合3

這些節點將通過使用者指定輸入的兩個或三個混合值之間進行混合:

../../_images/animtree5.gif

對於更複雜的混合, 建議使用混合空間.

混合也可以使用篩檢程式, 也就是說, 您可以單獨控制通過混合功能的軌道. 這對於動畫的層疊非常有用.

../../_images/animtree6.png

OneShot

此節點將執行子動畫, 並在完成後返回. 可以用於定制淡入淡出時間, 以及篩檢程式.

../../_images/animtree6b.gif

在設定時間和改變動畫播放後,尋找節點通過設定其 seek_position 值為 -1.0,在下一個程序影格自動進入睡眠模式。

# 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"]

時間縮放

這個節點可以用來使尋找命令發生在動畫圖像的任何子代上。使用這個節點型別可以從 AnimationNodeBlendTree 中的開始或某個位置播放 Animation

在設定時間和改變動畫播放後,尋找節點通過設定其 seek_position 值為 -1.0,在下一個程序影格自動進入睡眠模式。

# 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

時間縮放

允許縮放任何子節點的動畫速度(或將其反轉). 設定為0會暫停動畫.

轉場效果

非常簡單的狀態機(當您不想使用 StateMachine 節點時)。動畫可以連接到輸出,並且可以指定過渡時間。

# 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 是一個在二維空間進行高級混合的節點. 將點新增到一個二維空間, 然後可以控制位置來確定混合:

../../_images/animtree7.gif

可以控制X和Y的範圍(為方便起見, 還可以標記它們). 預設情況下, 可以在任何位置放置點(只需按右鍵坐標系統或使用 新增點 按鈕)將自動生成德洛內三角形.

../../_images/animtree8.gif

也可以通過禁用 自動三角形 選項來手動繪製三角形, 雖然基本上沒必要這麼做:

../../_images/animtree9.png

最後, 可能會更改混合模式. 預設情況下, 混合是通過在最近的三角形內插點來實作的. 當處理二維動畫(逐影格)時, 您可能希望切換到 離散 模式. 此外, 如果您想在離散動畫之間切換時保持目前播放位置, 請使用 進位元 模式. 此模式可在 混合 功能表中更改:

../../_images/animtree10.png

一維混合空間

這類似於二維混合空間, 但在一維空間中(所以不需要三角形).

狀態機

這個節點是一個狀態機,根節點都是狀態。根節點可以建立並通過線路連接。狀態通過*轉換*連接,它們是具有特殊性質的連接。轉換是單向的,但是可以用兩個來達到雙向連接。

../../_images/animtree11.gif

Godot 沒有使用限制

../../_images/animtree12.png
  • *Immediate*(立即):將立即切換到下一個狀態。目前狀態將結束,並與新狀態的開頭相混合。

  • *Sync*(同步):立即切換到下一個狀態,但會將新狀態快進並到舊狀態的播放位置。

  • *At End*(末尾):將等待目前狀態播放結束,然後切換到下一個狀態動畫的開頭。

過渡也有一些屬性。按一下任何過渡,它就會顯示在“屬性面板”面板中:

../../_images/animtree13.png
  • *Switch Mode*(切換模式)為過渡型別(見上文),可以在此處建立後修改。

  • Auto Advance*(自動前進)當達到此狀態時將自動開啟轉換。最適合 *At End 切換模式。

  • *Advance Condition*(前進條件)會在條件成立時打開自動前進。這是一個可以用變數名填充的自訂文字欄位。可以從程式碼中修改變數(稍後將對此進行詳細介紹)。

  • *Xfade Time*(疊化時間)是在這個狀態和下一個狀態之間交叉漸變的時間。

  • *Priority*(優先順序)與程式碼中的 travel() 函式一起使用(後述)。當從一個狀態到另一個狀態時,會優先使用優先順序較低的過渡。

  • *Disabled*(禁用)允許禁用此轉換(它不會在行程或自動前進期間使用)。

為了更好的混合

在 Godot 4.0+ 中,為了使混合結果具有確定性(可重現且始終一致),混合屬性值必須具有特定的初始值。例如,在要混合兩個動畫的情況下,如果一個動畫具有屬性軌跡而另一個動畫沒有,則計算混合動畫時就好像後一個動畫具有具有初始值的屬性軌跡一樣。

當對 Skeleton3D 骨骼使用位置/旋轉/縮放 3D 軌跡時,初始值為 Bone Rest。對於其他屬性,初始值為“0”,如果軌道存在於“RESET”動畫中,則使用其第一個關鍵影格的值。

例如,下面的AnimationPlayer有兩個動畫,但其中一個缺少Position的Property軌道。

../../_images/blending1.webp

這意味著缺少的動畫會將這些位置視為「Vector2(0, 0)」。

../../_images/blending2.webp

這個問題可以透過加入一個位置屬性軌跡作為「RESET」動畫的初始值來解決。

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

備註

請注意,「RESET」動畫的存在是為了定義最初載入物件時的預設姿勢。假設它只有一影格,並且預計不會使用時間軸進行播放。

另請記住,插值型別設定為線性角度或立方角的旋轉 3D 軌道和 2D 旋轉的屬性軌道將防止混合動畫從初始值旋轉超過 180 度。

這對於 Skeleton3D 很有用,可以在混合動畫時防止骨頭穿透身體。因此,Skeleton3D 的 Bone Rest 值應盡可能接近可移動範圍的中點。 這意味著對於人形模型,最好以 T 形姿勢匯入它們

../../_images/blending5.webp

您可以看到,優先考慮骨骼休息的最短旋轉路徑,而不是動畫之間的最短旋轉路徑。

如果您需要透過混合動畫進行運動來將 Skeleton3D 本身旋轉超過 180 度,則可以使用 Root Motion。

根骨骼運動

處理 3D 動畫時,一種流行的技術是動畫師利用根骨骼為其餘部分骨骼製作運動動畫。這使得動畫角色的腳步與下面的地板相配對。並且允許在電影拍攝期間與物體進行精確的互動。

在 Godot 中重播動畫時,可以選擇這根骨骼作為*根運動軌跡*。這會在視覺上取消這根骨骼的變換(動畫將保持原狀)。

../../_images/animtree14.png

然後, 實際運動可以通過 AnimationTree API 作為轉換:

# 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()

可以提供給 KinematicBody.move_and_slide 等函式,用來控制角色移動。

還有一個名為 RootMotionView 的工具節點,可以放置在場景中,作為你的角色和動畫的自訂地板(這個節點預設在遊戲期間禁用)。

../../_images/animtree15.gif

使用程式碼控制

建立樹和預覽之後,只剩下一個問題:“如何使用程式碼控制所有的節點?”。

要注意動畫節點就是資源,因此他們會在所有使用他們的實例之間共用。直接修改節點中的值,將會影響到場景中所有使用這個 AnimationTree 的實例。通常是不希望這樣的,不過也有一些不錯的用法,比如你可以複製貼上你的動畫樹的一部分,或者在不同的動畫樹中複用具有複雜佈局的節點(例如狀態機和混合樹)。

實際的動畫封包含在 AnimationTree 節點中, 並通過屬性存取. 檢查 AnimationTree 節點的 "參數" 部分, 查看所有可以即時修改的參數:

../../_images/animtree16.png

這很方便, 因為它可以通過 AnimationPlayer 獲得動畫效果, 甚至是 AnimationTree 本身, 允許實作非常複雜的動畫邏輯.

想要通過程式碼修改這些值, 必須獲得該屬性的路徑. 這是很容易做到的, 把滑鼠懸停在任何參數:

../../_images/animtree17.png

允許設定或讀取它們:

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

狀態機行程

Godot的“StateMachine”實現中一個不錯的功能是旅行的能力。可以指示圖形從當前狀態轉到另一個狀態,同時訪問所有中間狀態。這是通過 A* 演算法完成的。如果沒有從當前狀態開始到目標狀態結束的轉換路徑,則圖形將傳送到目標狀態。

要使用行程能力, 您應該首先從 AnimationTree 節點中檢索 AnimationNodeStateMachinePlayback 物件(其被匯出為一個屬性).

var state_machine = animation_tree["parameters/playback"]

一旦檢索到, 可以呼叫它提供的許多函式之一:

state_machine.travel("SomeState")

狀態機必須正在運作才能使用行程能力。確保呼叫 start() 或選擇一個節點以**在載入時自動播放**。

../../_images/animtree18.png