diff --git a/docs/01-project-charter.md b/docs/01-project-charter.md index 0166817..d122940 100644 --- a/docs/01-project-charter.md +++ b/docs/01-project-charter.md @@ -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 | 消除机制、首页层级、首批城市、美术风格、素材生产方式 | diff --git a/docs/02-game-design.md b/docs/02-game-design.md index 09b2c36..46601d3 100644 --- a/docs/02-game-design.md +++ b/docs/02-game-design.md @@ -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 | diff --git a/docs/03-content-pipeline.md b/docs/03-content-pipeline.md index 1506490..7a9d835 100644 --- a/docs/03-content-pipeline.md +++ b/docs/03-content-pipeline.md @@ -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 生产时间估算(单城市) | 步骤 | 耗时 | 说明 | |------|------|------|