前幾天整理的,都是已經落地的系統:存檔、選項、音訊、Pixi 場景和資源預載。到了 Day24,我原本想直接談「Web 之外還能怎麼走」,但回頭對照 repo 後,發現更值得先說清楚的是:現在到底做到哪裡,哪些還停在規劃階段。
《九重燼》現在的主軸仍然是 Web。Vercel 是實際部署平台,npm run build 是正式建置流程,PWA 已經接上 vite-plugin-pwa,但目前只快取一小部分 app shell 資源,沒有把整包美術與音訊做成離線遊戲。Electron、Steam、Tauri 目前也沒有專案骨架。
先看 vercel.json:
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"cleanUrls": true,
"trailingSlash": false
}
也就是說,正式部署時 Vercel 會跑 npm run build,然後把 dist 當靜態輸出目錄。
package.json 裡的 build 指令不是只有 vite build:
"build": "npm run check:ink && npm run generate:manifest && npm run generate:sections && vite build"
這裡有三個前置步驟:
check:ink:編譯 Ink 劇本,並輸出 src/narrative/compiled/main.json。generate:manifest:產生圖片與音訊預載 manifest,包括 Day23 提到的 preload-manifest.json。generate:sections:產生 src/data/chapter-sections.json 章節分段資料。最後才交給 Vite 打包。這代表部署不是「把前端丟上去」這麼簡單;每次 build 都會重新編譯劇本,並重產資源清單與章節資料。對互動敘事遊戲來說,這些步驟尤其重要,因為劇情資料一錯,畫面再漂亮也沒用。
vercel.json 還有幾條 rewrite:
{ "source": "/play", "destination": "/play.html" },
{ "source": "/ranking", "destination": "/ranking.html" }
入口頁、遊戲頁、排行頁各自有 HTML。vite.config.ts 也把它們列成三個 input:
input: {
portal: resolve(__dirname, 'index.html'),
play: resolve(__dirname, 'play.html'),
ranking: resolve(__dirname, 'ranking.html'),
}
所以這不是傳統 Vue Router 的單頁站,而是 Vite 多入口靜態站。/play 只是被 Vercel 導到 play.html,/ranking 導到 ranking.html。除了 assets/、story/、manifests/、api/ 與三個入口 HTML 之外的路徑,才會回到 index.html。
這個做法很適合現在的專案:Portal、遊戲本體、排行榜彼此獨立,又可以共用同一組 Vite build。
Vercel 設定裡也把快取分開:
{
"source": "/assets/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
}
/assets/ 主要放圖片和音訊,可以使用長效快取。檔案名稱穩定時,瀏覽器不用每次重新下載。
但 /story/ 和 /manifests/ 是另一回事:
"public, max-age=0, must-revalidate"
劇本和 manifest 不能像圖片一樣快取一年。故事修一句台詞、章節資料更新、結局條件調整,如果玩家一直拿到舊 JSON,存檔和劇情狀態就可能對不上。這也是 Day12 存檔版本化會遇到的同一個問題:遊戲資料會變,不能假裝每個版本都永遠相容。
PWA 支援已經接上,但範圍刻意壓得很小。
vite.config.ts 已經引入:
import { VitePWA } from 'vite-plugin-pwa'
設定也已經存在:
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css}'],
navigateFallback: null,
},
manifest: {
name: '九重燼 Embers of the Ninefold Court',
short_name: '九重燼',
start_url: '/play.html',
display: 'standalone',
orientation: 'any',
},
})
這代表 build 後會有 PWA manifest 和 service worker。public/pwa/ 也有 icon:
icon-192.png
icon-512.png
icon-maskable-512.png
但這裡最重要的是快取範圍。workbox.globPatterns 只把 js/css 放進 precache;PWA icon 和少數 favicon 仍會被快取,但遊戲中的 webp、mp3、json 不會整批放進去。
所以現在的 PWA 可以安裝到桌面/主畫面,並以 standalone 方式開啟。
文字冒險看起來很適合離線,但《九重燼》不是只有文字。
Day23 講過,素材載入已經有 PreloadManager 和 chapter bundle。那是為了降低切場景卡頓,不是為了永久離線。圖片、CG、音訊加起來的體積不小;如果 service worker 一口氣快取全部資產,會遇到幾個問題。
第一,第一次安裝成本太高。玩家只是想試玩,結果瀏覽器開始抓兩百 MB 素材,體感會很差。
第二,更新會變麻煩。劇本、manifest、素材、存檔版本要一起考慮。
所以現在只做 app shell 快取。
比較務實的路線應該是這樣:
先把 Web 版穩住。Vercel 建置、路由 rewrite、快取標頭、API,以及有限度的 PWA app shell 快取,這些基礎設施已經足以支撐公開 Demo。
接著把 PWA 做完整一點。不是馬上離線整包遊戲,而是先確認安裝體驗、圖示、啟動畫面與更新提示。尤其真機測試很重要,Chrome 桌面、Android、iOS Safari 對 PWA 的支援細節不完全一樣。
最後才考慮桌面封裝。等內容量、玩家需求、離線需求都明確之後,再評估 Electron、Tauri 或 Steam。那時候才知道要包的是一個試玩 Demo,還是一個值得安裝和更新的正式版。