跳至主要內容

FreeMinecraftModels 動畫

本頁記錄 FreeMinecraftModels 實際上如何處理 .bbmodel.fmmodel 檔案中的動畫資料:哪些名稱是特殊的、關鍵影格如何被烘焙、支援哪些插值與循環模式,以及 IK 是如何被驅動的。本頁刻意保守 —— 這裡的一切都能在匯入與運行時管線中看到。

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

五個狀態動畫

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

動畫名稱是否循環何時播放
spawn模型建立時播放一次。結束後會接續到 idle
idle當底層實體的速度小於或等於 0.08
walk當底層實體的速度大於 0.08
attack被觸發時播放;結束後回到 idle
death呼叫 removeWithDeathAnimation()

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

  • 若模型有 spawn,起始狀態就是 spawn,否則為 idle。兩者皆無的模型沒有目前狀態,因此在明確播放某個動畫之前不會有任何動作。
  • 只有模型中實際存在的動畫才會取得狀態。一個有 walk 但沒有 idle 的模型,永遠不會自行從 walk 轉換出來。
  • idle/walk 的切換讀取的是底層實體的速度,因此這實際上是 DynamicEntity 的功能。靜態實體與玩家偽裝在此並沒有底層實體,因此就停留在 idle;道具背後的盔甲架不會移動,所以道具也停留在 idle。這三者都是透過腳本、API,或(對偽裝而言)FMM 自己的偽裝控制器來驅動真正的動畫。
這裡的 jump 只存在於列舉中

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

玩家偽裝使用另一組名稱

玩家偽裝不會執行上述的狀態機。它有自己的每 tick 控制器,並保留五個名稱 —— attackjumpsneakwalkidle —— 依該優先順序評估;如果模型沒有 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 則會中斷並立即切換。
  • loop 僅適用於自訂動畫。內建狀態一律使用自己的循環設定,不論你傳入什麼值。
  • 當非循環的自訂動畫結束時,實體會回到最後一個已提交的內建狀態(也就是它先前正在做的事),若先前沒有狀態則回到 idle
  • 當名稱既不符合任何已註冊狀態、也不符合模型中的任何動畫時,playAnimation 會回傳 false
  • 若模型有 idlestopCurrentAnimations() 會轉換到 idle;否則它會退出目前狀態,讓模型沒有任何作用中的動畫。
  • 在自訂動畫執行期間,對 attackattack_meleeattack_ranged 的請求會被吞掉,以免腳本化的序列被例行戰鬥打斷。

時序與長度

  • Blockbench 以秒為單位儲存動畫長度。FMM 以 ceil(seconds x 20) 轉換它,因此長度一律是整數個 tick,短動畫會無條件進位而非捨去。
  • 每個動畫都會在匯入時被烘焙成一個扁平的逐 tick 影格陣列。播放是每 tick 一次的陣列查找,而非即時插值。
  • 循環動畫以 counter % duration 取索引;非循環動畫則鉗制在最後一個影格,之後不再渲染任何變化。
  • 關鍵影格時間會保留其小數的 tick 位置(20 x time,不四捨五入),因此位於 0.37 秒的關鍵影格會落在兩個 tick 之間並被正確插值,而不會被吸附或丟棄。動畫的最後一個關鍵影格會被保留,不會因四捨五入而被截掉。
  • 若同一通道上有兩個關鍵影格落在完全相同的時間,檔案順序中較後者勝出。
  • 時間為非有限值的關鍵影格會中止該軌道,並針對每個動畫產生一則 Malformed animation timeline for model ... 警告,而該模型的其他動畫仍會繼續轉換。

零長度動畫是有效的

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

循環模式

Blockbench 的循環設定會直接從動畫讀取:

Blockbench 循環模式Java 運行時Bedrock 匯出
loop無限重複"loop": true
once播放完畢後停止"loop": false
hold播放完畢後停在最後一個影格"loop": "hold_on_last_frame"

插值類型

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

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 ... 並變成 0Molang 運算式不會被求值。
  • 每個關鍵影格只會讀取第一個資料點,因此 Blockbench 在 step 關鍵影格上分開的 pre/post 值會收攏成一個。

哪些東西不會有動畫

  • hitbox 骨骼。指向它的動畫軌道會被直接略過。
  • Blockbench 的效果軌道(音效、粒子與時間軸指令動畫器)。任何類型不是 bonenull_object 的動畫器都會被忽略,因此 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. 只會讀取空物件的位置關鍵影格。它們會成為相對於控制器靜止位置的每影格目標偏移;空物件上的旋轉與縮放軌道會被忽略。
  3. 每個 tick 都會套用目前影格的目標偏移並求解該鏈。在沒有 IK 資料的影格上,該鏈的 IK 旋轉會被清除。
  4. 空物件上的 lock_ik_target_rotation 會從模型中讀取。

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

Bedrock 匯出

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

  • 動畫識別碼為 animation.fmm.<model_id>.<animation_name>,名稱會針對 Bedrock 做淨化處理。
  • animation_length 是以秒為單位的長度,下限為 0.05,因此一個 tick 的動畫仍然有效。
  • 循環模式的對應方式如循環模式表格所示。
  • 沒有任何動畫的模型仍會取得一個什麼都不做的 idle 條目,讓 Bedrock 實體定義維持有效。
  • 只有視覺骨骼會被匯出。hitbox、自動生成的 fmm_nametag_bone_* 名稱標籤骨骼,以及 m_ 騎乘點骨骼都會被排除在幾何體之外,因此也被排除在動畫骨骼區塊之外。你自己製作的 tag_ 骨骼不會被排除 —— 它會像其他骨骼一樣被匯出(通常是一個沒有立方體的空骨骼);被濾掉的只有它所生成的名稱標籤對應骨骼。
  • 每個動畫都會產生一個動畫控制器,並由實體屬性切換,這就是 FMM 在 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_modeledtrue 時可用)

周邊介面請參閱 API 與開發者指南Lua:道具與物品 API

疑難排解

我的動畫從來不會自動播放。 只有 spawnidlewalkattackdeath 會自行觸發。其他一切都需要明確呼叫 playAnimation / play_animation

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

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

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

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

我的 Blockbench 時間軸中的音效與粒子沒有作用。 效果軌道不會被匯入。請改從 Lua 腳本或你自己的外掛觸發它們。