fix: 修复 Codex review 的 5 项问题 + 3 项 open questions

01-charter: 去金币矛盾、JSON→JS module、更新下一步
02-game-design: 道具状态机重设计(仅操作槽位)、去金币改道具奖励、
  MVP 跳过洲选择页直入城市、物件数统一为3倍数、新增存档模型
03-content-pipeline: 猫猫路径统一 cat_{city_id}.png、
  新增难度生成器系统设计(overlap graph/seed/solver/死局检测)、
  新增元素配比规则(5 类目各 ≥2)、piecesPerElement 约束强化

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
manpengan
2026-03-28 22:48:57 +08:00
parent e74ac1e91e
commit 4d3da38102
3 changed files with 254 additions and 46 deletions

View File

@@ -100,7 +100,7 @@
| 图鉴系统 | 1 套基础城市护照/图鉴系统 | 城市解锁进度、猫猫收集、护照盖章 |
| 每日挑战 | 1 套每日挑战 | 每日 1 关限时挑战,提供日常活跃目标 |
| 分享系统 | 1 套基础分享明信片 | 城市通关/猫猫收集成果分享卡片 |
| 激励视频广告 | 复活、重排、道具、奖励翻倍 | 核心变现手段 |
| 激励视频广告 | 复活、重排、道具、额外道具奖励 | 核心变现手段 |
| 插屏广告 | 局后展示 | 严格控频(每 3 局最多 1 次,单日上限 5 次) |
### 8.2 不进入 MVP
@@ -120,7 +120,7 @@
| 壁垒 | 说明 |
|------|------|
| 城市内容系统 | 城市元素、标签、图鉴、封面、猫猫资产统一配置化JSON 驱动),新增城市只需配置 + 资源,不改代码 |
| 城市内容系统 | 城市元素、标签、图鉴、封面、猫猫资产统一配置化JS module 配置化驱动),新增城市只需配置 + 资源,不改代码 |
| 可控难度生成器 | 控制遮挡层级、解法数、失败率、难度曲线;支持按城市/关卡精调,保证通关率在目标区间 |
| 资源分包策略 | 按洲/城市内容包加载,首包只含核心代码 + 首城资源;后续城市按需下载,保证首包 < 4 MB |
| 纯 Canvas 2D 手写框架 | 不用引擎Cocos/Laya首包预算约 2.5 MB自研轻量渲染/事件/动画/资源管理模块 |
@@ -138,7 +138,7 @@
| 变现方式 | 触发场景 | 预估收入占比 | 阶段 |
|---------|---------|-------------|------|
| 激励视频 | 复活(通关失败时)、重排(牌面无解时)、获取道具、额外奖励翻倍 | 55-65% | MVP 起 |
| 激励视频 | 复活(通关失败时)、重排(牌面无解时)、获取道具、额外道具奖励 | 55-65% | MVP 起 |
| 插屏广告 | 局后结算页、城市解锁节点 | 15-20% | MVP 起 |
| 去广告月卡 | 12-18 元/月,免除所有强制广告 | 10-15% | V1.1+ |
| 猫猫装扮 | 猫猫皮肤/配饰付费解锁 | 3-5% | V1.1+ |
@@ -188,7 +188,7 @@
| 产出物 | 文件 | 内容要点 |
|--------|------|---------|
| 核心玩法与系统设计 | `02-game-design.md` | 堆叠三消规则、槽位机制、道具设计、地图结构、图鉴系统、每日挑战规则 |
| 内容生产流水线 | `03-content-pipeline.md` | 城市数据结构JSON schema、素材规范尺寸/格式/命名、AI 生成 prompt 模板、人工修正 checklist |
| MVP 城市清单 | 同上或独立文档 | 6 城市元素清单(每城 12-15 个)、猫猫设计参数、封面/图鉴配置 |
| 核心玩法原型 | 代码原型 | 关卡生成规则草案、难度参数定义、核心交互 demo |
| 道具状态机设计 | `02-game-design.md` 补充 | 道具类型、触发条件、状态流转、与激励视频的关联 |
| 存档模型 | `02-game-design.md` 补充 | playerState schema城市解锁进度、图鉴收集、道具库存、每日挑战记录 |
| 难度生成器系统设计 | 独立文档或 `02-game-design.md` 补充 | 遮挡层级算法、解法数控制、失败率目标、按城市/关卡精调参数 |
| 锁定 5 项待决策 | `01-project-charter.md` §12 | 消除机制、首页层级、首批城市、美术风格、素材生产方式 |

View File

@@ -22,11 +22,45 @@
### 1.3 道具系统MVP
道具只操作暂存槽,不操作场景物件,避免恢复堆叠遮挡的复杂度。
| 道具 | 效果 | 免费额度 | 额外使用 |
|------|------|---------|---------|
| 重排 Shuffle | 重新打乱场景中剩余物件的堆叠顺序 | 每局 1 次 | 激励视频 |
| 撤回 Undo | 撤回最后拾取的 3 个物件放回场景原位 | 每局 1 次 | 激励视频 |
| 移出 Remove | 从暂存槽移出 3 个不同物件放到临时区(下次消除后回归暂存槽) | 每局 1 次 | 激励视频 |
| 撤回 Undo | 将暂存槽最后放入的 1 个物件移回场景顶层(不恢复原位,放到顶层空闲位置) | 每局 3 次 | 激励视频 |
| 移出 Remove | 将暂存槽中 3 个物件移到旁路寄存区1 格),下次有任意消除发生时自动回归暂存槽 | 每局 1 次 | 激励视频 |
| 洗牌 Shuffle | 重新随机排列场景中所有剩余物件的位置和层级 | 每局 1 次 | 激励视频 |
**道具状态转移表:**
```
暂存槽状态:[A][B][C][D][E][F][G] 7 格)
旁路寄存区:[X][Y][Z] 3 格,仅 Remove 道具使用)
── Undo ──
前置:暂存槽非空
操作:取暂存槽末位 1 个物件 → 放回场景顶层可见位置
后置:暂存槽空出 1 格
特殊:如果暂存槽为空则道具不可用(置灰)
── Remove ──
前置:暂存槽已用 ≥ 3 格 且 旁路寄存区为空
操作:取暂存槽中 3 个不同类型的物件 → 移入旁路寄存区
后置:暂存槽空出 3 格
回归条件:场景中发生任意一次三消 → 旁路寄存区物件自动回归暂存槽
回归溢出:如果回归时暂存槽已满 7 格 → 先回归再判定失败(即回归物件 + 当前槽位 > 7 则游戏失败)
限制:旁路寄存区非空时不可再次使用 Remove
── Shuffle ──
前置:场景中剩余物件 > 0
操作:所有场景物件重新随机分配位置和层级
后置:物件总数和种类不变,堆叠关系重新生成
特殊Shuffle 后需保证至少 30% 物件可点击(同关卡生成约束)
```
**道具交互约束:**
- Undo 和 Remove 互斥使用:旁路寄存区非空时 Undo 不可用(避免回归顺序混乱)
- Shuffle 与其他道具无冲突
- 复活(激励视频):清空暂存槽 + 清空旁路寄存区,场景状态不变,继续游戏
### 1.4 操作交互
@@ -44,16 +78,19 @@
### 2.1 导航层级MVP 简化为二级)
```
主页面洲选择3x3 棋盘
└── 洲内页面:城市选择3x3 棋盘)
└── 城市关卡(单局游戏)
MVP 首版:直接进入亚洲城市页(跳过洲选择页
城市选择3x3 棋盘) → 城市关卡(单局游戏)
V1.1 扩展第二个洲后:
洲选择3x3 棋盘) → 城市选择3x3 棋盘) → 城市关卡
```
预留洲选择页的代码入口但 MVP 不显示。
### 2.2 主页面设计
- 3x3 棋盘布局,每格代表一个洲
- MVP 只开放 1 个洲(亚洲),其余锁定显示"即将开放"
- 洲封面:标志性元素 + 解锁进度(如"3/9 城市已解锁"
- MVP 直接显示亚洲城市 3x3 棋盘,顶部标题"亚洲"
- V1.1 扩展第二个洲后再开启洲选择页
### 2.3 洲内页面设计
@@ -68,7 +105,7 @@
每个城市包含 **6 个基础关卡**,全部通关才算"通关该城市"。
- 每关使用该城市 12-15 个特色元素中的 8-12 个
- 难度递增:关 1 用 6 种元素各 3 个,关 6 用 10 种元素各 3-4 个 + 更深堆叠
- 难度递增:关 1 用 6 种元素各 3 个,关 6 用 10 种元素各 3 或 6 个(混合配置) + 更深堆叠
- 通关后获得该城市的猫猫收集卡
---
@@ -128,7 +165,7 @@
|------|------|
| 内容 | 每天 1 个随机城市的特殊关卡 |
| 难度 | 比普通关卡略高(更多物件种类、更深堆叠) |
| 奖励 | 金币(用于未来 IAP 系统+ 特殊明信片 |
| 奖励 | 道具补给Undo ×2+ 特殊明信片 |
| 生成方式 | 程序化生成(基于难度参数随机组合) |
---
@@ -151,9 +188,9 @@
| 触发点 | 分享内容 | 奖励 |
|--------|---------|------|
| 通关城市 | 城市猫猫卡片 + "我解锁了XX猫" | 金币 |
| 护照盖章 | 护照截图 + 收集进度 | 道具 |
| 每日挑战通关 | 挑战结果卡片 | 金币 |
| 通关城市 | 城市猫猫卡片 + "我解锁了XX猫" | 道具补给Undo ×1 |
| 护照盖章 | 护照截图 + 收集进度 | 道具补给Shuffle ×1 |
| 每日挑战通关 | 挑战结果卡片 | 道具补给Undo ×2 |
| 图鉴里程碑 | "已收集X只猫猫" | 特殊明信片 |
分享卡片设计:猫猫形象 + 城市背景色 + 进度信息 + 小程序入口按钮。
@@ -191,3 +228,80 @@
| 城市 2-3 | 平滑上升 | 物件种类增加,层级不变 |
| 城市 4-5 | 进阶挑战 | 引入更高层级 + 更多种类 |
| 城市 6 | 挑战级 | 最大物件数 + 最深堆叠 |
---
## 9. 玩家存档模型
所有玩家数据持久化到 `wx.Storage`key 为 `player_state`
### 9.1 存档 Schema
```javascript
const playerState = {
// 版本控制(存档迁移用)
saveVersion: 1,
// 城市进度
unlockedCities: ['beijing'], // 已解锁城市 id 列表
levelProgress: {
// city_id: { levelId: { stars: 0-3, completed: bool, bestMoves: number } }
beijing: {
1: { stars: 3, completed: true },
2: { stars: 2, completed: true },
// ...
},
},
// 收集
collectedCats: ['beijing'], // 已收集猫猫的城市 id
passportStamps: ['beijing'], // 已盖章城市 id
// 道具库存
inventory: {
undo: 3, // 撤回次数
remove: 1, // 移出次数
shuffle: 1, // 洗牌次数
},
// 每日挑战
dailyChallenge: {
date: '2026-03-28', // 上次挑战日期YYYY-MM-DD
completed: false,
cityId: 'tokyo', // 今日挑战城市
seed: 12345, // 关卡生成种子
},
// 广告冷却
adCooldowns: {
interstitialCount: 0, // 今日插屏已展示次数
lastInterstitialTime: 0, // 上次插屏时间戳
lastRewardDate: '2026-03-28', // 上次激励视频日期
},
// 设置
settings: {
soundEnabled: true,
musicEnabled: true,
vibrationEnabled: true,
},
// 统计
stats: {
totalGamesPlayed: 0,
totalGamesWon: 0,
totalShareCount: 0,
firstPlayDate: '2026-03-28',
},
}
```
### 9.2 存档读写规则
| 规则 | 说明 |
|------|------|
| 读取时机 | 游戏启动时读取一次,缓存在内存 |
| 写入时机 | 关卡通关/失败、道具使用、城市解锁、设置变更时写入 |
| 写入方式 | `wx.setStorageSync('player_state', state)` 同步写入 |
| 版本迁移 | 读取时检查 `saveVersion`,低于当前版本则执行迁移函数 |
| 数据上限 | wx.Storage 上限 10MB预估存档 < 50KB |

View File

@@ -15,7 +15,7 @@ module.exports = {
continent: 'asia',
// 地图封面
cover: {
catImage: 'images/cats/beijing.png', // 猫猫头像
catImage: 'images/cats/cat_beijing.png', // 猫猫头像
bgColor: '#CC2936', // 城市主色调
},
// 城市文化信息
@@ -57,7 +57,7 @@ module.exports = {
| `elements[].name` | string | Y | 元素中文名 |
| `elements[].category` | string | Y | 分类:`food` / `culture` / `landmark` / `nature` / `item` |
| `elements[].image` | string | Y | 图片路径(相对于资源根目录) |
| `levels[].piecesPerElement` | number\|number[] | Y | 3 的倍数;数组时长度须等于 `elementCount` |
| `levels[].piecesPerElement` | number\|number[] | Y | 3 的倍数;数组时长度须等于 `elementCount`,每个值必须是 3 的倍数 |
### 1.2 洲索引配置
@@ -167,31 +167,111 @@ module.exports = {
**难度递进规律:** 关 1-2 低密度入门 -> 关 3-4 中密度爬坡 -> 关 5-6 高密度挑战。关 6 允许 `piecesPerElement` 为数组,制造不均匀分布的额外难度。
### 4.2 关卡布局生成
### 4.2 布局生成器系统设计
不手工摆放物件位置,用程序化生成器:
#### 核心概念
- **输入:** 难度参数 + 城市元素列表
- **输出:** 每个物件的 `{ elementId, x, y, layer, rotation }`
- **生成约束:**
- 每层物件不超过总数40%
- 最底层至少有 30% 的物件初始可点击
- 保证可解性(从目标状态反向推导)
**Overlap Graph遮挡图**
- 每个物件是一个节点,如果物件 A 遮挡物件 B则有一条有向边 A → B
- 物件可点击的条件:入度为 0没有任何物件遮挡它
- 遮挡判定:两个物件的包围盒重叠面积 > 阈值(物件面积20%)且 A.layer > B.layer
### 4.3 难度验证
**Dead-end死局定义**
- 所有可点击物件放入暂存槽后无法触发任何三消,且暂存槽已满 7 格
- 等价条件:当前可点击物件集合中,没有任何 3 个同类物件
用脚本模拟 1000 次随机合理操作,验证通关率:
#### 生成算法(反向法)
| 关卡 | 目标通关率 |
|------|-----------|
| 关 1 | > 95% |
| 关 2 | > 90% |
| 关 3 | > 80% |
| 关 4 | > 70% |
| 关 5 | > 60% |
| 关 6 | 50-60% |
```
输入difficulty = { elementCount, piecesPerElement, layers, density }
输出pieces[] = [{ elementId, x, y, layer }]
失败率过高时降低 `density` 或 `layers`。
步骤:
1. 创建物件池:按 elementCount × piecesPerElement 生成全部物件
2. 定义画布区域:根据 layers 划分 Z 层,每层定义可用网格
3. 正向放置:
a. 从最底层layer=0开始逐层往上放置物件
b. 每层放置数量 = 总数 × layerDistribution[layer]
- density='low': [0.4, 0.3, 0.2, 0.1]
- density='medium': [0.3, 0.3, 0.25, 0.15]
- density='high': [0.25, 0.25, 0.25, 0.25]
c. 在层内随机放置,确保相邻物件有 overlap按 density 控制重叠率)
4. 构建 overlap graph
5. 可点击性校验:初始可点击物件占比 ≥ 30%(入度为 0 的节点数 / 总节点数)
6. 可解性验证(见 4.3
7. 不通过则重新生成(最多重试 50 次)
```
#### Seed种子
- 每个关卡实例由 `seed: number` 唯一确定
- 正式关卡:`seed = hash(cityId + levelId + attemptCount)`
- 每日挑战:`seed = hash(dateString + 'daily')`
- 同一 seed 保证生成完全相同的布局(可复现、可分享、可验证)
#### 可点击判定规则
```javascript
function isClickable(piece, allPieces) {
// 同层或更高层的物件中,是否有包围盒重叠超过阈值的
for (const other of allPieces) {
if (other === piece || other.removed) continue
if (other.layer <= piece.layer) continue
if (overlapArea(piece, other) > piece.area * 0.2) {
return false // 被遮挡
}
}
return true
}
```
### 4.3 Solver / 可解性验证
#### Solver 算法(贪心模拟)
```
输入pieces[](布局), maxSimulations=1000
输出:{ solvable: bool, avgMoves: number, passRate: number }
每次模拟:
1. 初始化暂存槽 slot[7],旁路寄存区 bypass[3]
2. 循环直到场景清空或暂存槽满:
a. 获取所有可点击物件overlap graph 入度=0
b. 优先策略:
- 优先选择能在暂存槽中凑成三消的物件
- 其次选择暂存槽中已有 1-2 个同类的物件
- 最后随机选择
c. 放入暂存槽,触发三消判定
d. 如果暂存槽满且无法消除 → 失败
3. 场景清空 → 成功
```
#### 验证标准
| 关卡 | 目标通关率 | 不达标处理 |
|------|-----------|-----------|
| 关 1 | > 95% | 降低 layers 或 density |
| 关 2 | > 90% | 同上 |
| 关 3 | > 80% | 减少 elementCount |
| 关 4 | > 70% | 调整 layerDistribution |
| 关 5 | > 60% | 同上 |
| 关 6 | 50-60% | 在此区间即合格 |
#### 死局检测
```javascript
function isDeadEnd(slot, clickablePieces) {
if (slot.length < 7) return false // 槽未满,还能操作
// 检查可点击物件中是否有能和槽中物件凑成三消的
for (const piece of clickablePieces) {
const sameInSlot = slot.filter(s => s.elementId === piece.elementId).length
if (sameInSlot >= 2) return false // 放入后可凑三消
}
return true // 真死局
}
```
注意:死局 ≠ 游戏失败。玩家可以使用 Undo/Remove/Shuffle 道具脱困。只有道具用尽且死局时才判定失败。
---
@@ -203,7 +283,7 @@ module.exports = {
主包(<= 4MB
├── game.js + js/(核心框架、渲染引擎、游戏逻辑) ~150KB
├── images/ui/(通用 UI 元素、按钮、图标) ~300KB
├── images/cats/beijing.png首个城市猫猫头像 ~50KB
├── images/cats/cat_beijing.png首个城市猫猫头像 ~50KB
├── audio/通用音效6-8 个) ~300KB
├── cities/beijing.js首个城市数据配置 ~5KB
└── images/elements/beijing/(首个城市元素图标) ~200KB
@@ -214,7 +294,7 @@ module.exports = {
└── sub-asia/
├── cities/tokyo.js + bangkok.js + ...
├── images/elements/tokyo/ + bangkok/ + ...
└── images/cats/tokyo.png + bangkok.png + ...
└── images/cats/cat_tokyo.png + cat_bangkok.png + ...
```
### 5.2 首包预算分配
@@ -237,7 +317,21 @@ module.exports = {
## 6. MVP 城市清单与素材计划
### 6.1 首批 6 城市
### 6.1 元素选取配比规则
每个城市的 12-15 个特色元素必须覆盖以下 5 个类目,每类至少 2 个:
| 类目 | category 值 | 最少数量 | 示例 |
|------|------------|---------|------|
| 地标建筑 | landmark | 2 | 天安门、鸟巢 |
| 美食饮品 | food | 3 | 糖葫芦、烤鸭、豆汁 |
| 文化符号 | culture | 2 | 京剧脸谱、兔儿爷 |
| 交通器物 | item | 2 | 铜锣、毛笔 |
| 动植物/自然 | nature | 2 | — |
剩余 1-4 个名额自由分配。此规则确保每个城市的元素视觉多样性和辨识度。
### 6.2 首批 6 城市
| 城市 | 元素主题方向 | 猫猫特色 |
|------|------------|---------|
@@ -248,7 +342,7 @@ module.exports = {
| 新加坡 | 鱼尾狮、辣椒螃蟹、榴莲建筑、叻沙、金沙酒店、兰花、肉骨茶、冰激凌三明治、组屋、咖椰吐司 | 花猫 + 小狮子鬃毛 |
| 伊斯坦布尔 | 土耳其红茶、蓝色清真寺、热气球、烤肉串、郁金香、恶魔之眼、土耳其冰淇淋、地毯、石榴、旋转舞裙 | 安哥拉猫 + 恶魔之眼项圈 |
### 6.2 生产时间估算(单城市)
### 6.3 生产时间估算(单城市)
| 步骤 | 耗时 | 说明 |
|------|------|------|