前面幾天大多在談遊戲本體。Day28 換個角度,來看玩家按下「進入遊戲」之前會先看到什麼。
《九重燼》不是只有 play.html。根目錄的 index.html 是官方 Portal,負責介紹故事、人物、章節、素材、結局與開發歷程。這個頁面不直接跑 Ink,也不碰 Pixi 舞台,但它很重要。玩家第一次打開網站時,通常不是帶著完整背景設定來的;Portal 的工作,就是在幾分鐘內把「這是一款什麼遊戲」講清楚。
聽起來像一般 Landing Page。做到後來才發現,它其實是一個獨立子系統。
目前 Vite build 有三個 HTML 入口:
input: {
portal: resolve(__dirname, 'index.html'),
play: resolve(__dirname, 'play.html'),
ranking: resolve(__dirname, 'ranking.html'),
}
也就是:
/ 對應 index.html,官方 Portal/play 透過 Vercel rewrite 指到 play.html,遊戲本體/ranking 透過 Vercel rewrite 指到 ranking.html,鮮花與雞蛋榜Portal 首頁、遊戲本體、排行榜共用同一個 repo 和部署流程,但彼此不是同一個 runtime。Portal 的素材圖鑑資料寫錯,不該讓玩家進不了遊戲;遊戲本體的某個 Pinia 狀態壞掉,也不該拖垮官網首頁。
vercel.json 也配合這個分工:
{ "source": "/play", "destination": "/play.html" },
{ "source": "/ranking", "destination": "/ranking.html" }
剩下不屬於 assets、story、manifests、api 或特定 HTML 的路徑,才 fallback 回 index.html。這讓 Portal 可以扮演網站門面,同時不干擾遊戲和 API。
index.html 是靜態骨架,頁面上有幾個主要錨點:
#story:故事背景#features:遊戲特色#chapters:主線與番外章節#characters:角色介紹#gallery:素材圖鑑#endings:結局圖鑑#dev:開發歷程與開發者導覽列就是直接連到這些錨點,另外有 /ranking 的獨立頁面連結,以及 /play.html 的「進入遊戲」按鈕。
遊戲本體需要的是玩家當下所在章節的狀態;Portal 需要的是「把整個作品攤開給玩家看」。兩者資料密度不同,互動目的也不同。
Portal 的章節區目前很單純:chapters 和 extraChapters 都是 status: 'live',畫面上顯示「可遊玩」。它不是章節鎖的真正來源。
真正的章節鎖在遊戲端,由 src/services/PasswordManager.ts 讀 /api/chapter-locks,遠端資料放在 Vercel Edge Config;管理端則走 /api/chapter-locks-admin。Portal 首頁只負責展示「現在官網要怎麼介紹章節」,不負責判斷玩家能不能進某章。
Day3 談角色設計時,重點是角色不只是好感度容器。Portal 的角色區做的是另一件事:把那套設計翻成玩家看得懂的入口。
例如六位女主角各自有:
皇室、朝堂、北梁使團和其他登場人物也各自有簡短 bio。這些不是劇情正文,不會被 Ink 執行;它們比較像作品手冊。玩家還沒玩到某個章節之前,先知道「這個人可能和哪種衝突有關」,進遊戲後才比較容易記住他。
我以前很容易低估這種頁面的價值。做遊戲時會一直想把時間花在功能上,但對外展示時,玩家不會先打開 docs/GAME_STORY_BIBLE.md。他只會掃一眼首頁,看自己有沒有興趣。
Day6 提過素材管理時,Portal 圖鑑其實是很容易出問題的地方。現在 gameAssets 有 411 筆,類型分成:
每筆資料有 id、title、type、chapter、thumbnail、image、alt 和 order。畫面上則有幾個基本操作:
這裡沒有引入圖庫套件,也沒有做複雜狀態管理。selectedType、selectedChapter、searchKeyword、sortOrder、visibleCount 幾個變數就夠了。
Portal 圖鑑只是展示與瀏覽,不需要和遊戲 runtime 同步,也不需要保存玩家進度。用原生 DOM 把資料篩出來、排序、render 到 grid,維護起來最直接。
素材圖鑑的互動重點是 lightbox。index.html 裡先放好:
<div id="lightbox" class="lightbox" hidden>
...
</div>
src/portal/main.ts 裡則用 openAssetLightbox()、updateLightboxView()、initAssetLightbox() 管理開關與切換。點縮圖會打開全螢幕預覽;左右箭頭可以上一張、下一張;鍵盤也支援 Escape、ArrowLeft、ArrowRight。
這個 lightbox 沒有很花,但夠用。對 Portal 來說,重點不是做一個華麗相簿,而是讓玩家能真的看清楚場景、CG 和角色素材。尤其《九重燼》這種文字冒險,視覺資產是氛圍的一半;圖鑑如果只能看小縮圖,就有點浪費。
/ranking 是另一個小子系統。它有自己的 ranking.html、src/ranking/main.ts 和 src/ranking/style.css。
這頁做的是角色投票:玩家可以對角色獻花或丟雞蛋。前端會先呼叫 /api/votes 讀票數,再用 /api/vote 送出或取消投票;同一個瀏覽器投過哪個角色,會用 localStorage 記下來。後端票數儲存層在 api/_lib/votes.ts,支援 Vercel Marketplace 的 Upstash Redis / KV REST API 環境變數。
這裡也有一個務實設計:如果投票服務沒啟用,畫面仍可預覽榜單,只是按鈕不能投,狀態列會提示「投票系統尚未啟用」。這比整頁壞掉好很多,尤其 Portal 本來就承擔對外展示的責任。
index.html 的 <head> 目前有:
<title>
<meta name="description">
description 寫得算完整,有作品名稱、玩法類型、技術關鍵字、多分支、多結局、六位女主角等資訊。
但現在還沒有:
og:title、og:description、og:image
sitemap.xml
robots.txt
所以如果玩家把連結貼到社群平台,預覽卡片不一定好看;搜尋引擎也只拿得到最基本的頁面資訊。
我會先補 og:title、og:description、og:image 和 twitter:card。這幾個成本最低,效果最直接。sitemap.xml 和 robots.txt 可以接著補,尤其未來如果有更多獨立頁面,不想全部只靠首頁 fallback。
做完這頁後,我對 Portal 的看法有點變了。
它不是「遊戲外面那張宣傳頁」而已。它其實是作品的第二個介面:遊戲本體負責讓玩家做選擇,Portal 負責讓玩家理解這個選擇世界有多大。
這也是為什麼我不想把它塞進遊戲本體。play.html 應該專心服務遊玩;index.html 則可以大方展示世界觀、角色、素材、結局、開發歷程。兩邊都重要,但它們不該互相綁死。
Day28 結論:作品要被玩到,入口也得被好好設計。遊戲本體做得再完整,如果首頁講不清楚、素材看不到、分享出去沒有預覽,玩家可能還沒進宮就走了。
下一步,我會優先補社群分享標籤。不是為了 SEO 分數好看,而是讓每一次分享都像一張真正能邀請人進來的門票。