ClaudeDocs/NEXT_STEPS.md
下一步:從哪裡接手
這份每輪重寫,不追加。 HANDOFF.md 說明現況與已排除的假設,
這一份說明下一輪先做什麼、為什麼是這個順序。最後一節是可直接貼進新對話的提示詞。
(前一版累積了三個「附錄:以上已過期」,那是追加而不是重寫的結果。
歷史都在 RE_NOTES_AVATAR.md §36~§46,這份只留現在該做的事。)
0. 三十秒摘要
除了彈簧骨物理與卡通渲染,其他都完成了:549 動作 + 20 相機 + 臉部 + 頭髮 已在 GLB 裡,Live2D 三次係數與 Bezier 轉檔已修(§43), 279 套服裝通用性已驗(§45),README 已更新(§46)。
彈簧骨誤差 110%(100% = 完全不做),
ProcessDynamicBoneLayer 的子系統全部實作完了,
症狀收斂成單一項:過擺 1.50 倍,候選只剩「damping 的第二用途」與「風」。
驗收設施齊全 —— 每個假設都可以量,不必用推的。
1. 先做這個:風(CalcWindPower)
為什麼是它
windPower = 0.7 是開著的(§35 從遊戲讀出來的),但模擬完全沒實作。
它是唯一一個「遊戲有、我們沒有」的能量項,而剩下的症狀正是能量太多(過擺)。
§35 當時的判斷是「風會增加擺動、方向不對,先擱著」——
那是在還有四個 bug 的模型上做的(§38 的限制鏡射、§39 的 delta、
§41 的 _ast、§42 的平滑都是之後才修的)。那個判斷不能算數,必須重測。
怎麼做
CalcWindPower(float3, ManagePropertyData ByRef, Single, Single) @ 0x02793194
呼叫點:newPos += rotate(CalcWindPower(…) × wind × 全域風強)
尚未逆向。先讀出簽章裡兩個 Single 是什麼(時間?相位?),
再決定實作。風若是週期性的,它同時是驅動源也是相位約束 ——
這比純阻尼更能解釋「過擺但時間資訊仍正確」。
怎麼判讀
四支工具一起看,不要只看百分比(理由見 §3):
| 結果 | 意義 |
|---|---|
過擺往 1.00x 掉且 k=0 仍是最佳 |
對了,繼續往精確係數走 |
過擺掉但 k=0 掉出最佳 |
跟 --vel 同一種假象(§38),退回 |
| 過擺不動 | 風不是能量項,剩 damping 那條 |
需要跑遊戲的前置
風做下去之前要確認 run00 錄製當時場景的風向/風強。 若是 0,那 §35 讀到的 0.7 只是材質參數不是實際施加值,這條就直接結案。
2. 接著:damping 的第二個用途
目前 damping 只進 Verlet 慣性項 (1-damping)²。
重讀 0x02792f20 附近,確認它有沒有被用在別的地方。
已知的約束:--vel(力→速度→位置)能把過擺壓到 1.00x
但破壞時間資訊(§38)=> 阻尼不是那個形式。所以要找的是
「不引入一階延遲的耗散」,例如直接對位移量或對限制夾取後的殘差衰減。
3. 判準(重要,違反這條的結論一律不算)
不要只看誤差百分比。 §37 證明舊的 142% 落在「時間亂序帶」內 —— 分辨不出模擬與它自己的隨機平移版。四支工具要一起看:
scripts/diag_swing_trace.py 單根骨逐尤拉分量 + 平移對照組 <- 解析度最高
scripts/diag_swing_align.py k=0 是否為最佳位移 <- 有沒有時間資訊
scripts/diag_swing_error.py 軸夾角(雜訊底線 ±13°)
compare_swing.py 誤差% + 過擺倍率
一個機制若讓 k=0 掉出最佳,誤差百分比再好看都是退步。
4. 與彈簧骨獨立、可以平行做的線
4.1 描邊 pass 與 stencil(卡通渲染唯一剩下的缺口)
251 個 metallib 已拆好、Metal Toolchain 已裝,render_sheet.py 可做渲染驗收。
未解的具體項目(§29/§33):_DefMap 各通道的完整語意、描邊 pass、
_StencilRef=64 出現在哪個 pass、描邊外推量。
注意:mos9527 那邊頂點色 R = 描邊遮罩,我們這邊 R 是「下半身、48 的倍數的 離散等級」—— 形狀像分區索引或描邊寬度等級,但不能直接套用他的結論(§25)。
4.2 阻尼解出來之後要一起做的收尾
--delta-current(§39)與 --quartz(§41)都忠於遊戲、都讓百分比變差,
原因相同:下游缺阻尼,正確的輸入反而被懲罰。兩個要一起翻成預設,
判準用 §39 的分組表與 §37 的對齊掃描,不是總體百分比。
4.3 QuartzDriver 參考骨的延遲(需要跑遊戲)
直接比對說 lag=1、下游看說 lag=0,兩個量測不一致(§41)。
把大腿與 _ast 錄進同一個 run 即可釘死(agent.bakeSwing 走 Transform
階層找骨,加幾個骨名)。與 §1 的風一起在同一次進遊戲做掉。
5. 不要做的事
- 不要在沒有對照組的情況下「N 個選項挑最小」。 有雜訊時那個程序必然 回傳一個贏家,而贏家不是證據。§43 的子步數掃描就是靠「不是單調關係」 才判定它不是答案,不是靠「哪個最小」。
- 不要用總體中位數判斷某個機制有沒有效。 局部 76° 的變化會被彙總指標吃掉。
- 不要重做
HANDOFF.md§2.3 表上那十項。 每一項都有量測與章節出處。 - 不要把
_avatar/baked*.json、driven_*.json當可信輸入。 那是中間產物, 現行資料版本見HANDOFF.md§4。
6. 提示詞(直接貼進新對話)
接手一個 Unity 手遊資產逆向專案。請先讀這三份,順序照列:
ClaudeDocs/NEXT_STEPS.md <- 從這裡開始,講下一步做什麼
ClaudeDocs/HANDOFF.md <- 現況、已排除的假設、踩過的坑
ClaudeDocs/RE_NOTES_AVATAR.md <- 主要來源,§1~§46
先 grep '^## ' 看章節,不要整份讀
檔案索引在 ClaudeDocs/INDEX.md。
專案有一組從這些筆記提煉的 skills(.claude/skills/ 是領域專用,
~/.claude/skills/ 是通用),其中 verify-before-believe、ground-truth-harness、
fan-out-and-prune、notes 直接適用於這輪的工作,請在對應時機使用。
第一件事做 NEXT_STEPS.md §1:逆向 CalcWindPower @ 0x02793194
(windPower=0.7 是開著的但完全沒實作,是唯一「遊戲有我們沒有」的能量項),
實作進 sim_swing.py,然後四支診斷工具一起量,回報過擺倍率、軸夾角、
k=0 的排名,不要只回報誤差百分比。
工作方式的三個要求:
1. 每個假設都用量的,不要用推理走到結論。
基準線 110% 誤差 / 1.50x 過擺 / 55.9° 軸夾角 / k=0 排名 2/90。
2. 效應小於 2 倍時要有對照組,並說明雜訊底線是多少。
軸夾角的雜訊底線是 ±13°。
3. 失敗與更正寫進 RE_NOTES_AVATAR.md 的新章節(接 §47),不要覆蓋舊結論;
HANDOFF.md 與 NEXT_STEPS.md 是重寫不是追加。
只想做渲染那條(不碰彈簧骨)的話,把第一件事換成:
第一件事做 NEXT_STEPS.md §4.1:描邊 pass 與 stencil。
251 個 metallib 已拆在 _avatar/shader/(Metal Toolchain 已裝),
render_sheet.py 可做逐像素渲染驗收。
未解項目:_DefMap 各通道語意、描邊 pass、_StencilRef=64 在哪個 pass、
描邊外推量。注意不要套用 mos9527 的通道結論(§25 已證明約定不同)。