工程實作 12 min read

角色走樓梯腳會穿模?腳步 IK 四步驟拆解,對照實驗誤差 4.22→0.32 px

香港獨立開發者零零花一週讓遊戲角色走樓梯時每步都踩在階上,Threads 貼文 1.9 萬次瀏覽。我把腳步 IK 拆成射線、踏板吸附、兩段骨 IK、骨盆下沉四步,做成瀏覽器對照實驗:階高 22 時腳底誤差從 4.22 px 降到 0.32 px——其中骨盆下沉的效果要有下樓場景才量得出來。

角色走樓梯腳會穿模?腳步 IK 四步驟拆解,對照實驗誤差 4.22→0.32 px
本文目錄 · 10
懶人包
  • 香港獨立開發者零零(@00vchannel)在 2026-10-17 發了一篇 Threads:花一週讓角色走樓梯時每一步都踩在階上,標竿是《絕區零》。到 2026-10-25 這篇有 1.9 萬次瀏覽、440 讚。
  • 沒有腳步 IK 時,角色上樓腳會插進階梯、下樓腳會懸空。我用 POC 統計,階高 22 時上樓的不良樣本 373 筆是插入、下樓 360 筆是懸空。
  • 腳步 IK 拆成四步:射線找落點、挪到踏板中央、兩段骨 IK 解膝蓋、骨盆往較低的腳下沉。
  • 我用單一 HTML 檔重做成對照實驗,模擬 20 秒、階高 22:腳底誤差從 4.22 px 降到 0.32 px,支撐不良從 32% 降到 5%。
  • 逐項關閉的結果:關踏板吸附誤差回到 1.81 px,關骨盆下沉回到 1.77 px、支撐不良升到 14%。骨盆下沉的效果要有下樓的場景才量得出來。
  • 要做進遊戲,Unity 和 Unreal 都有現成的 foot placement 方案可用;這個 POC 的用途是看懂原理。

一篇「浪費時間」的貼文為什麼有 1.9 萬次瀏覽?

2026-10-17,香港的獨立開發者零零(Threads 帳號 @00vchannel)分享了一段影片:自己的遊戲角色走上一段樓梯,每一步都踩在階面上。他在原貼文裡這樣寫:

分享一下做了一週的角色腳步走樓梯功能~ 其實我的遊戲好像也沒有特別需要走樓梯的功能,但是看到《絕區零》的角色腳步可以完美配合樓梯,真的很想嘗試一下。我都不知道我浪費時間在這些幹什麼?哈哈~

我在 2026-10-25 查的時候,這篇有 1.9 萬次瀏覽、440 讚、33 則留言。留言裡有人說「Ik 算很久喔」,也有人問「好想知道怎麼做到的」。零零目前在做獨立遊戲《異世界港V》和《異世界潛行者》,個人頁寫預計 2026 年底上架。

這是一個玩家多半說不出哪裡不對、開發者卻很在意的細節。他沒有公開程式碼,《絕區零》怎麼做的我也找不到官方資料,所以這篇不猜他們的實作。我做的是另一件事:把通用的腳步 IK 拆開,用瀏覽器做一個能逐項關掉的對照實驗,看每一步各自貢獻多少。

沒有腳步 IK,角色走樓梯會怎樣?

一般的角色控制器用一顆膠囊體貼著地面走。膠囊體跟著腳下的地面高度升降,腿則播一段在平地錄好的走路動畫。平地上兩者對得起來,到了樓梯就對不起來了:動畫裡的腳以為地面是平的,實際上每隻腳底下的高度都不一樣。

我在 POC 裡統計了沒有 IK 那組的錯誤方向。階高 22、模擬 20 秒,支撐期腳底標紅的樣本裡:

區段腳插進階梯腳懸空
上樓37364
下樓84360
原因在 A 組的腳一律放在「身體正下方的地面高度」。上樓時前腳踩的那階比身體正下方高,腳就插進階梯;下樓時前腳那階比較低,腳就浮在半空。這兩種錯誤方向相反,所以只靠把整個角色往上或往下挪,修不好。

腳步 IK 怎麼做?四個步驟

Vulkan 官方教學〈Foot Placement on Uneven Terrain〉把不平地形的腳步放置整理成每幀依序執行的流程:從每隻腳上方往下打射線、對命中點做時間上的平滑、把骨盆往下移到夠得著較低的那隻腳、再用 IK 解腿並讓腳掌貼合地面法線。

我的 POC 是 2D 的樓梯,踏板都是水平面,所以用的是這套流程的簡化版:

步驟做什麼解決什麼
1. 射線找落點擺動腳出發時,往前一個步幅處向下打射線,拿到那裡的地面高度腳要落在哪一階
2. 踏板吸附落點在樓梯範圍內,就挪到該階踏板中央避免腳跟踩空、腳尖頂到下一階
3. 兩段骨 IK已知大腿長、小腿長、髖和踝的位置,用餘弦定理解出膝蓋角度腿長固定,膝蓋自己彎到對的角度
4. 骨盆下沉骨盆高度跟著較低的那隻腳走(擺動中的腳用它的落點算)低的那隻腳伸得到地
另外還有一個抬腳弧線:擺動腳的弧線頂點要高過起點到落點之間最高的那一階,否則腳在半路會踢穿階緣。

第 3 步不需要通用的 IK 解法。腿只有大腿、小腿兩段,髖、膝、踝構成一個三角形,三邊長都知道(第三邊是髖到踝的距離),膝蓋角度用餘弦定理一次算出來。Vulkan 教學用的是 CCD 或 FABRIK 這類迭代解法,那是為了處理更多節骨頭的鏈;兩段骨用解析解就夠了。POC 裡這段只有幾行:

function solveIK(hip, target) {
  const dx = target.x - hip.x, dy = target.y - hip.y;
  const d = Math.min(Math.max(Math.hypot(dx, dy), 1e-3), LEG - 0.01);
  const base = Math.atan2(dy, dx);
  const cosA = (THIGH * THIGH + d * d - SHIN * SHIN) / (2 * THIGH * d);
  const a = Math.acos(Math.max(-1, Math.min(1, cosA)));
  const knee = { x: hip.x + THIGH * Math.cos(base + a), y: hip.y + THIGH * Math.sin(base + a) };
  const ankle = { x: hip.x + d * Math.cos(base), y: hip.y + d * Math.sin(base) };
  return { knee, ankle };
}

d 被夾在腿長以內。目標點太遠時,腿會打直、踝停在伸得到的最遠處,不會被拉長。這個限制後面量測時很重要。

對照實驗怎麼做?

POC 是一個不用安裝任何套件的 HTML 檔,畫面上下兩組角色走同一段樓梯,先上 5 階、走一段平台、再下 5 階:

  • A 組(沒有 IK):身體跟著腳下地面高度走,腿播平地走路動畫。
  • B 組(腳步 IK):四個步驟全開。
頁面可以開關踏板吸附和骨盆下沉,也能調速度和階高。線上版在 html.yanchen.app/stairs-foot-ik,打開就會跑。 樓梯腳步 IK 對照實驗畫面:上方 A 組沒有 IK,角色下樓時支撐腳標成紅色、沒有踩實;下方 B 組開啟腳步 IK,支撐腳標成綠色,藍色虛線是往下打的射線與預測落點

腳底那一小段會變色:支撐期的腳如果超過 15% 的取樣點離地面超過 2 px,就畫成紅色,否則綠色。

量測方式是這樣:只量支撐期(腳應該踩在地上的時候),沿腳底每 1 px 取一個點,算腳底高度和地面高度的差。統計兩個數字:

  • 腳底接觸誤差:所有取樣點的平均高度差,單位 px。
  • 支撐不良:誤差超過 2 px 的取樣點比例。
程式另外提供 window.simulate(mode, seconds),用固定步長跑模擬、不畫圖,結果不受瀏覽器幀率影響。每組跑 20 秒、dt = 1/120。

結果:四個步驟各貢獻多少?

情境無 IK 誤差 / 支撐不良IK 誤差 / 支撐不良
階高 81.53 px / 23%0.27 px / 5%
階高 224.22 px / 32%0.32 px / 5%
階高 346.52 px / 36%0.39 px / 6%
階高 22,IK 關閉踏板吸附—1.81 px / 8%
階高 22,IK 關閉骨盆下沉—1.77 px / 14%
這組數字最早是 2026-10-25 用 Playwright 在瀏覽器裡跑的;2026-10-26 我把同一份程式抽出來用 Node 再跑一次,五列數字完全相同。

幾個讀法:

  • 階越高,沒有 IK 越糟。誤差從 1.53 px 一路漲到 6.52 px,有 IK 的那組則一直壓在 0.4 px 以下。
  • 關掉踏板吸附,誤差回到 1.81 px。腳落在階緣附近時,腳底有一段懸在空中。
  • 關掉骨盆下沉,誤差 1.77 px,支撐不良升到 14%,是兩個開關裡傷得最重的。下樓時骨盆不先降,低的那隻腳伸不到地,IK 只能把腿打直、踝停在半空。

量測過程修正了兩次

第一次:量的位置。 第一版直接拿 IK 的目標點去比地面高度。目標點本來就是射線打到的地面,所以 IK 組的誤差恆為 0,等於什麼都沒量到。改成量 IK 實際解出來的踝位置後,腿伸不到的情況才會反映在數字上,這也是前面 d 被夾在腿長以內的原因。

第二次:場景只有上樓。 原本的樓梯只往上走。上樓時骨盆的平滑會自然落後,身體比腳低,腿怎樣都伸得到,關掉骨盆下沉也看不出差別。補上下樓之後,關掉骨盆下沉的誤差才跳到 1.77 px。

兩次修正的共同點:量測要能量到「失敗」,數字才有意義。一個永遠回報 0 的指標,跟沒有指標一樣。

78 秒看完四個步驟

同一個實驗剪成了一支直式短片,用吉祥物當角色,依序講問題、四個步驟和數據:

這個 POC 沒處理什麼?

  • 只有 2D、水平踏板。 沒做腳掌依地面法線旋轉,斜坡和不規則地形不在範圍內。
  • 只有走路。 零零的貼文底下就有人接著問「再來是樓梯奔跑與跳躍」,這兩種情況步幅和節奏都不同,POC 沒有涵蓋。
  • 前提是樓梯碰撞體是一階一階的。 有 Skyrim 模組作者在開發紀錄裡提到,如果樓梯的碰撞體做成一個斜坡,射線打到的是斜面,樓梯 IK 就失效了。要做腳步 IK,得先確認樓梯的碰撞體是一階一階的。
  • 不是遊戲引擎套件。 Unity 和 Unreal 都有現成的 foot placement 做法與討論,例如 Unity 論壇的 FOOT PLACEMENT IK 官方討論串、Unreal 論壇的 Foot placement。要放進遊戲,從引擎現成的方案開始比較實際。

想自己動手的話

  1. 打開 html.yanchen.app/stairs-foot-ik,先把階高拉到最高,看 A 組下樓時的腳。
  2. 關掉「骨盆下沉」,看 B 組下樓時腿怎麼打直、腳懸在半空。
  3. 再關掉「踏板吸附」,看腳落在階緣的樣子。
  4. 要看程式,右鍵檢視原始碼,solveIK、stepIK、footContact 三個函式就是全部的核心。
想看零零自己的版本,到 Threads 追 @00vchannel,他的遊戲預計 2026 年底上架。

常見問題

腳步 IK 是什麼?

IK 是 inverse kinematics(反向運動學)。一般動畫是給每個關節角度,算出腳在哪裡;IK 反過來,先決定腳要踩在哪裡,再算出膝蓋和髖要怎麼彎。腳步 IK 就是讓角色的腳貼合實際地形,而不是照著平地錄的動畫走。

為什麼走樓梯特別難?

樓梯每一階的高度是跳躍式的,兩隻腳常常踩在不同高度,腳也可能落在階緣。我的 POC 裡,沒有 IK 時上樓腳插進階梯、下樓腳懸空,兩種錯誤方向相反,只調整整個角色的高度修不好。

骨盆下沉為什麼這麼重要?

腿長是固定的。下樓時如果骨盆不先往下,低的那隻腳到地面的距離超過腿長,IK 只能把腿打直,腳停在半空。實驗裡關掉骨盆下沉後,支撐不良從 5% 升到 14%。

兩段骨 IK 一定要用 FABRIK 或 CCD 嗎?

不用。大腿和小腿只有兩段,髖、膝、踝是一個三邊長都已知的三角形,用餘弦定理就能直接解出膝蓋角度。FABRIK、CCD 這類迭代解法適合更長的骨鏈,例如觸手或尾巴。

author
陳
陳彥彤

AI 工程師 · AI 顧問。Java 後端 8 年、AI 工程師 2 年。AI 內訓 · AI 導入顧問 · 前後端與雲端培訓。

support

覺得文章有用可以到 GitHub 給個 star,或是透過信箱聊聊 AI 內訓、AI 導入顧問或前後端 / 雲端培訓。

related

相關文章

[工程實作] · 15min
yt-dlp「Could not copy Chrome cookie database」怎麼修?Windows Chrome 156 實測 4 種做法
Windows 上的 Chrome 和 Edge 開著時會鎖住 cookie 資料庫,yt-dlp 的 --cookies-from-browser 就會報 Could not copy Chrome cookie database。我在 Chrome 156 把四種做法各跑一次——流傳的 LockProfileCookieDatabase 旗標已經沒用,關掉瀏覽器或匯出 cookies.txt 才讀得到。
[工程實作] · 13min
企業 AI 客服的 8 層架構:只接模型 1/8、做滿 8/8,禮拜五改一行提示詞使用者看不出來
David Ooi 的 IG reel 把「公司自己的 AI」拆成 8 層:模型、提示詞、RAG、工具、權限、護欄、快取路由、可觀測性,我照著用 Claude Haiku 做了虛構家電品牌的客服 Chloe,只接模型的版本 8 題過 1 題,還主動稱讚對手。8 層全開後把提示詞改一行,5 輪重跑使用者看到的分數沒掉,護欄觸發從 8 次變 14 次——沒有可觀測性和回歸測試就看不到,而且判定式和關鍵字護欄本身也會誤判。
[工程實作] · 15min
韓國線上寺廟산사為什麼擠爆伺服器?我仿做 3D 禪堂壓測 2000 人,延遲從 4.2 秒降到 0.45 秒
산사是韓國個人開發者 10 月 28 日上線的 3D 線上寺廟,大家化身小沙彌坐在法堂裡合掌、叩拜,不能聊天,開站一天就湧進數萬人擠爆伺服器。我照它的 Three.js + WebSocket 架構仿做一間壓測 2000 人:全擠一間 p95 延遲 4.2 秒、分成 20 間只要 0.45 秒——同房廣播量跟人數平方成正比,分房才擋得住。