iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0
Vibe Coding

使用 Vibe Coding 開發一套 Tutting 模擬器吧!系列 第 8

📐DAY 08 | 用 MVP 最小改動打造「8拍動態編舞」

  • 分享至 

  • xImage
  •  

看著畫面上的 Xbot 靈巧地擺出各種姿勢,我本以為這套工具已經很厲害了。

直到昨天深夜,我隨便放了一首音樂準備試著編舞時,我才恍然發現:這套軟體目前只是一個「動作編輯器」,根本沒有產生動畫的功能耶 QQ

不管姿勢調得再美,它依然只是死板板的靜態 3D 公仔。

為了驗證「動態編舞」的可行性,我決定貫徹 MVP 導向,用最小的程式碼改動與小跑步研究,讓 Xbot 真正跳起來!

https://ithelp.ithome.com.tw/upload/images/20260821/201442889qY7yujqH0.jpg

小跑步 1:Keyframe 陣列(記錄每一個拍點)

要讓角色動起來,我們不需要做一個像 Premiere 或 Blender 那樣複雜的時間軸。

最 MVP 的做法,就是用一個簡單的 JavaScript 陣列:

  • 擺好 Pose:用 FK 微調出第一個 Tutting 姿勢。
  • 新增拍點:按下「+ 新增拍點」,把當前的關節角度直接 push() 進 Pose 陣列上記為 Frame 1。
  • 連續編排:調整第二個姿勢,記為 Frame 2……以此類推,輕鬆拉出一整套 8 拍舞步序列!

小跑步 2:四元數平滑插值(Slerp)

有了 Frame 1 與 Frame 2,中間的動作該怎麼變過去?

如果直接「瞬間跳變」,看起來會像畫面卡頓;如果計算得不好,手臂可能會穿過自己的身體。

這時候,我們用最簡潔的 3D 旋轉過渡演算法——四元數球面線性插值(Slerp):

Slerp(Spherical Linear Interpolation,球面線性內插)是用來處理 3D 旋轉過渡 的技術。
簡單來說:讓一個物體從「旋轉角度 A」平滑、勻速地轉到「旋轉角度 B」。常見用途於 3D 遊戲相機追隨、角色骨骼動畫過渡、遙控無人機姿態修正。


圖片來源:https://starrykss.tistory.com/1806

小跑步 3:按下 Play 鍵▶️

按下 Play 鍵的那一刻,Xbot 的手臂順暢、平滑且乾淨地從前一個方塊滑向下一個方塊,動作順暢得讓人驚嘆!

https://ithelp.ithome.com.tw/upload/images/20260821/20144288KSfwwGYZsS.png

MVP 研究的收穫:從「靜態模型」到「動態編舞」

這一步以 MVP 為導向的小跑步研究,對這套模擬器來說至關重要。

我們沒有花幾天時間去刻複雜的 UI 軌道,而是用最精簡的程式碼(陣列 + Slerp)快速驗證了核心體驗。

現在,這套工具真正擁有了「時間」的維度,舞者可以真正坐在電腦前,把腦海中的舞步序列一拍一拍寫下來,並即時預覽流暢的動態成果!


Keyframe 拍點時間軸擴充功能 Prompt


## Keyframe 拍點時間軸(核心新需求)
- 維護一個 `keyframes` 陣列,每個元素是 `{ angles: { 關節key: [x,y,z], ... } }`,涵蓋全部 19 個關節的當前 `target` 快照
- 「+ 新增拍點」按鈕:把目前 `target` 深拷貝 `push()` 進 `keyframes`,畫面上以拍點方塊(chip)列表呈現,標示 `F1`、`F2`……
- 每個拍點方塊要能:
  - 點擊主體:立即套用該拍點角度到目前姿勢(方便預覽或接著用控制環微調)
  - 點擊上面的刪除小按鈕:從陣列移除該拍點
  - 標示目前選取中的拍點(供「更新選取拍點」「刪除選取拍點」按鈕使用)
- 額外按鈕:「更新選取拍點」(用目前姿勢覆寫選取中的拍點)、「清空全部」、「循環播放」核取方塊
- 「▶ 播放拍點」按鈕:
  - 少於 2 個拍點時要提示使用者、不執行播放
  - 開始播放時要停用「預設循環播放」與關節控制環(`transformControls.enabled = false`,並先 `detach()`),播放中畫面上的關節球不可再被點擊選取
  - 播放邏輯:以 `requestAnimationFrame` 取得的時間戳計算每一拍的經過時間,一拍時長 = `60000 / bpm`(與預設循環共用同一顆 BPM 滑桿)
  - **核心:每根骨骼在 `keyframes[i]` 與 `keyframes[i+1]` 之間,用四元數 Slerp 而非 Euler 線性插值**:
    ``js
    const qa = eulerToQuat(anglesA);
    const qb = eulerToQuat(anglesB);
    const q = qa.clone().slerp(qb, t); // t: 0→1,本拍進度
    bone.quaternion.copy(restQuat[key]).multiply(q);
    ``
    這樣才能保證每個關節走最短旋轉路徑、角速度均勻,不會有 Euler 插值常見的抖動或繞遠路問題
  - 每拍播放完(t 到達 1)要把 `target`/`current` 同步成該拍的角度(避免下一幀開始時跳動),再前進到下一個拍點區間;播放到最後一個拍點時,依「循環播放」是否勾選決定回到 F1 重播,或停止並停留在最後一個姿勢
  - 播放中,目前正在播放的拍點方塊要有視覺提示(例如亮框),跟著播放進度移動
  - 停止播放後要把控制環權限還回來(`transformControls.enabled = true`),可以繼續點關節球微調


上一篇
📐DAY 07 | 操作升級🆙:FK搭配 3D Helper 與角度貼合
系列文
使用 Vibe Coding 開發一套 Tutting 模擬器吧!8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言