第01章 - CHAGS项目概述与设计理念
1.1 CHAGS 是什么
CHAGS(Chinese Historical Anecdotes Game Series,中国历史典故儿童游戏合集)是一个面向 6–12 岁儿童的开源 H5 游戏合集:一个典故一个小游戏,让小朋友在「做」故事的过程中记住典故。
项目源码托管在 https://github.com/znlgis/CHAGS,采用 MIT 许可协议。它不是一个传统意义上的「游戏引擎」,也不是一套课程软件,而是一个把「内容、玩法、壳、工具链」完整拆开的工程样本:
- 十二个典故全部实现并可玩通:曹冲称象、盘古开天辟地、后羿射日、女娲补天、精卫填海、夸父追日、愚公移山、神农尝百草、大禹治水、太公钓鱼、烽火戏诸侯、杞人忧天;
- 全部游戏、界面、语音、文档支持中英文双语一键切换;
- 手机浏览器即开即玩,同一份站点产物还能打成 Windows 便携 exe(约 3.3 MB)与安卓 apk;
- 无后端、无广告、无账号,进度只存本机;
- 一条
pnpm verify命令覆盖类型检查、Lint、单测、内容校验、构建与体积守卫,端到端测试在真实浏览器里把每部游戏玩一遍。
本教程分为「使用」与「开发」两部分。使用部分回答「怎么把它跑起来、玩起来、发出去」;开发部分回答「它的架构为什么这样设计、每个包各管什么、怎么从零加一个新典故、怎么保证质量」。每章相对独立,但建议按顺序阅读。
1.2 产品定位决定的技术取向
这个项目的所有技术决策都从一句产品定位出发:重点不是「玩过」,而是「记住 + 理解寓意」。
对儿童产品来说,这句话会推导出一系列具体的约束,它们最终都落进了代码:
| # | 产品诉求 | 技术约束 |
|---|---|---|
| 1 | 家长随手就把手机给孩子,不能要求装 App | H5 优先,扫码即玩;不做原生独占 |
| 2 | 小朋友注意力短,单局 3–5 分钟 | 无长连接、无加载等待,随时中断可恢复 |
| 3 | 场景常在车内、老家、弱网 | 静态资源、本地存档 |
| 4 | 家里的旧安卓机、旧 iPad 也要能跑 | 2D、体积预算、按住墙钟计时而不是帧率 |
| 5 | 核心指标是「记住」 | 内容数据结构内建关键抉择、常见误解、迁移练习、复习间隔 |
| 6 | 合集要能长到 30–100 个典故 | 模块化 + 契约化 + 玩法模板库,新增典故 = 加一个包 + 一份 JSON |
| 7 | 儿童产品,家长会问隐私 | 无广告 SDK、无社交、不采集儿童个人信息,数据默认只存本机 |
| 8 | 主要由 AI 辅助/主导开发 | 全文本资产(可 diff)、可单测、一条命令验证 |
第 8 条不是附带说明,而是硬约束:它直接否掉了 Cocos / Unity 这类「编辑器里拖拽出场景」的方案 —— 那些工程的场景文件是二进制或巨型 JSON,无法 review、无法 diff、无法被自动化验证。所以 CHAGS 选择了代码即资产的路线:游戏画面全部由代码绘制,玩法规则写在纯 TypeScript 模块里,可以用文本工具完成全部开发与审查。
1.3 十二部游戏一览
十二个典故目前全部跑通。每一部的设计都遵循同一条原则:每部游戏有一个只属于它的动词,避免「第 N 部又是换个皮的连点游戏」。
| 典故 | 数据定性(kind) | 动词 | 玩法骨架 |
|---|---|---|---|
| 曹冲称象 | 信史(三国) | 称 | 拖大象上船 → 刻水痕 → 换石头等量代换 → 石头过秤 |
| 盘古开天辟地 | 神话(上古) | 撑 | 连点蛋壳炸开 → 连点撑天一万八千年 → 拖六团光归位 |
| 后羿射日 | 神话(上古) | 射 | 拉弓瞄准射日,射九留一;把最后一个也射掉会天黑 |
| 女娲补天 | 神话(上古) | 补 | 按形状(不靠颜色)把五色石拖进五个洞 |
| 精卫填海 | 神话(上古) | 填 | 连点:衔石投海,海上浮起一小片陆地 |
| 夸父追日 | 神话(上古) | 追 | 长按奔跑、松手减速;到河渭点一下喝水再追 |
| 愚公移山 | 信史(战国) | 挖 | 连点挖山;三成智叟出现、六成锄头交给下一代 |
| 神农尝百草 | 传说(上古) | 尝 | 把草拖到嘴边先尝,再分进对应木牍 |
| 大禹治水 | 传说(夏) | 疏 | 先连点筑堤直到决口,再手指划出三段河道 |
| 太公钓鱼 | 信史(商周) | 等 | 主线只有「等」;诱惑会把终点后移但永远等得到 |
| 烽火戏诸侯 | 信史(西周) | 点 | 点一次烽火,信任少一格;第四次真有事,没人来 |
| 杞人忧天 | 信史(战国) | 解 | 把四个「想明白」拖进四个担忧泡泡,逐个破除 |
表中「数据定性」是内容 JSON 里
kind字段的取值(神话 / 传说 / 信史),典故馆里的徽章直接读它。典故馆当前按yearApprox排序,公元前为负数 —— 神话排在时间轴最前(如盘古是 -9999),信史落在后面,天然形成「从神话到信史」的时间纵深。
它们背后共享的是同一套工程结构:分幕故事 + 玩法场景 + 典故卡,由 defineGame() 统一组装;玩法骨架来自模板库,各典故只提供自己的内容数据、画面绘制与少量定制代码。
1.4 记忆工程:五层机制与代码落点
CHAGS 对「让孩子记住」这件事的回答不是策划文档里的一句话,而是数据结构与代码的一部分。项目把记忆拆成五层,每层都有对应的技术落点:
| 层 | 机制 | 技术落点 |
|---|---|---|
| L1 | 情境代入:不是读故事,是「做」故事 | story 模板分幕绘本 + 旁白;玩家扮演典故中的角色 |
| L2 | 关键抉择:自己找到那个办法 | 内容里的 keyMoment + misconceptions;关键动作由交互本身承载 |
| L3 | 多通道编码:看 + 听 + 做 + 说 + 选 | 画面、旁白(TTS)、操作、跟读、复习题 |
| L4 | 收集与结构:典故卡 + 图鉴 | 通关解锁 card:unlocked,卡片按年代落位 |
| L5 | 间隔重复 + 迁移:1/3/7/21 天后再见 | ReviewScheduler(简化版 SM-2),复习关卡从含 transfer 的测验生成 |
一个具体例子:曹冲称象的关键抉择不做成文字选择题,而是让玩家亲手走完「直接称 → 秤垮了 → 大象上船 → 刻水痕 → 换石头 → 石头过秤」的过程。孩子是自己撞见「称不动」这个问题的,代换的办法也是自己做出来的 —— keyMoment 数据仍然保留,但它提供的是揭晓语与复习题,渲染选择题这件事被有意放弃了。
1.5 技术选型速览
| 决策项 | 选型 | 一句话理由 |
|---|---|---|
| 游戏引擎 | Phaser 4.2 | 纯代码、可 diff、可单测,适合「一个小游戏一个小模块」的合集形态 |
| 应用外壳 | Vue 3.5(SFC + Composition API)+ Pinia + Tailwind 4 | 大厅/图鉴/设置是 UI 密集型,SFC 写得快 |
| 语言 | TypeScript 5.9.3(strict) | 引擎与游戏之间靠接口契约隔离;版本锁定有实测原因(见第 2 章) |
| 构建 | Vite 8 + pnpm workspace | 原生多入口 + 代码分割,每个游戏自动独立 chunk |
| 内容 | JSON + zod schema,构建期校验 | 换典故 = 换内容,不用改代码 |
| 玩法 | 玩法模板库(story / substitution / tap / place / aim / sort / trace / wait / quiz / reward) | 让「第 30 个典故」的成本远低于「第 1 个」 |
| 存储 | IndexedDB(本地优先)+ 可替换接口 | 无后端、无广告、无个人信息采集 |
| 测试 | Vitest + vue-tsc + Playwright + 内容校验脚本 + 体积守卫 | pnpm verify 全绿才算完成 |
| 后端 | 完全不做 | 纯静态站点,部署最便宜、最稳、最合规 |
1.6 三条架构铁律
整个仓库的所有设计都服从三条铁律,它们在第 4 章(契约)与后续各章会被反复引用:
- 逻辑与渲染分离 —— 计分、判定、调度等规则写在纯 TS 模块(
*.logic.ts)里,不 import Phaser,可以脱离浏览器单测。ESLint 还会禁止这类模块使用Math.random与Date.now,保证测试可复现。 - 游戏不得自己碰环境 —— 存档、音频、生命周期、分享一律通过注入的
GameContext.services,禁止游戏内部直接localStorage/fetch/wx.*。这是将来上微信小游戏或云存档的唯一低成本出口。 - 每个游戏必须能被「无头直达」 —— 支持
#/play/<id>?autoplay=1这类调试入口,否则自动化测试与 AI 协作都做不了。
1.7 仓库结构总览
CHAGS/
├─ apps/
│ ├─ site/ # Vue 3.5 外壳(唯一的产品代码入口)
│ ├─ desktop/ # Tauri 2 桌面壳(Windows 便携 exe)
│ └─ mobile/ # Capacitor 8 安卓壳(apk)
├─ packages/
│ ├─ game-kit/ # 契约层:GameModule / GameContext / 事件 / 平台接口(不含 Phaser)
│ ├─ content/ # 典故 JSON + zod schema + 双语补丁
│ ├─ templates/ # 玩法模板库 + 内容校验 CLI
│ ├─ phaser-kit/ # Phaser 启动器、主题、基础场景、卡通绘制、拖拽
│ ├─ platform/ # Web 平台实现:IndexedDB / WebAudio / TTS / 生命周期
│ ├─ progress/ # 存档、图鉴、复习调度(纯 TS)
│ └─ i18n/ # 界面文案表与取词器
├─ games/ # 十二个游戏包,每个典故一个
│ └─ caochong-elephant/ ... pangu/ ... dayu/ ...
├─ e2e/ # Playwright 端到端
├─ scripts/ # 体积守卫、apk 构建、图标生成
├─ docs/ # 技术方案、典故方案、打包方案
└─ package.json # 根脚本:dev / verify / e2e / dist:win / dist:apk ...
packages/game-kit 与 packages/content 不含 Phaser 依赖,所以 Vue 外壳可以安全地 import 它们;Phaser 只出现在 games/*、packages/templates 与 packages/phaser-kit 里,外壳首屏永远不会加载那 350 KB 的引擎。
1.8 一个典故的完整生命周期
为了给后面的章节建立一张全局地图,先看一个典故从「打开」到「收下典故卡」的完整流程:
大厅(HallView)点击「开始玩」
└─ hash 路由 #/play/<id> → PlayView 动态 import 游戏包(独立 chunk)
└─ defineGame().mount(ctx)
├─ createStoryScene 分幕绘本(2 幕,点哪都行)
├─ 玩法场景(模板或专属场景,一到多个)
│ 期间持续 emit 事件:scene:enter / beat:enter / choice:made / level:complete ...
└─ createRewardScene 典故卡(星星、寓意、出处)+ emit card:unlocked
└─ session:exit → requestExit() → 外壳回大厅
外壳收到 level:complete → ProgressManager 记录星星与复习计划 → IndexedDB 落盘
这条链路里的每一步都对应一个明确的模块:
- 外壳(第 5 章):路由、注册表、视图、Pinia 状态;
- 契约(第 4 章):
GameModule/GameContext/ 事件系统; - 内容(第 6 章):
AllusionContent数据模型; - 模板(第 7 章):
createStoryScene/createSubstitutionScene/ … 等场景工厂; - 渲染工具箱(第 8 章):
BaseScene、主题、卡通绘制、拖拽控制器; - 平台层(第 9 章):存档、音频、生命周期;
- 进度层(第 10 章):星星、复习调度;
- 双语(第 11 章):界面文案、内容翻译、语音。
1.9 与「使用教程」相关的三条快速通道
如果只是想把项目跑起来体验,可以直接跳到第 3 章;以下是三个最常用的入口:
pnpm install # 安装依赖(需要 Node 24 + pnpm 11)
pnpm dev # 开发服务器 http://127.0.0.1:5183
pnpm verify # 提交前必须全绿:类型 → Lint → 单测 → 内容校验 → 构建 → 体积
调试直达链接(开发时非常常用):
http://127.0.0.1:5183/?autoplay=1#/play/caochong-elephant # 自动通关,用于无头验证
http://127.0.0.1:5183/?lang=en#/play/pangu # 英文界面 + 英文内容
1.10 本教程的章节安排
- 第 1–3 章(使用入门):项目概况、技术栈与工程结构、环境准备与快速开始;
- 第 4–11 章(架构与开发):核心契约、Vue 外壳、内容模型、模板库、渲染工具箱、平台层、进度系统、双语实现;
- 第 12–14 章(实战与交付):从零开发一个新典故、测试体系与质量门禁、构建打包与分发;
- 第 15–16 章(规范与经验):儿童向设计规范、踩坑记录与常见问题。
后续章节引用的所有代码都来自仓库当前版本,示例经过精简,但接口与行为与源码一致。