第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 章(契约)与后续各章会被反复引用:

  1. 逻辑与渲染分离 —— 计分、判定、调度等规则写在纯 TS 模块(*.logic.ts)里,不 import Phaser,可以脱离浏览器单测。ESLint 还会禁止这类模块使用 Math.random 与 Date.now,保证测试可复现。
  2. 游戏不得自己碰环境 —— 存档、音频、生命周期、分享一律通过注入的 GameContext.services,禁止游戏内部直接 localStorage / fetch / wx.*。这是将来上微信小游戏或云存档的唯一低成本出口。
  3. 每个游戏必须能被「无头直达」 —— 支持 #/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 章(规范与经验):儿童向设计规范、踩坑记录与常见问题。

后续章节引用的所有代码都来自仓库当前版本,示例经过精简,但接口与行为与源码一致。