跳至主要內容

FreeMinecraftModels 動畫

FreeMinecraftModels 從 .bbmodel 與 .fmmodel 檔案匯入動畫。本頁介紹保留名稱、影格時間、插值、循環模式與逆向運動學(IK)。

關於骨骼命名規則與其餘的匯入契約,請參閱模型製作注意事項。

五個狀態動畫​

FreeMinecraftModels 只把五個小寫動畫名稱綁定到自動的運行時狀態。模型中的其他一切都是自訂動畫。

動畫名稱是否循環何時播放
spawn否模型建立時播放一次。結束後會接續到 idle
idle是水平方向靜止時;X 或 Z 速度不為零時切換到 walk
walk是水平移動時;接地且 X、Z 速度皆為零時切換到 idle
attack否被觸發時播放;結束後回到 idle
death否呼叫 removeWithDeathAnimation() 時

由狀態機的建構方式推導出的規則:

  • 若模型有 spawn,起始狀態就是 spawn,否則為 idle。兩者皆無的模型沒有目前狀態,因此在明確播放某個動畫之前不會有任何動作。
  • 只有模型中實際存在的動畫才會取得狀態。一個有 walk 但沒有 idle 的模型,永遠不會自行從 walk 轉換出來。
  • idle/walk 切換讀取底層實體的水平速度;單純的垂直移動不會開始行走。靜態實體與玩家偽裝沒有供此功能使用的底層實體,靜止道具也沒有水平移動。其他動畫透過腳本、API 或 FMM 偽裝控制器驅動。
這裡的 jump 只存在於列舉中

JUMP 確實存在於 AnimationStateType 列舉中,而且當實體離地時 walk 狀態也的確會請求一次 jump 轉換 —— 但由於從未註冊任何 jump 狀態,該請求最終不會有任何結果。名為 jump 的動畫並非無效:它只是表現得跟其他自訂動畫一樣,必須手動觸發。玩家偽裝是例外 —— 它們使用另一個控制器,其中的 jump 確實有被接上。

玩家偽裝使用另一組名稱​

玩家偽裝新增逐 tick 控制器,透過相同的播放引擎請求動畫。它的五個保留名稱是 attack、jump、sneak、walk、idle,在單次動畫倒數未執行時依此優先順序檢查。模型沒有 idle 時會在主控台警告。完整表格與單次動畫的時間限制請參閱玩家偽裝。

自訂動畫​

任何名稱不屬於上述五個之一的動畫,仍可依名稱播放:

modeledEntity.playAnimation("open", /* blend */ true, /* loop */ false);
modeledEntity.stopCurrentAnimations();
boolean exists = modeledEntity.hasAnimation("open");
context.prop:play_animation("open", true, false)
context.prop:stop_animation()
  • blend 並不會做交叉淡入淡出。true 會把動畫排入佇列,等目前狀態的該 tick 完成後才開始;false 則會中斷並立即切換。
  • 佇列只有一個位置,後來的排隊請求會取代前一個。沒有目前狀態時,自訂動畫立即開始。排隊的內建狀態無法從空的目前狀態繼續執行;此時請使用 blend=false。
  • loop 僅適用於自訂動畫。內建狀態一律使用自己的循環設定,不論你傳入什麼值。
  • 自訂動畫與 hasAnimation 查詢區分大小寫,請使用精確名稱。內建狀態的播放請求接受不同大小寫,但註冊狀態仍要求模型內使用小寫名稱。
  • 非循環自訂動畫結束時,會請求儲存的最後提交的內建狀態;沒有記錄時請求 idle。這是管理器先前離開的最後一個內建狀態,可能不是自訂動畫開始前正在執行的狀態。請求的返回狀態不存在時,自訂狀態仍被選取,但不再更新影格。
  • 對未知名稱,playAnimation 回傳 false,但下述被抑制的攻擊請求除外。回傳成功表示請求已接受,不代表已經播放了可見影格。
  • 若模型有 idle,stopCurrentAnimations() 會轉換到 idle;否則它會退出目前狀態,讓模型沒有任何作用中的動畫。
  • 在自訂動畫執行期間,對 attack、attack_melee 或 attack_ranged 的請求會被吞掉,以免腳本化的序列被例行戰鬥打斷。
  • 死亡是共用狀態機的終止狀態:新請求回傳 false,排隊的切換被捨棄,停止動畫也不會改變該狀態。不過公開的停止方法還會向 Bedrock 個別傳送停止請求,因此不要用它管理兩種客戶端上的死亡動畫。

時序與長度​

  • Blockbench 以秒為單位儲存動畫長度。FMM 以 ceil(seconds x 20) 轉換它,因此長度一律是整數個 tick,短動畫會無條件進位而非捨去。
  • 每個動畫都會在匯入時被烘焙成一個扁平的逐 tick 影格陣列。播放是每 tick 一次的陣列查找,而非即時插值。
  • 循環動畫以 counter % duration 取索引;非循環動畫則鉗制在最後一個影格,之後不再渲染任何變化。
  • 關鍵影格時間保留小數 tick 位置(20 x time,不取整),因此 0.37 秒的關鍵影格也參與 tick 之間的插值。影格在 0 到 duration - 1 的整數 tick 上取樣;恰好位於宣告結束時間的關鍵影格會影響插值,但本身不會被取樣。如果必須在可見影格達到最終姿勢,請將它放在這個邊界之前。
  • 若同一通道上有兩個關鍵影格落在完全相同的時間,檔案順序中較後者勝出。
  • 時間為非有限值的關鍵影格會中止該軌道,並針對每個動畫產生一則 Malformed animation timeline for model ... 警告,而該模型的其他動畫仍會繼續轉換。

零長度動畫是有效的​

長度為 0(或負數)的動畫會被視為刻意的靜態姿勢 —— 這是家具與其他需要一個具名「無動畫」條目的道具常見的製作選擇。它會被靜默略過且不發出警告,也不會貢獻任何影格。

循環模式​

Blockbench 的循環設定控制匯出的 Bedrock 動畫:

Blockbench 循環模式Bedrock 匯出
loop"loop": true
once"loop": false
hold"loop": "hold_on_last_frame"

Java 播放使用內建狀態的循環規則,或自訂動畫傳入的 loop 參數,不使用這個 Blockbench 欄位決定是否循環。因此設定不一致時,Java 與 Bedrock 的播放可能不同。

插值類型​

每個關鍵影格都帶有自己的插值類型,而進入某個關鍵影格的區段會使用該關鍵影格的類型。支援四種:

Blockbench 類型在 FMM 中的行為
linear直接的線性插值
catmullrom平滑插值(緩入/緩出)
bezier以固定的 0.42 / 0.58 控制點近似 —— FMM 不會讀取個別關鍵影格的貝茲控制桿
step吸附到前一個值,直到下一個關鍵影格

超出這組範圍的任何內容都會解析失敗,並被回報為格式錯誤的時間軸。

有動畫的通道​

每個骨骼會烘焙三個通道:旋轉、位置與縮放。製作時值得注意的事項:

  • 位置值會被除以 16(Blockbench 像素轉方塊)。
  • 旋轉值會被轉換成弧度。
  • Blockbench format_version 5 及以上會翻轉 X 與 Y 旋轉以及 X 位置的正負號。 FMM 會依據宣告的格式版本自動補償,因此請不要手動修正 —— 但也不要把 v5 的宣告與 v4 形式的資料混在一起。
  • 某個通道上沒有關鍵影格的骨骼,會在該通道保持其靜止值;在某個 tick 完全沒有影格的骨骼,則會被重設為旋轉 0,0,0、位移 0,0,0、縮放 1,1,1。
  • 關鍵影格資料點在 .bbmodel 中可能被寫成字串。FMM 會把它們當成純數字解析 —— 空字串對縮放而言變成 1,其他情況變成 0;無法解析的內容會記錄 Failed to parse supposed number value ... 並變成 0。Molang 運算式不會被求值。
  • 每個關鍵影格只會讀取第一個資料點,因此 Blockbench 在 step 關鍵影格上分開的 pre/post 值會收攏成一個。

哪些東西不會有動畫​

  • hitbox 骨骼。指向它的動畫軌道會被直接略過。
  • Blockbench 效果軌道中的音效與時間軸指令關鍵影格。FMM 只會匯入效果軌道中的粒子關鍵影格;請參閱粒子。音效請改以 Lua 腳本或你自己的外掛播放。
  • 無法依名稱解析的骨骼。指向不存在骨骼的軌道會記錄 Failed to get bone <name> from model <model>! 並被略過。

逆向運動學(IK)​

Blockbench 的空物件會作為 IK 控制器。FMM 在運行時以 FABRIK(Forward And Backward Reaching Inverse Kinematics)求解鏈,上限為 10 次迭代、容差為 0.001。

各部分如何搭配:

  1. 同時具有 ik_source(根骨骼)與 ik_target(末端骨骼或定位器)的空物件定義骨鏈。偵測會檢查階層,也支援同層級及向下搜尋;根層級的同層目標會產生僅包含來源骨骼的骨鏈。
  2. 只有空物件的位置關鍵影格驅動 IK。每影格偏移會加到目標骨骼或定位器的靜止位置。執行階段求解器不會使用已儲存的控制器靜止位置作為目標基點,旋轉與縮放軌道也不驅動 IK。
  3. 每個 tick,目前動畫所關聯的 IK 骨鏈會接收目標偏移並求解。關聯骨鏈缺少該影格資料時會被清除。每次切換動畫以及呼叫 stopCurrentAnimations() 時,都會清除所有骨鏈的 IK 旋轉,因此 IK 姿勢不會延續到下一個動畫。
  4. lock_ik_target_rotation 會被解析並儲存,但目前求解器不會套用它。

無法解析的鏈會被略過,並附帶一則具名的主控台警告 —— 確切訊息與製作限制請見模型製作注意事項。

Bedrock 匯出​

每個被轉換的模型也會在產生的套件內寫入一份 Bedrock 動畫檔案,位於 animations/<model_id>.animation.json:

  • 動畫識別碼為 animation.fmm.<model_id>.a_<hex>,其中 <hex> 是動畫名稱的 UTF-8 位元組以十六進位寫成的字串。因此,兩個只差在 Bedrock 不允許之字元的名稱,會得到不同的識別碼。
  • animation_length 是以秒為單位的長度,下限為 0.05,因此一個 tick 的動畫仍然有效。
  • 循環模式的對應方式如循環模式表格所示。
  • 沒有任何動畫的模型仍會取得一個什麼都不做的 idle 條目,讓 Bedrock 實體定義維持有效。
  • 幾何體排除 hitbox、產生的 fmm_nametag_bone_* 名稱標籤骨骼及 m_ 騎乘點,保留作者建立的 tag_ 錨點。動畫匯出器寫入烘焙軌道時,不會套用相同的可見骨骼篩選,因此不要期待被排除的騎乘骨骼在播放動畫後出現可見幾何體。
  • Bedrock 動畫匯出使用烘焙的骨骼旋轉、位置及縮放影格,不會將執行階段的 IK 求解結果寫入這些軌道。依賴 IK 的模型需要在 Bedrock 上個別驗證。
  • 每個動畫都會產生一個動畫控制器,並由實體屬性切換,這就是 FMM 在 Bedrock 客戶端上播放特定動畫的方式。
  • 粒子關鍵影格會成為該動畫的 particle_effects 時間軸,讓 Bedrock 客戶端以原生方式播放。請參閱粒子。

關於該套件在磁碟上的位置,請參閱資源包輸出。

從其他系統播放動畫​

呼叫端進入點
外掛(Java)ModeledEntity#playAnimation(String, boolean blend, boolean loop) / #stopCurrentAnimations() / #hasAnimation(String)
道具 Lua 腳本context.prop:play_animation(name, blend, loop) / context.prop:stop_animation()
任何 Lua 實體表entity.model:play_animation(name, blend, loop) / entity.model:stop_animations()(當 entity.is_modeled 為 true 時可用)

相關介面請參閱 API 與開發者指南與 Lua:道具 API。

Lua 預設參數不同:context.prop:play_animation(name) 預設為 blend=true, loop=true,而 entity.model:play_animation(name) 預設為 false, false。如果這個區別會影響行為,請明確傳入兩個布林值。

疑難排解​

我的動畫從來不會自動播放。 共用狀態機辨識小寫的 spawn、idle、walk、attack 與 death。移動驅動 idle/walk 切換,攻擊與死亡仍需對應的執行階段觸發。除了偽裝控制器額外選擇的名稱,其他名稱都需要明確呼叫 playAnimation / play_animation。

我的模型完全沒有動作。 它很可能既沒有 spawn 也沒有 idle 動畫,因此建立時不會進入任何狀態。請加上一個 idle。

我的動畫有播放,但什麼都沒動。 請檢查動畫長度。零長度動畫依設計會被視為靜態姿勢並被靜默略過。

旋轉是鏡像的。 請檢查 .bbmodel 中的 meta.format_version。FMM 會為格式版本 5 及以上翻轉 X/Y 旋轉的正負號;宣告某個版本卻攜帶另一版本資料的檔案,出來的結果就會是鏡像。

主控台顯示 Malformed animation timeline for model ...。 該動畫中有一條軌道無法被讀取或插值。這則警告在每個動畫只會出現一次,並指出牽涉到的骨骼或 IK 控制器;該模型的其他動畫仍會正常轉換。

我的 Blockbench 時間軸中的音效沒有作用。 音效關鍵影格不會被匯入。請改從 Lua 腳本或你自己的外掛觸發音效。粒子關鍵影格則會被匯入;若粒子沒有顯示,請參閱粒子。