Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/ai-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
- 【已实现】生成的图片先预览,接受后才存入 `assets/`。
- 【已实现】声音也是这样:模型只挑参数(`op: "sound"`),采样在浏览器里合成(`src/lib/audio/render.ts`),
预听的 WAV 就是最后存进 `assets/` 的那一份。参数一律夹紧到安全范围(音高 20–8000 Hz、增益 ≤ 0.6、时长 ≤ 4 秒)。
不登录也能用预设和编辑器做出同一份 WAV。音频文件(`*.wav`、`*.ogg`、`*.mp3`、`*.aiff`)和小助手、WebMCP 都改不了。
- 【目标】删除文件必须在改动列表里单独列出并单独确认。
- 【目标】来自 WebMCP 外部 Agent 的写入走同一个候选与采用流程,没有「自动采用」。
- 【已实现】模型结果不合规时整体丢弃,不展示半成品,候选也不会生成(E2E:`a broken model answer changes nothing`)。
Expand All @@ -83,7 +84,7 @@
- 【已实现】普通 pygame-ce,不发明 GameKit API。
- 【已实现】帧循环里有 `await asyncio.sleep(0)`(通常紧跟 `pygame.display.flip()`);
不用 `time.sleep`、`pygame.time.wait`、`pygame.time.delay`。
- 【已实现】字体用 `pygame.font.Font(None, size)` 或项目里的 `.ttf`;网页音频用 OGG Vorbis。
- 【已实现】字体用 `pygame.font.Font(None, size)` 或项目里的 `.ttf`;网页音频用 16-bit PCM WAV 或 OGG Vorbis。
- 【提示词】代码面向孩子阅读:变量名有意义、函数短、关键处有简短中文注释;不引入孩子还没学过的高级写法,
除非项目里已经在用。
- 【提示词】沿用项目已有的结构和命名;只改被要求的范围。
Expand Down
31 changes: 20 additions & 11 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,8 @@ Worker 对每个 `/api/*` 请求校验 Access JWT(签名、aud、iss、过期
- 声音生成(仅授权用户)

音效与音乐两种。模型只挑参数(振荡器/包络/扫频,或 bpm/调式/和弦/三个声部),**不写采样**;
浏览器按同一份参数确定性地合成 PCM 并写成 16-bit 单声道 22.05 kHz WAV,预览播放的就是最后存进 assets/ 的那一份,
面板还会写上「在代码里这样用:pygame.mixer.Sound(...)」。入口 `worker/ai.ts`(提示词与参数校验)、
`src/lib/audio/spec.ts`、`src/lib/audio/render.ts`(纯函数合成)、`src/lib/audio/wav.ts`、`src/ui/Overlays.tsx`。
浏览器按同一份参数确定性地合成 PCM 并写成 16-bit 单声道 22.05 kHz WAV。不登录时用预设和滑杆做同一件事。
入口 `worker/ai.ts`(提示词与参数校验)、`src/lib/audio/`、`src/ui/SoundEditor.tsx`。编辑器见下一条。

- AI 素材生成(仅授权用户)

Expand Down Expand Up @@ -227,7 +226,7 @@ Worker 对每个 `/api/*` 请求校验 Access JWT(签名、aud、iss、过期

小助手和 WebMCP 不能改写 `*.anim.json`。入口 `src/ui/PixelAnimPanel.tsx`、`src/lib/pixel/anim.ts`、`src/lib/pixel/film.ts`;保存仍走 `savePixelImage`。

还没做:音效编辑器、让小助手按这段说明检索素材。
还没做:让小助手按这段说明检索素材。音效编辑器已在阶段 D 交付。

- 瓦片地图编辑器(阶段 C,不登录可用)

Expand All @@ -241,13 +240,23 @@ Worker 对每个 `/api/*` 请求校验 Access JWT(签名、aud、iss、过期

入口 `src/ui/MapEditor.tsx`、`src/ui/MapNewDialog.tsx`、`src/lib/map/`;写入走 `src/studio/store.tsx` 的 `saveMap`。

还没做:用项目里已有的 PNG 换瓦片图、音效编辑器。
还没做:用项目里已有的 PNG 换瓦片图。

- 音效编辑器(阶段 D,不登录可用)

「看代码」的素材区能打开声音。预设有跳跃、轻跳、金币、碰到蘑菇和一段走路音乐,不登录也能用。滑杆改波形、音高、包络、音量,也能截取一头一尾。勾上循环时,首尾会接上,免得 `play(-1)` 爆音。音乐能改速度和小节,主旋律、低音、踩镲可以单独听。画面上的包络和波形就是正在听的那一份。点保存,写进项目的 WAV 和刚才试听的字节相同,可以撤销。写入失败时项目不变,声音还留在编辑器里。

「在舞台上听听」会先给看 `main.py` 变成什么样,确认后才写。那段代码是普通 pygame:点一下画面再 `mixer.init()` 和 `Sound.play`,并且 `await asyncio.sleep(0)`。官方音效在素材库的「森林声音」包里,和森林的图用同一套标签连着(跳跃对角色,金币对金币,蘑菇对蘑菇)。轻跳是跳跃的一种变体,改参数就行,不用重新画图。孩子收录的声音存在这台电脑上,清单字段和官方包一样(许可、作者、来源、署名、hash),格式不对的不会收。复制进项目之后,游戏读的是项目里的文件。

浏览器能播的是 16-bit PCM WAV,和通过检查的 OGG Vorbis。MP3、AIFF、坏 WAV、Opus 会在问题里说明。小助手和 WebMCP 不能改这些音频文件,也不能改素材库记录。

入口 `src/ui/SoundEditor.tsx`、`src/lib/audio/`、`src/lib/assets/sounds.ts`;写入走 `src/studio/store.tsx` 的 `saveSound`。

- 内置素材库(阶段 A,不登录可用)

顶栏和「看代码」的素材区都能打开素材库。包的说明是版本化的 `asset-pack` manifest(稳定 id、版本、路径、媒体类型、像素尺寸、动画帧和碰撞、中文标签、主题、预览、许可/作者/来源/署名、内容 hash),语义不靠文件名猜。现在收录一套 GameKit 原创、CC0 的「森林冒险」(角色、地形、道具、界面)。缩略图和包说明在打开素材库之后才加载,不进首屏脚本。
复制进项目的是文件本身,记在 `assets/library/<包>/…`,版本写在 `assets/library.json`。运行和导出用的是这份副本,不访问素材库。同名、重复、更新和替换都会先说明,不会静默盖掉;盖掉之后可以撤销。加载示例是普通的 `pygame.image.load`。小助手和 WebMCP 不能把素材库文件写进项目,也不能改那份版本记录。
入口 `src/ui/LibraryDialog.tsx`、`src/lib/assets/`、`public/asset-packs/`。还没做:更多主题包、孩子自己的跨项目素材、音频库、配方,以及让小助手检索素材库(本阶段只保证它不能绕过确认去写)。动画编辑器已在阶段 B 交付,地图编辑器已在阶段 C 交付。
入口 `src/ui/LibraryDialog.tsx`、`src/lib/assets/`、`public/asset-packs/`。还没做:更多主题包、孩子自己跨项目的图片、配方,以及让小助手检索素材库(本阶段只保证它不能绕过确认去写)。动画编辑器已在阶段 B 交付,地图编辑器已在阶段 C 交付。官方音效和孩子收录的声音已在阶段 D 放进同一套清单。

- 导出

Expand All @@ -257,7 +266,7 @@ Worker 对每个 `/api/*` 请求校验 Access JWT(签名、aud、iss、过期

推送 `main` 后 CI 单元测试、类型检查、构建、E2E,全部通过后 `wrangler deploy`(`.github/workflows/ci.yml`)。

尚不存在:云同步、PWA 清单与离线外壳、教程与配方、音效编辑器(单张像素图、阶段 B 精灵动画、阶段 C 瓦片地图和阶段 A 素材库已有)、AI 按人配额、
尚不存在:云同步、PWA 清单与离线外壳、教程与配方(单张像素图、阶段 B 精灵动画、阶段 C 瓦片地图、阶段 D 音效编辑和阶段 A 素材库已有)、AI 按人配额、
对话历史持久化(一轮的对话刷新后就没了,设计卡和作品在)。

## L0 分阶段(Issue #14)
Expand All @@ -269,7 +278,7 @@ Worker 对每个 `/api/*` 请求校验 Access JWT(签名、aud、iss、过期
| A. 统一内置资产库闭环 | 已交付 | 版本化 manifest、一个 CC0 主题包、匿名浏览/预览、复制进项目并固定版本、pygame 示例、离线导出。见上「内置素材库」 |
| B. 精灵与动画编辑器 | 已交付 | 帧时间轴、精灵表、多动作、选区与图层。产物是 PNG + 可读 JSON,播放是普通 pygame。见上「精灵与动画编辑器」 |
| C. 可视化瓦片地图 | 已交付 | 瓦片调色板、点绘/矩形/填充/擦除、图层、背景、出生点、物件标记、碰撞标注和预览。产物是 JSON + CSV + PNG,并附可读的 pygame 加载示例。见上「瓦片地图编辑器」 |
| D. 音效与音乐编辑 | 未做 | 在现有合成上加试听和裁剪,官方与生成的声音进同一套索引,导出后离线可播 |
| D. 音效与音乐编辑 | 已交付 | 预设、包络和波形、裁剪、音量、循环、音乐的节奏和声部。官方音效与收录的声音走同一套清单。导出的 WAV 离线可播。见上「音效编辑器」 |
| E. 游戏配方与分步挑战 | 未做 | 完整可玩配方、分步目标,用静态检查或运行证据判断,不覆盖旧项目 |
| F. AI / WebMCP 资源检索 | 未做 | 只读检索和提议;写入仍走候选和采用。阶段 A 只守住了「不能直接写素材库」 |
| G. 离线与发布验证 | 未做 | 在阶段 A 的导出契约之上,补发布演练和断网运行已缓存游戏 |
Expand Down Expand Up @@ -313,7 +322,7 @@ Worker 对每个 `/api/*` 请求校验 Access JWT(签名、aud、iss、过期

- 内置创作工具

像素精灵编辑器(单张图片的编辑与新建已完成;多帧、精灵表和动作说明已在阶段 B 交付)、瓦片地图编辑器(阶段 C 已交付)、音效合成器(本地参数化生成 OGG)。产物是普通的
像素精灵编辑器(单张图片的编辑与新建已完成;多帧、精灵表和动作说明已在阶段 B 交付)、瓦片地图编辑器(阶段 C 已交付)、音效编辑器(阶段 D 已交付,本地参数化生成 WAV)。产物是普通的
PNG/OGG/数据文件,代码用标准 pygame 读取。不依赖 AI,未登录也可用。服务于「打开就能做游戏」。

- 小助手覆盖全流程
Expand Down Expand Up @@ -410,9 +419,9 @@ PNG/OGG/数据文件,代码用标准 pygame 读取。不依赖 AI,未登录
| 游戏配方与教程 | 未实现 | 中 | ❌ 缺口 | ❌ 缺口 | 不适用 | 不适用(加载总是新建项目) | 无 |
| 内置素材库 | 已有(阶段 A:一个「森林冒险」包。更多主题、音频库、配方、AI 检索未做;动画见阶段 B,地图见阶段 C) | 高(覆盖作品、版权) | ✅(匿名浏览、筛选、预览许可与来源 → 复制进项目 → pygame 在舞台加载 → 源码包和网页包里都有这份文件;加入时没有 `/api/` 请求) | ✅(同名先问再保留/另存/替换;重复复制不写第二份;hash 不符或许可/来源为空则校验失败,构建会被挡住) | ✅ 匿名可用。小助手和 WebMCP 仍不能改 `assets/` 里的二进制,也不能改 `assets/library.json` | ✅(替换后撤销回到旧文件;复制后撤销去掉这份;删进回收站可以恢复) | `tests/e2e/library.spec.ts`;`src/lib/assets/*.test.ts`;构建时 `vite.config.ts` 的素材包检查 |
| 像素编辑器 / 新建图片 | 已有(单张图片的画笔、保存和撤销;多帧见下一行) | 高(覆盖孩子已有素材、作品丢失) | ✅(新建 → 画 → 保存 → 项目里出现 PNG、预览里 pygame 加载;打开已有 PNG → 改 → 保存 → 撤销还原;另存为新图片;触控与笔;导出包字节一致;生成素材继续改像素) | ✅(非法尺寸被拒;同名不覆盖、自动加序号;未保存关闭先问、继续画/不保存/保存;写入失败后画布保留、可重试;缓冲区/填充越界与死循环、PNG 往返逐字节、alpha 保持、解压体积受表头限制、撤销占用有上限的单元测试;生成素材里「保存并关闭」不会再弹出空的生成面板) | ✅ 匿名可用(不发任何 `/api/` 请求;不新增 AI 工具,小助手与 WebMCP 没有改 assets/ 的能力) | ✅(写入失败不留半成品,重试成功;覆盖可用工作室撤销还原;新图保存后可撤销) | `tests/e2e/pixel.spec.ts`(14 条);`src/lib/pixel/*.test.ts`(单张图画笔仍由 `buffer`、`png`、`rules`、`history`、`session`、`save`、`export` 覆盖;多帧见下一行) |
| 精灵与动画编辑器 | 已有(阶段 B。音效编辑器仍未做) | 高(覆盖已有素材、帧和说明对不上) | ✅(匿名:画多帧 → 设时长和动作 → 保存 → 重新打开后帧序和时长还在 → PNG 与 JSON 的像素、alpha、帧序可比对 → 源码包里两份文件都在 → pygame 在舞台打出 `ANIM-OK` 和 `ANIM-PIXEL`) | ✅(坏的动画说明按整张图打开且不改文件;写入失败时 PNG 和 JSON 都不留下,画布还在,重试成功;选区移动、图层显隐、洋葱皮、复制/删除帧、精灵表导入可撤销) | ✅ 匿名可用,不发 `/api/`。小助手和 WebMCP 不能改写 `*.anim.json` | ✅(失败写入不留半成品,重试成功;试播先确认,工作室撤销回到试播前的代码,精灵表还在) | `tests/e2e/anim.spec.ts`;`src/lib/pixel/anim.test.ts`、`film.test.ts`、`save.test.ts`;`src/lib/ai.test.ts`;`worker/mcp.test.ts` |
| 精灵与动画编辑器 | 已有(阶段 B) | 高(覆盖已有素材、帧和说明对不上) | ✅(匿名:画多帧 → 设时长和动作 → 保存 → 重新打开后帧序和时长还在 → PNG 与 JSON 的像素、alpha、帧序可比对 → 源码包里两份文件都在 → pygame 在舞台打出 `ANIM-OK` 和 `ANIM-PIXEL`) | ✅(坏的动画说明按整张图打开且不改文件;写入失败时 PNG 和 JSON 都不留下,画布还在,重试成功;选区移动、图层显隐、洋葱皮、复制/删除帧、精灵表导入可撤销) | ✅ 匿名可用,不发 `/api/`。小助手和 WebMCP 不能改写 `*.anim.json` | ✅(失败写入不留半成品,重试成功;试播先确认,工作室撤销回到试播前的代码,精灵表还在) | `tests/e2e/anim.spec.ts`;`src/lib/pixel/anim.test.ts`、`film.test.ts`、`save.test.ts`;`src/lib/ai.test.ts`;`worker/mcp.test.ts` |
| 瓦片地图编辑器 | 已有(阶段 C) | 高(覆盖已有关卡、半截文件) | ✅(匿名:画关 → 碰撞、出生点、物件 → 保存 → 重新打开还原 → 导出地图包再导入一致 → 源码包里有 JSON/CSV/PNG → pygame 打出 `MAP-OK` 碰撞和出生点;加入时没有 `/api/` 请求) | ✅(非法地图说明原因且原文件不变;写入失败时不留半截文件,画面还在,重试成功) | ✅ 匿名可用,不发 `/api/`。小助手和 WebMCP 不能改写 `*.map.json`、`maps/*.csv`、`*.tileset.png` | ✅(失败写入不留半成品,重试成功;试玩先确认,工作室撤销回到试玩前的代码,地图文件还在) | `tests/e2e/map.spec.ts`;`src/lib/map/*.test.ts`;`src/lib/ai.test.ts`;`worker/mcp.test.ts` |
| 音效编辑器 | 未实现 | 中 | ❌ 缺口 | ❌ 缺口 | 不适用 | ❌ 缺口 | 无。地图编辑器已单独成行。参数化声音生成见「声音生成」 |
| 音效编辑器 | 已有(阶段 D) | 中(覆盖作品、噪音、格式播不出) | ✅(匿名:选预设 → 波形和包络 → 试听的 WAV 与保存的字节相同 → 撤销去掉;官方音效进素材库 → pygame 打出 `SOUND-OK` → 源码包和网页包里都是这份 WAV;收录进本机素材库) | ✅(坏 WAV / MP3 在问题里说明;写入失败时项目不变,再试能保存) | ✅ 匿名可用,不发 `/api/`。生成参数仍要登录。小助手和 WebMCP 不能改 `*.wav` / `*.ogg` / `*.mp3` / `*.aiff` | ✅(保存后可撤销;写入失败不改项目) | `tests/e2e/sounds.spec.ts`;`src/lib/audio/*.test.ts`;`src/lib/assets/validate.test.ts`;`src/lib/diagnostics.test.ts`;`src/lib/ai.test.ts`;`worker/mcp.test.ts` |

缺口的最低期望:

Expand Down
6 changes: 5 additions & 1 deletion docs/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ GameKit 没有自己实现 Pygame。预览和导出使用同一套 [pygbag 0.9.3
| 导出后不依赖 GameKit | 成品必须能单独打开 | ZIP 里只有 `index.html`、`favicon.png` 和 `gamekit.apk` | 运行时脚本仍从 pygame-web CDN 加载。GameKit 下线不影响;pygame-web CDN 下线会影响。把整份 CPython/pygame WASM 打进每个 ZIP 大约几十 MB,而且要自己跟上游版本,所以没有内置 |
| 桌面项目直接拷进来 | pygbag 不能在一个同步死循环里刷新页面 | 模板诊断 `await asyncio.sleep(0)`、`time.sleep` 和 `pygame.time.wait` | 这不是私有 API。同一份 async 循环也能在安装了 pygame 的桌面 Python 上运行。GameKit 不会在构建时偷偷改写用户源码 |
| 音频格式 | 项目里生成的声音是 WAV(浏览器里合成,无需服务端) | 实测 pygame-web 运行时能 `pygame.mixer.Sound("assets/probe.wav")` 并读到正确时长(0.3 秒),所以 WAV 可用;OGG 仍是体积上更好的选择 | MP3/AIFF 仍给警告。声音要等孩子点一下画面才会响 |
| 音频 | 浏览器需要用户手势,且 pygame-web 支持的是 OGG Vorbis | 示例把 `mixer.init()` 放在第一次跳跃;WAV/MP3 只给出警告 | 打开预览后要点一下画面,声音才会响。桌面 WAV 文件不会被自动转码 |
| 音频 | 浏览器需要用户手势。pygame-web 能播 8/16-bit PCM WAV 和 OGG Vorbis | 声音编辑器存 WAV;OGG 要过文件头检查才当能播。MP3、AIFF、坏 WAV、Opus 在问题里说明 | 打开预览后要点一下画面,声音才会响。桌面文件不会被自动转码 |
| 停止游戏 | WASM 进程活在 iframe 里 | Stop 把 iframe 设为 `about:blank`,重新 Run 使用新的会话 | 没有进程级调试器,也不能在任意行暂停 |
| 离开页面 | pygbag 运行时在 `can_close` 为假时会注册 `window.onbeforeunload`(`pythons.js`: `if (!vm.config.can_close)`),浏览器就会弹「Leave site? Changes you made may not be saved.」 | 构建时渲染 `can_close = 1`(`src/lib/build.ts`),预览与导出都不再出现这个弹窗 | 游戏自己不再拦住关闭页面。游戏里没有未保存的数据,工作室的保存由 store 负责,所以这是想要的 |
| AI | 不能让模型自己改项目、构建或发布 | Worker 只提供补全和生图两个接口,只对授权用户开放(见 [ai-rules.md](ai-rules.md))。产品逻辑在编辑器里,生成结果要先看 diff 再接受 | 系统提示词仍在浏览器里拼装,授权用户可以改写它;还没有按人配额 |
Expand All @@ -35,6 +35,10 @@ await asyncio.sleep(0)

预览和导出都打包项目里的这份副本(和别的 `assets/` 文件一样)。成品不请求素材库目录,也不请求 GameKit 站点。素材库以后升级,不会改掉已经复制进项目、或已经导出的那一份。模板本身没有为素材库改过。

## 声音

声音编辑器把参数合成成 16-bit 单声道 22.05 kHz WAV。试听播放的字节和保存进 `assets/` 的字节是同一份,所以导出的游戏不调用生成接口。pygame-web 能播这种 PCM WAV,也能播通过文件头检查的 OGG Vorbis。MP3、AIFF、坏掉的 WAV、以及 Ogg 里的 Opus 会在问题面板里说明,不会假装能播。官方音效在素材库里,复制进项目之后和别的 `assets/` 文件一样打包。播放用普通 `pygame.mixer.Sound`。模板没有为声音改过。声音要等孩子点一下画面才会响。

## 精灵动画

精灵动画是项目里的两份普通文件:一张 PNG 精灵表,和旁边的 `.anim.json`。说明里的 `animation` 字段和素材库清单相同(帧宽高、帧序号、毫秒、动作)。预览和导出把它们和其他 `assets/` 文件一起打包。播放用普通 pygame(`image.load`、`subsurface`、`json`),可以是孩子复制的片段,或项目里可见的 `sprite_anim.py`。模板没有为动画改过。
Expand Down
Loading
Loading