Roblox 動畫,無人值守模式
第一次使用這台機器? 請閱讀此資料夾中的
LOCAL.md。如果該檔案不存在,請執行roblox-skills-setup技能(或將LOCAL.template.md複製為LOCAL.md並填寫內容)。LOCAL.md儲存你的路徑與工具位置;它是你專屬的檔案,絕不回傳。
將動畫創作視為數據(一個定義隨時間變化的姿勢的 JSON 規格),將其烘焙到真實的 Blender 動作中,渲染它,然後在相信結果之前先查看結果。
循環是:創作 → 渲染每一影格 → 拼貼成連絡表 → 查看 → 修正 → 重複。 這個技能存在的每一個部分,都是因為這個循環的某個環節捕捉到了其他所有檢查都遺漏的失敗。
先讀這個:最浪費時間的陷阱
大多數 Roblox 綁定出廠時都內建了一個儲存其靜止姿勢的 Blender 動作。 Blender 在渲染期間會重新計算該動作。所以如果你設定了姿勢數值然後渲染:
- 每個數值探針回報你的姿勢已完美套用
- 每個渲染的影格顯示的都是綁定的預設姿勢
- 數據和圖像永遠無法對齊,且沒有任何錯誤訊息
清除該動作也沒有幫助,因為那個動作就是靜止姿勢本身——清除它,四肢就會縮回軀幹。
解決方法:在同一個動作中插入關鍵影格,然後逐影格使用 frame_set() 並進行渲染。如此一來,動畫就是那個動作,沒有任何東西可以覆蓋它,未觸及的骨骼透過常數外插保持其靜止值,而你最終會得到一個真實的動作——這正是 Roblox 匯出所需要的。scripts/anim_render.py 已經實作了這個邏輯。
完整列表在 references/traps.md 中——共有 28 個陷阱。在除錯任何問題之前請先閱讀它。
使用 FK 進行創作,而非 IK
Roblox 的 KeyframeSequence 是一個按關節旋轉的樹狀結構,每個 Motor6D 對應一個。這就是 FK。每個 R15 綁定都為每個關節提供一個控制骨骼來與之匹配。如果你使用 IK 目標來驅動角色,就等於在一個 Roblox 從未要求的求解器中進行來回轉換,並為此付出代價:
- 僅限位置的 IK 目標會讓肢體的 ROLL 保持自由,因此方塊狀的 Roblox 零件會呈現求解器隨機選擇的面,並在影格之間跳動。在有紋理的虛擬人偶上,這會表現為顏色瞬間改變——這是讓動畫看起來有問題的症狀。
- 求解器會在影格之間跳到同等優秀的分支上,因此即使每個影格驗證都完美通過,四肢仍會跳動。
- 速度很慢。
FK 沒有分支、沒有奇異點、沒有滾動歧義,角度完全 1:1 對應,並且可以直接匯出到 Roblox。
大多數綁定出廠時處於 IK 模式,這會讓所有 FK_* 骨骼看起來像是不起作用。請切換開關——不要就此認定 FK 已失效:
"properties": {"ARM_IK_FK.L": 0.0, "ARM_IK_FK.R": 0.0,
"LEG_IK_FK.L": 0.0, "LEG_IK_FK.R": 0.0}
在一個免費的社群 R15 綁定上測量,結果相當一致:每條肢體靜止時都筆直指向下方;在每個 FK_* 骨骼上,X 負值向前擺動,Z 正值向右擺動(角色的右側),角度 1:1 對應,兩側完全相同——沒有鏡像反轉。子角度是相對於父角度的。
無論你使用哪種綁定,通用的經驗法則是:如果一個控制骨骼看起來不起作用,在放棄它之前,先尋找一個可以禁用它功能的開關。 Rigify 風格的 R15 綁定帶有 ARM_IK_FK / LEG_IK_FK 自訂屬性,預設值為 1.0(IK)。將它們設為 0.0,添加關鍵影格(這樣 frame_set 就無法將其復原),然後在 FK 模式下進行創作。上述的方向慣例是在一個綁定上測量的;calibrate_rig.py 和 sweep_control.py 會在你的綁定上重新測量它們,所以對任何你沒用過的綁定都要執行這兩個腳本。你的綁定的 .blend 檔案路徑需填入 LOCAL.md。
只有在肢體必須命中精確的世界座標點時,才使用 IK。然後請閱讀陷阱 12–15。
讓它看起來像 Roblox,而不是像參考影片
- 30 FPS,一個戰鬥動作持續 0.3–0.7s。動畫編輯器的預設值就是 30fps。現實生活中的時機對遊戲來說太慢了。從動作的真實運作方式中提取力學機制;從 Roblox 中提取時機。
- 五個姿勢:待機、預備、接觸、跟隨、收招。
- 每個片段使用緩動——用
"ease": "in"加速進入打擊,用"ease": "out"平穩結束。固定的平滑步進(smoothstep)會讓每個關鍵影格減速,因此打擊動作會在其自身的接觸影格上緩慢停止,整個動作會感覺軟弱無力。 - 誇張化。 真實的 Roblox 戰鬥動畫遠遠超過人類的活動範圍,並且必須透過輪廓來辨識。
- 渲染成純灰色(
"plain": true)。複雜的虛擬人偶紋理會將方塊狀的綁定格變得難以辨認,最終你會在除錯服裝而不是姿勢。真正的 Roblox 動畫正是因為這個原因而在純色假人上展示。
references/pose-recipes.md 有實際的數值——包含防禦、預備、架勢、接觸和跟隨階段的測量角度表、時機骨架,以及兩個容易出錯的規則(肩膀 + 手肘總和必須保持在約 120 度以下;高踢需要 UpperLeg X 超過 -120 度,否則腿會與軀幹重疊)。從這些數值開始,然後根據預覽畫面進行調整。不要重新推導它們。
要快——用數據說話,而不是憑感覺
整個循環只需幾秒鐘。如果一次處理需要幾分鐘,那就有問題了。在中階電腦上測量,一個 24–36 影格的動畫:
| 階段 | 時間 | 備註 |
|---|---|---|
| Blender 載入綁定 | 0.7s | 啟動從來不是問題 |
| 數值探針(整個 FK 控制圖) | 0.1s | 用數字向綁定提問 |
預覽渲染("preview": true) | 4–6.5s | 1 個視角,320x240,1 個樣本 |
| 完整渲染,2 個視角 @ 640x560 | 9–17s | 只在最後做一次 |
| GIF 編碼 | 0.3s | |
to_roblox.py → Luau | 0.1s | |
| 在 Studio 中建構並驗證 | 一次 execute_luau 呼叫 |
一台機器花費在生成一個準備好放入 Studio 的動畫的時間:遠少於一分鐘。 真正耗費時間的是創作疊代,所以要把時間花在預覽上,絕不要花在完整渲染上。有了 references/pose-recipes.md 作為起點,第二個動作應該能在 1–3 次預覽內完成——references/ 中的勾拳和揮手在第一次嘗試時就是對的。
使用 "report": true 會列印每個關節在每個關鍵節拍的位置:當一個姿勢看起來不對勁時,它能在一次執行中命名出有問題的關節,而不是透過五次渲染來猜測。
不要為了回答一個探針就能回答的問題而去渲染。
不可妥協的規則
每一條都存在,因為跳過它曾經讓看起來正常的東西出錯:
- 絕不相信一個退出碼為 0 的執行。
anim_render.py會斷言每個影格的綁定完整性,斷言影格已經寫入,否則會硬性退出並傳回非零值。 - 查看影格。 每一次處理都要看。
- 如果有參考資料,請建立並排比較並以此為判斷依據——絕不要只看渲染結果。
- 檢查完成的 GIF 本身,而不是你用於編碼的 PNG 檔案。
- 絕不為了讓紅燈變綠燈而放寬容差。 修正規格。
工作流程
1. 校準綁定(任何你沒用過的綁定)
blender --background <rig>.blend --enable-autoexec --python scripts/calibrate_rig.py
blender --background <rig>.blend --enable-autoexec --python scripts/sweep_control.py -- FK_UpperLeg.R:rot TORSO:rot
calibrate_rig.py 會回報形變骨架與控制骨架、動作陷阱、IK/FK 開關狀態,以及哪些控制實際上會移動東西。
sweep_control.py 會給出每個軸的方向、正負號和可用範圍——它會測量骨骼的方向和位置,因此不會遺漏純旋轉控制。
如果 FK_* 骨骼看起來不起作用,那就是 IK/FK 開關的問題,而不是綁定失效。
2. 創作規格
包含 fps、duration、w、h、margin、views、properties、plain、preview、report 以及 keyframes[](其中包含 {t, ease, bones: {Bone: {rot|loc|target|aim}}})。
幾乎總是使用 rot。對於沒有末端效應器的控制(如 ROOT),使用 loc。target / aim 是 IK 的逃生艙——請參閱陷阱 12–15。
如果你忘記以下三點,每次都會出錯:
- FK 子角度會與父角度疊加。 肩膀 50° + 手肘 70° 會使前臂從垂直方向偏移 120°,指向上方越過肩膀。對於防禦姿勢,保持肩膀 + 手肘在約 110° 以下。
- 傾斜和扭轉作用在
UpTorso上,它只驅動上半身。TORSO也會驅動腿部,因此傾斜它會導致角色傾倒並使固定的腳滑動。綁定還會放大效果:X = +15會使頭部擺動 23°。 - 反向旋轉支撐腳的腳踝,
FK_Foot.X ≈ -(FK_UpperLeg.X + FK_LowerLeg.X),否則腳會隨著腿傾斜,角色看起來像站在斜坡上。
請參閱 references/example-fk-roundhouse.json(0.8s 迴旋踢)和 references/example-fk-punch.json(0.5s 交叉拳),以及 references/pose-recipes.md。
2a. 如果手中有道具,首先研究它是如何被真實握持的。
這個步驟在你創作任何關鍵影格之前就要完成,而且它不是可選的。 一旦一個片段中包含武器或工具,“姿勢看起來對不對”就不再是有關綁定的問題,而是有關那個物體的問題,這是關於世界的事實,而不是可以從矩陣推斷出來的東西。
這個規則存在的目的是為了防止一個真實建構中發生過的失敗:一把長柄刀被創作成刀頭朝下,懸在持有者的臀部。之後的每個下游階段都完美地完成了自己的工作——引擎重現了所創作的握持方式,姿勢條與連絡表影格完全匹配,握持誤差為 0.000,完全沒有武器穿入身體的樣本——結果遊戲發布的是一個頭朝下拿著的長柄武器。在這裡創作的錯誤姿勢,對之後的每一個檢查都是不可見的,因為之後的每一個檢查都是在測量與這個姿勢的一致性。 當人類最終檢查時,裁決是它拿反了。他們是對的,而代價是整個下游階段都要為一個這裡十秒鐘做出的決定買單。
在創作之前:
- 搜尋該武器類型是如何被攜帶和使用的。 搜尋練習者的照片,而不是概念藝術,也不是其他遊戲。十秒鐘的閱讀勝過四輪對數值的猜測。
- 將答案作為
_intent寫入規格中,用一句話描述武器:“刀頭朝上,柄越過前導肩膀”、“刀刃從放低的手向上並向前傾斜”。這句話將作為連絡表的判斷標準——見第 4 步。 - 在開始搜尋之前,先對照下表檢查明顯的案例。
| 類型 | 靜止時 | 揮動時 |
|---|---|---|
| 長柄武器、偃月刀、薙刀、矛 | 刀頭朝上——扛在肩上、柄尾抵地、或處於刀刃在胸部高度或以上的防禦姿勢。絕不能垂在腳邊 | 刀頭引導弧線;柄在身體前方交叉,絕不能與手臂在同一垂直平面上 |
| 劍 | 從放低的手向上並向前傾斜,約 +35 度。筆直朝下代表掉了 | 刃口領先;手腕不是鉸鏈,整個手臂承載弧線運動 |
| 斧、錘 | 刀頭朝上或扛在肩上,重量保持在高處 | 刀頭是質量所在——它最後到達,也到達得最猛 |
| 拳套、指虎 | 是穿戴的,不是握持的;格鬥式防禦,雙拳舉起 | 是髖部驅動,不是肩膀驅動 |
以穿戴者自身身體能達到的姿勢來握持道具。 用單手攜帶任何比角色身高還長的物品,都不會與手臂在同一平面上,否則會穿模。
3. 渲染和驗證
blender --background <rig>.blend --enable-autoexec --python scripts/anim_render.py -- spec.json out/
ffmpeg -start_number 0 -i "out/side_%03d.png" -vf "scale=240:-1,tile=6x6" sheet.png
攝影機會自動根據整個動畫的邊界進行取景,並根據視野計算其距離,因此沒有東西會跑出畫面;margin(預設值 1.10)是唯一的調整參數。至少渲染兩個角度——右腿踢從左邊是看不見的。
然後根據參考資料來判斷連絡表,而不是根據連絡表本身。 打開第 3a 步中的 _intent 句子,逐影格檢查連絡表:刀頭朝上了嗎,刃口領先了嗎,柄在身體前方交叉了嗎。連絡表只能證明渲染結果與規格相符。它無法告訴你規格本身是對的,而下游的每個階段都會將它視為事實——所以這是管線中最後一個修正錯誤姿勢成本低廉的點。如果連絡表和參考資料不一致,參考資料獲勝,規格必須更改。
4. 交付 GIF,然後檢查 GIF
ffmpeg -framerate 30 -start_number 0 -i "out/side_%03d.png" -framerate 30 -start_number 0 -i "out/tq_%03d.png" -filter_complex "[0]scale=420:-1[a];[1]scale=420:-1[b];[a][b]hstack=2,split[s0][s1];[s0]palettegen=stats_mode=diff[p];[s1][p]paletteuse=dither=bayer:bayer_scale=3" -loop 0 anim.gif
只看一次連絡表,以你需要的、且尺寸不要更大的方式看。 連絡表是這個管線中最昂貴的物件:一個圖像會在後續每次工具呼叫時在同一上下文中重新傳送,因此在執行初期讀取一個全解析度的連絡表,到執行結束時會浪費數十萬個 token。將它縮小拼貼,當問題涉及特定肢體時裁切到該肢體,並且不要為了重新檢查一個本來可以測量的東西而重新打開它。當檢查可以是數值時——武器是否脫手、是否穿過肢體、以什麼角度放置——就把它做成數值並讀取一行文字。
然後從完成的 GIF 中抽出影格並查看它們——動態與靜態的閱讀感覺不同,而且編碼設定可能默默地失敗:
ffmpeg -i anim.gif -vf "select='not(mod(n,2))',tile=4x3" -vsync 0 verify.png
你創作的片段會破壞你沒創作的片段
Roblox 預設的 Animate 腳本以 Core 優先級播放待機、行走、奔跑、跳躍、墜落和攀爬動畫。你所創作並以 Idle 或更高優先級播放的任何動畫,都會在其播放期間遮蓋它對每個關節所設定的姿勢。
因此,一個全身待機循環——讓持武器角色靜止站著的正確解決方案——會在玩家行走時停止腿部移動,因為你的片段以比行走循環更高的優先級對腿部設定了姿勢。這看起來完全就像“行走動畫缺失了”,而且只會出現在你創作了待機動畫的角色身上,這會讓人感覺...
...(後續內容被截斷)
[截斷]