把物理老師的 Godot 遊戲搬上網頁:116 題、11 章、還有三個我不敢自己決定的問題

《格物江湖錄:天理殘卷》原本只有 Windows 跟 macOS 執行檔。我把它做成純網頁、可離線、手機能玩的版本,逐條對照原作 bytecode 還原。這篇講移植別人的作品要處理的三件事:授權怎麼談、還原到什麼程度算數、以及碰到原作沒寫清楚的地方該怎麼辦。

格物江湖錄:天理殘卷 網頁版

這篇講的東西

先把功勞放對位置:這個遊戲的劇情、116 道物理題、美術、世界觀,全部是原作者的,一個字都不是我寫的。我做的是把它搬到瀏覽器裡。

會動手是因為一件很小的事:原作只發佈 Windows 跟 macOS 執行檔,沒有 Linux 版,我自己就打不開。既然要弄,乾脆弄成手機也能玩、關掉網路也能玩的版本。

移植的第一件事不是寫程式,是問人

我先取得原作者授權才開工,基於原作 release v1.4.0。repo 裡的 provenance/ 放的是授權告知、檔案 SHA-256、音檔來源,SOURCE.md 記溯源,授權切成三段寫清楚:網頁版程式碼是 MIT 掛我的名字,遊戲內容與美術著作權屬原作者,配樂 CC0、字型走 OFL。README 最上面就放原作者的贊助連結。

我覺得這段比任何技術細節都重要。把別人的作品搬平台,署名分工線畫不準就不用談後面了。

要 1:1 的是規則,不是外觀

先把界線畫清楚,因為這兩件事我的處理方式完全不同。

規則的部分是嚴格還原:11 章加序章、116 題物理、A/B 雙線分流、9 人好感、5 道具、氣勢戰鬥、4+4 個結局含真結局、3 條情緣、三印、30 個成就、三檔難度。流程與判定逐條對照原作 bytecode 還原,不是照著玩一遍憑印象重寫。音效也是硬還原,8 個音效從原作 PCK 抽出來(Godot 的匯入串流格式,得自己寫 remuxer 還原 vorbis 封包),觸發點對齊原作的 play_sfx_campaign_clue_sfx

還原到什麼程度算數,一開始我用的標準是「連座標都對回去」。證據點原本做成純圓點清單,後來對照反編出來的 main.gd 改成狀態符號加證據名稱按鈕加物理概念 tooltip,連按鈕 190×38 的尺寸都照抄;好感、行囊、格物卷三個面板也從簡化清單改成原作的固定座標排版。

但外觀後來我沒有停在那裡。

原作沒有、我自己加上去的

7 月 25、26 兩天基本上都在做這件事,所以這篇如果只寫「1:1 復刻」就是漏講一半。

配樂整套是重來的。 原作的音檔在 PCK 裡是 Godot 包裝的 OggPacketSequence,不是標準 ogg,而且被降到 16kHz。我改成直接取原作標示的 CC0 原始來源(opengameart 那些)重編,共 24 首,18 首 ogg 加 6 首 mp3,約 30MB 按章 lazy 載入。曲目對應照原作各章:第 N 章投卷用原作該章的 investigation 曲、破局用 battle 曲。音質不受原作降取樣所限。

殺青片尾。 電影式滾動片尾,可暫停、可滾輪、可拖曳前後翻,左側跑幕後花絮膠捲,片尾曲清單循環播,劇照像拍立得那樣左右錯開微傾散落在捲動流裡,最後是 10 位角色立繪的登場人物演員表,正下方放一張 AI 生成的卡司群像殺青照。原作沒有這個東西,是我想給它一個像電影的收尾。

配樂鑑賞做成真的播放器,有封面、控制、進度、收藏、清單。分享做成分享視窗,卡片預覽帶佩印稱號跟進度,可以複製文字連結或直接下載圖片。

美術這邊也動了不少:題名改用毛筆金字題字圖加電影感打光、印章改朱文並加墨韻(蓋得太齊就不像印)、成就徽記改成自製的 6 格銅印圖集取代原本的 CSS 字、格物先賢譜補上 9 位先賢肖像。另外從高解析原檔重出 48 張非證物圖,救回過壓掉的畫質。

操作層則是照網頁的習慣重整:所有視窗統一右上角 X 關閉、ESC 關窗、視窗改單例(連按或空白鍵不再疊出好幾個)、存檔分成進度跟收藏庫兩層、通關後可以選章重玩、離線打包有進度徽章、慢速網有低調的底部載入條。這些原作都沒有,因為單機執行檔本來就不需要。

所以準確的講法是這樣:判定跟流程是還原的,介面與周邊是在那之上重做的。玩起來會比原作多一些東西,但沒有一條規則被我改掉。

判定寫錯的時候,畫面完全正常

這是整個專案我最想寫下來的一條。

結局跟成就都是由好感、旗標、封印算出來的。條件寫錯的時候,遊戲不會壞、不會報錯、玩起來一切正常,只是某個結局或某個成就變成永遠拿不到,而你要玩十週目才會發現。

所以測試不是點一點確認畫面有出來。tools/test.sh --full 是真的把遊戲玩完:八個結局各通關一次,連續十週目收齊 30 個成就,操作真實 DOM,不塞狀態,跑約 12 分鐘。7 月 26 日那次跑完抓到兩個「永遠拿不到」的 bug。這種測試寫起來很煩,但它是唯一能證明判定沒寫壞的方法。

離線包的下載順序決定你能玩到第幾章

Service Worker 分兩層:安裝時只吃 10 個檔的外殼,背景再照遊玩順序暖快取 CORE 那 189 個檔(含 23 首配樂跟 8 個音效)。第一版是進站就一次全部抓完,可玩門檻卡在 28.6 MB。改成漸進載入之後,門檻降到 2.8 MB。

有意思的是 CORE 這份清單的順序本身就是規格:它決定「下載到一半就斷線」的人能玩到哪一章。排錯不會有任何錯誤訊息,所以我寫了 gen_core.py --check 讓測試去顧,不靠人記得。

攻略站也踩過同一類問題,整頁原本要載 9.3 MB,因為它把立繪跟結局圖用全解析度直接顯示,加了縮圖產生器之後首屏從 2.8 MB 降到 0.7 MB。

三個我不敢自己決定的問題

還原到後面一定會遇到原作資料裡有、但看不出來該怎麼呈現的東西。我把這類問題單獨列在 VERIFICATION.md 第九節,現在有三件掛著:

第一,「無主長路」這個結局的判定條件。移植版原本寫成第十一章兩個選項以外的 fallback,但那條路永遠走不到,結局跟「天地四卷」成就都拿不到。我改成用第十章的對白分流,依據是兩篇結局正文各自出現對方沒有的關鍵詞,不是原作行為。

第二跟第三是位置問題,不是有沒有演的問題。battle_beatsaction(答對的現場結果)跟 failure(答錯的劇情代價),還有 thread,原作資料裡都有,移植版一開始卻沒有渲染路徑,等於資料在那裡沒被演出來。現在都演了:答題結果做成左右雙欄,左邊算式、右邊現場結果或劇情代價;thread 分別放進通關畫面的本章脈絡跟格物卷的天理分頁。

演法後來還改過一次。7 月 27 日我查原作反編出來的 campaign_story_progression,發現每一則劇情都自帶 speaker,形狀跟 battle_beats 一模一樣,都是旁白加 speaker 加台詞,那它們本來就該是對白。所以收證據後的劇情從「印在證據視窗裡的靜態文字」改成用立繪對話框演:證據視窗只留格物(拿到什麼證據、正解觀念),收起面板之後角色再出來演一次,旁白沒有角色名、角色台詞配立繪,接著路線分歧,收滿進度時再演里程碑。同一輪也修掉一個誤讀:原本把 reveal 冠上 reveal_speaker,對照 actionresponse 之後可知那應該是旁白。

要問原作者的是「原版把這些演在哪裡」,因為位置是我挑的,不是原作行為。

這三件的共通點是:資料是原作者的,條件或位置是我定的。遊戲照樣可全破、可收滿成就,只影響對原作的忠實度。與其自己拍板然後裝作是原版,我寧可寫進文件、標成待問。

分工線

範圍、還原標準、測試要嚴到什麼程度、哪些問題不能自己決定,是我定的;能跑的程式碼、還原腳本跟自動化測試是 AI 寫的。遊戲本身是原作者的。

先去玩玩看,喜歡的話請去支持原作者,這款遊戲是他為學生做的。