在拖曳 IK 目標球時,很容易將雙手拉扯到非人類解剖結構的怪異角度。如果 3D 模擬器跳出的姿勢是真人舞者根本無法做到的,那它就失去了作為編舞輔助工具與模擬器的價值了。
今天 DAY 26,我們要專注於為 XBOT 標準骨架注入關節角度限制(Joint Angular Limits)與人體工學限制,確保模擬器的每一次幾何折疊,都符合真實舞者的身體能力!
關節角度限制(Joint Angle Limits)是用來防止3D人偶或骨架做出超越真人極限的異常扭曲之功能。
在實務編舞測試中,如果沒有限制關節,我們會遇到以下生硬且不自然的動態:

為了讓模擬器全面契合人體工學,我們針對 XBOT 標準骨架設定了以下嚴格的關節旋轉極限(左右側同步生效):
| 關節類別 | 關節名稱與代號 | 旋轉軸 / 運動方向 | 限制角度範圍 | 備註說明 |
|---|---|---|---|---|
| 1. 上肢與手臂關節 | 肩關節 / 鎖骨 (Clavicle L/R) | 前後屈伸 | -30° ~ 45° | |
| 上下聳肩 | -15° ~ 30° | |||
| 肩肱關節 (Shoulder L/R) | 前屈 / 後伸 (Pitch) | -60° ~ 180° | 3 軸旋轉 | |
| 內收 / 外展 (Roll) | -50° ~ 180° | |||
| 手臂內旋 / 外旋 (Yaw) | -90° ~ 90° | |||
| 手肘關節 (Elbow L/R) | 屈曲 / 伸直 (X 軸) | 0° ~ 145° | 單軸限制;極限伸直 0°,完全收合 145°,嚴禁反向折斷 | |
| 手腕關節 (Wrist L/R) | 掌屈 / 背伸 | -80° ~ 70° | 2 軸旋轉 | |
| 尺偏 / 橈偏 | -30° ~ 20° | |||
| 2. 軀幹與脊椎關節 | 腰椎 / 胸椎 (Spine & Spine1) | 前後俯仰 | -15° ~ 30° | 單節阻尼限制 |
| 左右側彎 | -15° ~ 15° | |||
| 水平扭轉 | -15° ~ 15° | |||
| 上胸椎 / 頸根 (Spine2) | 前後俯仰 | -20° ~ 30° | 軀幹頂端 | |
| 水平扭轉 | -20° ~ 20° | |||
| 脊椎整體累加限制 | 軀幹整體前傾 / 後仰 | -45° ~ 90° | Spine + Spine1 + Spine2 總和 | |
| 軀幹整體水平總扭轉 | -45° ~ 45° | 防止扭毛巾現象 | ||
| 頸部 / 頭部 (Neck & Head) | 低頭 / 抬頭 | -40° ~ 50° | ||
| 左右轉頭 | -70° ~ 70° | |||
| 3. 下肢關節 | 髖關節 (Hip L/R) | 前屈 / 後伸 | -30° ~ 120° | 3 軸旋轉 |
| 內收 / 外展 | -30° ~ 45° | |||
| 大腿內旋 / 外旋 | -40° ~ 40° | |||
| 膝蓋關節 (Knee L/R) | 屈曲 / 伸直 (X 軸) | -140° ~ 0° | 單軸限制;禁止膝蓋向前反折 | |
| 腳踝關節 (Ankle L/R) | 跖屈 / 背屈 | -45° ~ 20° | ||
| 4. 掌指微觀關節 | 大拇指 (Thumb1 ~ Thumb3) | CMC 掌指基部 | -30° ~ 60° | 雙手共 30 節 |
| MCP / IP 指節 | 0° ~ 80° | 僅向掌心收合 | ||
| 四指 (Index/Middle/Ring/Pinky 1~3) | MCP 掌指根部 | -10° ~ 90° | ||
| PIP / DIP 中末指節 | 0° ~ 100° | 單向向掌心彎曲,無側向甩動 |
針對系統中每個關節(ALL_JOINT_KEYS),獨立定義 X/Y/Z 三軸設定:
// 每個關節的三軸設定結構
{
x: { enabled: true, min: 0, max: 145 },
y: { enabled: false, min: -180, max: 180 },
z: { enabled: false, min: -180, max: 180 }
}
3D Tuttinhg 模擬器中確保骨架角度符合人體工學限制(Joint Limits)的核心夾緊演算法(Pure Functions),主要功用是用來強制過濾並修剪不合法的角度,防止虛擬角色做出脫臼、反折或穿模等怪異動作。
// 單軸夾緊邏輯:含防呆與防修改保護
function clampAxis(val, config) {
if (!config || !config.enabled) return val;
const min = Math.min(config.min, config.max); // 防錯:使用者填反自動校正
const max = Math.max(config.min, config.max);
return THREE.MathUtils.clamp(val, min, max);
}
// 關節層級夾緊 (回傳全新陣列)
function clampJointAngles(jointKey, [x, y, z]) {
const cfg = jointLimitsConfig[jointKey];
if (!cfg) return [x, y, z];
return [
clampAxis(x, cfg.x),
clampAxis(y, cfg.y),
clampAxis(z, cfg.z)
];
}
使用者用滑鼠/ Gizmo 拖曳關節時,系統實時擷取旋轉並夾緊,直接將夾緊後的角度寫回骨骼 Quaternion。骨骼在拖曳當下即「頂住」邊界,避免放開滑鼠才彈回的突兀視覺。

解決了骨架的「自然與限制」後,我們正式邁向了科技與人性的融合。3D 模擬器不僅要算得精準,更要算得自然。透過為 XBOT 注入完整的人體工學關節限制,我們確保了模擬器吐出的每一次「90 度手盒(Hand Box)」或「Threading 穿線招式」,都是舞者真正能上台演繹的動作。
# 關節角度限制(Joint Limits)功能規格
請在一個使用骨骼(bone)+ 歐拉角驅動姿勢的 3D 角色系統中,新增「關節角度限制」功能,需求如下:
## 1. 資料結構
- 針對系統中每一個關節(`ALL_JOINT_KEYS`),各自建立 X/Y/Z 三軸獨立的限制設定:
``
{ enabled: boolean, min: number, max: number }
``
- 角度單位為度,數值定義為「相對 rest pose」的旋轉角,需與現有的 `target`/`current` 姿勢陣列使用同一套座標系。
- 每軸可獨立開關;未啟用時該軸完全不受影響,維持原本自由旋轉行為。
- 預設全部關節、全部軸皆為停用狀態(`enabled:false`),min/max 給合理預設值(例如 -180~180)。
- 設定需持久化到 `localStorage`,頁面重新載入時自動還原。
## 2. 核心夾緊函式
- 實作單軸夾緊函式:
- 若未啟用則直接放行。
- 若 min > max 則自動互換,避免使用者填反導致角度被鎖死在單一值。
- 否則用 `clamp(v, min, max)`。
- 實作關節層級的夾緊函式:對輸入的 `[x, y, z]` 角度陣列,逐軸呼叫單軸夾緊函式,回傳新陣列(不可修改原始輸入)。
- 若某關節沒有對應的限制設定,原樣放行,不影響既有行為。
## 3. 生效範圍(所有角度寫入路徑都必須經過夾緊)
### 統一寫入入口
系統中所有「賦值姿勢角度」的地方(套用姿勢庫、匯入 JSON、關鍵影格編輯等)都必須經過同一個函式,寫入前先執行關節夾緊。
### 即時拖曳
使用者用滑鼠/觸控拖曳關節時,從骨骼目前旋轉反推出角度後,若超出限制:
- 資料層的角度要夾緊。
- 同時要把夾緊後的角度重新寫回骨骼本身的旋轉(quaternion),讓骨骼在拖曳過程中「即時頂住」邊界,而不是放開滑鼠才彈回去,確保操作手感一致、不違和。
### 設定變更即時生效
使用者在限制設定介面勾選啟用或修改 min/max 時,要立刻對該關節「目前」的姿勢角度重新夾緊一次;如果目前姿勢已超出新設定範圍,要馬上收回到範圍內,不必等待下一次操作。
## 4. UI 需求
- 依部位分組(例如:軀幹、頭、手臂、腿、手指)顯示所有關節的限制設定,各分組可個別收合。
- 提供關節名稱搜尋/過濾功能。
- 每個關節、每一軸顯示:一個「啟用」勾選框 + min/max 兩個數字輸入框;未啟用時輸入框應隱藏收起,保持畫面簡潔。
- 提供「全部展開」「全部收合」「恢復預設(全部停用)」等批次操作按鈕。
- 建議在介面上提示使用者:先不啟用限制、手動轉動關節觀察目前角度範圍,再回頭設定合理的 min/max 並啟用,避免亂填數值。