SDVX Wallpaper 程式碼整理
紀錄這次對 assets/js/model.js 和 assets/js/wallpaper.js 的修改。 model.js 從 2920 行減到 2402 行,行為維持不變(下面有標「行為改變」的除外)。
1. 拖曳移動修好了
問題一:模型跑得比游標快
modelX / modelY 是視窗寬高的百分比,不是像素:
model.x = width / 2 + this.modelX * width / 100;但拖曳的程式碼是直接加像素差 × 0.1:
model.modelX += deltaX * 0.1; // 舊的換算下來模型實際移動 deltaX * width / 1000 像素 —— 在 1920 寬的螢幕上等於游標的 1.9 倍,所以會越拖越偏離抓取的位置。
改成用實際視窗寬度換算,並且以按下去的那一點為基準計算,而不是每次累加:
let deltaX = (e.clientX - dragOriginX) * 100 / window.innerWidth;
model.modelX = clamp(dragOriginModelX + deltaX, -50, 50);用原點計算還順便修掉一個問題:以前拖超過 ±50 的邊界再拖回來,模型會永遠偏移一段距離;現在拖回範圍內就會重新對齊游標。
順便加了 setPointerCapture,游標移出視窗邊緣時拖曳不會斷掉。
問題二:拖曳會卡頓
pointermove 每次都呼叫 model.onResize(1),而 onResize() 裡面有:
updateBg()—— 把背景 sprite 整個砍掉重建,重新PIXI.Texture.from(videoURL),還對影片元素呼叫.pause()/.play()renderer.resize()—— 重新配置 WebGL frame buffer
滑鼠一秒可以送出上百個 pointermove,等於一秒重建上百次背景素材。
所以把單純移動模型的部分拆成新的方法:
updateModelTransform() // 只改模型的 x / y / scale,拖曳和縮放用這個
onResize(event) // 呼叫上面那個,再處理背景和 renderer,只有視窗真的改變大小才用拖曳、雙指縮放、滾輪縮放都改成呼叫 updateModelTransform()。
2. 換模型的等待時間改成讀 json
以前 wallpaper.js 裡有一個 85 行的 switch (oldModelname),每個模型硬寫一個毫秒數:
case "kureha_9thkac":
setTimeout(() => { model.loadModel(modelName) }, 4117);那些數字其實就是退場動作的長度。現在直接從模型自己的設定讀出來:
const settings = live2dModel.internalModel.settings; // 已載入模型的 model3.json
const url = settings.resolveURL(settings.motions[group][0].File);
const motion = await fetch(url).then(r => r.json());
const duration = Math.round(motion.Meta.Duration * 1000); // motion3.json 裡的長度- 讀過的會存進
outDurationCache,同一個模型只抓一次 - 讀不到(沒有那個動作群組、fetch 失敗)就退回 1800ms,跟以前的
default:一樣 - 動作開始時記
performance.now(),抓完 json 只等剩下的時間,所以 fetch 的延遲不會加到換模型的等待上 - 哪個模型用哪個退場動作(
Out/Out1/Final/EG4_out/Last)集中到getOutMotionGroup(),播放和算長度共用同一個值,不會再對不上
驗證結果
對照 data.json 裡全部 53 個模型:原本有寫 case 的全部算出一模一樣的數字(1583、2050、4117、9880、12633…),代表那些硬寫的值確實就是 Meta.Duration × 1000。
⚠️ 行為改變
有 25 個模型以前是掉進 default: 1800,動作被切掉或多等,現在會用真正的長度:
| 模型 | 原本 | 現在 |
|---|---|---|
| grace_yukata_ver5 | 1800 | 4000 |
| tsunomaki_watame | 1800 | 3067 |
| left_hexathlon | 1800 | 2520 |
| rasis_hexathlon | 1800 | 2467 |
| kureha_ver6 | 1800 | 2167 |
| right_hexathlon | 1800 | 2067 |
| nianoa_egbpl / _sp | 1742 | 2058 |
| 其他 18 個 | 1800 | 1517 ~ 1867 |
另外兩件事:
- 舊的
case "grace_yukata"是永遠不會執行的——data.json傳進來的值是grace_yukata_ver5。它的動作其實有 4 秒,等於以前每次都被切掉 2.2 秒。 ortlinde_akasha_game跟_kac共用 12633ms,但它根本沒有Out動作群組(只有In1/In2/Idle/Idle2/Last),所以以前是空等 12.6 秒什麼都沒播。現在會退回 1800ms。如果想讓它跟_kac一樣播Last,在getOutMotionGroup()加一行就好,長度會自動算對。
3. model.js 整理
按鈕:35 段複製貼上 → makeButton()
檔案裡有 35 個按鈕,每個都是同樣的 15 行樣板(白色圓形 + 藍色文字 + zIndex 5 + interactive + 推進 buttonArray + 加到 stage),只有文字、字級、位置不一樣。
現在統一由一個方法生成:
makeButton(label, x, y, onClick, fontSize = 24)呼叫端從 15 行變 3 行:
this.makeButton('OK', 35, 35, () => {
this.model.motion("Ok", 0, 3);
});這是這次最大的一塊,省掉約 450 行。
35 個按鈕的 handler 內容有逐字比對過,除了下面刻意改寫的 3 個之外,其餘 32 個完全沒動。
反應動作:3 段重複 → playReaction()
Good / Bad / VGood 三個按鈕各有 20 行一模一樣的邏輯(播動作 → 把 Idle 的 fade 拉長到 1.5 秒 → 等動作播完再改回 0.015)。抽成:
this.playReaction("R_Good");小差異
setTimeout 裡面改成用呼叫當下抓好的 motionGroups 參考,而不是等 timer 觸發時再讀 this.model。如果在動作播完前就換了模型,舊的寫法會去改到新模型的 fade(或因為 this.model 已經是 undefined 而報錯),新的寫法不會。
模型路徑:40 行 switch → MODEL_JSON_PATHS 表
loadModel() 裡找 model3.json 的那段 switch 改成一張表,只列不規則的 23 個,其餘一律套 <name>/<name>_0101.model3.json:
const MODEL_JSON_PATHS = {
'grace_ver5': 'grace_ver5/grace_ver5_0102',
'rasis_ver5': 'rasis_ver5/rasis_ver5_0103',
'bpl3': 'left_right_hexathlon_bpl/left_right_hexathlon_bpl',
// ...
};
const modelJsonPath = (name) => './assets/live2d/' + (MODEL_JSON_PATHS[name] || `${name}/${name}_0101`) + '.model3.json';54 個模型的路徑都跟舊的 switch 比對過,全部一致,而且檔案都確實存在。
其他小地方
- 音效:8 個
new Howl({...})各 6 行 →loadSound()一行一個 updateModelTransform():model2/model3/lrVFXmodel/rasisVFXmodel各自重複四行一樣的定位程式碼 → 收進一個陣列跑迴圈destroy()的記憶體問題:buttonArray和subContainers只把東西從 stage 移除,陣列本身從來沒清空,每換一次模型就越積越多。現在會重設成[]case 'maxima_vm'沒有break:是故意的(maxima 除了眼鏡按鈕還要有一般按鈕),加了註解免得以後自己看到以為是 bug
沒有動的地方
generate_button_sudden() 還是 700 行。裡面是 sudden death 那一整段演出的流程(sprite、filter、時間軸),不是重複的樣板,硬拆反而更難讀,所以留著。
同理,27 個 PIXI.Sprite.from(...) 的設定區塊有 10 種不同的屬性組合,包裝成通用 helper 要傳一堆 optional 參數,不見得比現在好讀。
還沒驗證的部分
以上都通過 node --check 語法檢查,換模型的等待時間和模型路徑也用腳本對全部模型驗算過。但沒有實際跑起來看畫面 —— 這台機器沒有跑這個站的 server,Live2D 也需要 WebGL。建議自己開起來確認:
- 右鍵拖曳模型(應該完全貼著游標走)
- 滾輪縮放、雙指縮放
- 換幾個模型,特別是 grace_yukata_ver5、tsunomaki_watame、ortlinde_akasha_game 這三個等待時間有變的
- 各個特殊模型的按鈕(bpl3、mion、666、grace、akasha、sudden、maxima)
原始檔案的備份在 scratchpad:model.js.bak / wallpaper.js.bak。