主題
網頁文字策略遊戲:技術棧選擇總結
遊戲定位
以文字為主的劇情遊戲,包含大量數據與策略模擬元素,在網頁上遊玩。開發模式為 vibe coding,以 Google Sheet 管理數據、Markdown 撰寫劇本腳本,搭配 AI 輔助生成程式碼。
技術棧選擇結論:React + TypeScript
為什麼不選專門的敘事引擎?
| 引擎 | 適合場景 | 不適合的原因 |
|---|---|---|
| Twine | 純互動敘事(choose-your-own-adventure) | 缺乏複雜狀態管理能力,不適合策略模擬 |
| ink (Inkle) | 分支劇本撰寫 | 擅長敘事但非遊戲邏輯引擎,可作為混合方案的劇本層 |
| Ren'Py | 有立繪的視覺小說 | Web 版體驗差(載入慢、體積大),不適合數據密集型遊戲 |
為什麼選 React + TypeScript?
- 狀態管理成熟:Zustand / Redux 等工具天生適合管理複雜的遊戲世界狀態(資源、屬性、關係、時間軸)
- 數據驅動設計:事件、角色、規則用 JSON/CSV 定義,程式碼只負責引擎邏輯,與內容分離
- UI 自由度高:策略遊戲需要的面板、儀表板、卡牌介面,前端框架處理最自然
- AI 生成品質最佳:Claude 等工具對 React + TypeScript 的程式碼生成品質是所有前端技術中最好的,最適合 vibe coding
- 與現有工作流相容:Google Sheet 匯出 CSV/JSON 直接餵入、Markdown 可用 remark/marked 解析
混合方案(可選)
如果劇情文本量很大,可將敘事部分交給 ink 處理(語法撰寫體驗好),遊戲邏輯由 TypeScript 負責。inkjs runtime 支援與遊戲引擎雙向溝通。
建議的系統架構
遊戲引擎核心(純 TypeScript,不碰 UI)
├── 世界狀態 (World State)
├── 事件系統 (Event Engine) — 條件判斷、觸發、結果
├── 模擬引擎 (Simulation) — 每回合的數值運算
├── 劇情管理 (Narrative) — 可嵌入 ink 處理對話分支
└── 存檔系統 (Save/Load)
UI 層(React 元件)
├── 主畫面、地圖、角色面板
├── 對話介面
├── 事件卡牌 / 選擇介面
└── 數據儀表板建議的資料架構
project/
├── data/
│ ├── characters.csv ← 從 Google Sheet 匯出
│ ├── items.csv
│ ├── events.csv ← 事件條件、觸發規則、結果
│ └── balance.csv ← 數值參數表
├── scripts/
│ ├── chapter-01.md ← 劇情腳本
│ ├── chapter-02.md
│ └── ...
├── src/
│ ├── engine/ ← 遊戲邏輯(純 TypeScript,不碰 UI)
│ ├── components/ ← React UI 元件
│ └── editors/ ← 視覺化編輯器(後期開發)核心原則:CSV 和 Markdown 是 single source of truth,日常內容迭代在 Google Sheet 和 .md 裡完成,不需要碰程式碼。
Markdown 劇本格式設計範例
markdown
# 第一章:起點
你走進了村長的辦公室,桌上散落著文件。
> [條件: 智力 > 5] 你注意到文件裡藏著一張地圖。
> [條件: 魅力 > 7] 村長對你的到來顯得格外熱情。
## 選擇
- [查看文件] -> scene:examine_documents {好奇心 +1}
- [跟村長交談] -> scene:talk_to_chief {村長好感 +2}
- [直接離開] -> scene:leave {無變化}人類讀寫直覺,同時可被解析器結構化處理。
視覺化編輯器開發優先順序
不建議一開始就做編輯器。先用 Google Sheet + Markdown 手動跑通流程,確定數據結構穩定後,依痛點優先開發:
- 劇情流程圖 — 檢視分支結構與死路(推薦使用 React Flow)
- 數值模擬器 — 快速測試策略路線的數值平衡
- 事件條件編輯器 — 處理 Google Sheet 不擅長的複雜巢狀條件
後端方案:Supabase(BaaS)
為什麼需要後端?
Phase 1 為純前端(localStorage 存檔),但後續功能需要伺服器支援:
| 功能 | 需求階段 | 為什麼不能純前端 |
|---|---|---|
| 跨裝置存檔同步 | Phase 2-3 | localStorage 綁定瀏覽器,換裝置就丟失 |
| 源血基因碼交換 | Phase 5 | 跨玩家資料必須存在共享伺服器 |
| 公共遺骸事件池 | Phase 5 | 野外死亡遺物流入公共池,需全域資料庫 |
| 付費驗證 | Phase 5 | 付費狀態需要可信的伺服器端記錄 |
| 反作弊 | Phase 5 | 存檔驗證與源血品質校驗需後端參與 |
為什麼選 Supabase?
| 考量 | Supabase | Firebase | 自架(VPS + PostgreSQL) |
|---|---|---|---|
| 資料庫 | PostgreSQL(成熟、可遷移) | Firestore(NoSQL、廠商鎖定) | PostgreSQL |
| 認證 | 內建 Auth(支援匿名、第三方) | 內建 Auth | 需自行整合 |
| 即時同步 | Realtime subscriptions | Realtime | 需自建 WebSocket |
| 檔案儲存 | Storage(S3 相容) | Cloud Storage | 需自行架設 |
| 邊緣函數 | Edge Functions(Deno) | Cloud Functions | 需自行部署 |
| 免費額度 | 500MB DB、1GB Storage、50K MAU | Spark 方案有限制 | 無,需自費 |
| 廠商鎖定風險 | 低(標準 PostgreSQL,可隨時自架) | 高(Firestore 專有查詢語法) | 無 |
| 維運負擔 | 幾乎零 | 幾乎零 | 需自行維運 |
結論:Supabase 提供完整的 BaaS 功能,底層為標準 PostgreSQL,廠商鎖定風險低。免費額度足以支撐早期開發與測試。若未來需要遷移,資料庫可直接匯出至自架 PostgreSQL。
Supabase 服務對應遊戲功能
| Supabase 服務 | 遊戲功能 |
|---|---|
| Auth | 玩家帳號(匿名登入→綁定社群帳號)、付費狀態驗證 |
| PostgreSQL | 存檔同步、源血基因碼池、公共遺骸事件池、玩家設定檔 |
| Storage | 存檔歷史版本(JSON 快照)、未來可能的 UGC 內容 |
| Edge Functions | 付費回呼(ECPay/藍新金流)、源血品質驗證、反作弊校驗 |
| Realtime | 暫不使用;未來若加入即時社群功能可啟用 |
資料庫 Schema 概念設計
sql
-- 玩家基本資料
profiles (
id UUID PRIMARY KEY REFERENCES auth.users,
display_name TEXT,
is_paid BOOLEAN DEFAULT FALSE,
paid_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
)
-- 存檔(每玩家最多 3 欄位)
saves (
id UUID PRIMARY KEY,
user_id UUID REFERENCES profiles(id),
slot INTEGER CHECK (slot BETWEEN 1 AND 3),
save_data JSONB, -- 完整 SaveData 結構
version INTEGER, -- 存檔版本號,用於遷移
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id, slot)
)
-- 存檔歷史(自動備份)
save_history (
id UUID PRIMARY KEY,
save_id UUID REFERENCES saves(id),
save_data JSONB,
saved_at TIMESTAMPTZ DEFAULT NOW()
)
-- 源血基因碼池(跨玩家共享)
source_blood_pool (
id UUID PRIMARY KEY,
code TEXT UNIQUE, -- WL-XXXX 短碼
donor_user_id UUID REFERENCES profiles(id),
gene_data JSONB, -- 加密的基因碼資料
quality INTEGER CHECK (quality BETWEEN 1 AND 5),
is_consumed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
)
-- 公共遺骸事件池(野外死亡遺物流轉)
public_remains (
id UUID PRIMARY KEY,
origin_user_id UUID REFERENCES profiles(id),
region TEXT, -- 死亡區域編碼 R##
remain_data JSONB, -- 遺物內容(技能、遺言片段、物資)
quality_weight FLOAT, -- 品質權重(影響被抽到的機率)
created_at TIMESTAMPTZ DEFAULT NOW()
)所有表啟用 Row Level Security(RLS),確保玩家只能讀寫自己的存檔,源血池和遺骸池為全域可讀但僅由 Edge Functions 寫入。
整合時程
| 階段 | 後端工作 |
|---|---|
| Phase 1 | 純前端,不碰 Supabase。localStorage 存檔 |
| Phase 2-3 | 引入 Supabase Auth(匿名登入)+ saves 表,實現跨裝置存檔同步。保留 localStorage 作為離線快取 |
| Phase 5 | 啟用 source_blood_pool、public_remains、Edge Functions。實裝付費驗證(ECPay/藍新)、源血交換、遺骸流轉 |
成本估算
| 階段 | 預估 MAU | Supabase 方案 | 月費 |
|---|---|---|---|
| 開發 + Alpha 測試 | < 100 | Free | $0 |
| Beta 公測 | 100-1,000 | Free | $0 |
| 正式營運初期 | 1,000-5,000 | Free(接近上限) | $0 |
| 正式營運成長 | 5,000-50,000 | Pro | ~$25/月 |
Free 方案(500MB 資料庫、1GB 儲存、50K MAU)足以支撐到正式營運初期。SaveData 以 JSONB 存放,單筆存檔預估 10-50KB,500MB 可容納數萬份存檔。
風險與對策
| 風險 | 對策 |
|---|---|
| RLS 設定錯誤導致資料洩漏 | 上線前撰寫 RLS 測試腳本,模擬跨用戶存取 |
| 離線體驗降級 | Phase 2+ 採混合架構:localStorage 為主、Supabase 為同步層。無網路時降級為純本地 |
| 廠商鎖定 | Supabase 底層為標準 PostgreSQL + S3 Storage,可隨時匯出遷移至自架 |
| Edge Functions 冷啟動延遲 | 付費驗證回呼為非即時場景,冷啟動可接受 |
其他備選方案參考
| 方案 | 適用情境 |
|---|---|
| Phaser.js | 需要較多視覺互動(地圖點擊、拖放、動畫),可搭配 React 做 UI 覆蓋層 |
| Vue + TypeScript | 與 React 方案等價,依個人偏好選擇 |
| 純 ink + inkjs | 若策略模擬需求較輕,劇情為絕對核心 |