# 《星环弹弓》部署上线与测试验收（QA）文档

> 版本 v1.0 · 适用对象：开发 / 运营 · 配套规格：[02-gamedesign.md](02-gamedesign.md)
> 工程结构：`game/` 微信小游戏 · `web/` 浏览器试玩 · `scripts/` 构建与自测脚本

## 0. 流程总览（照此顺序执行）

```
1 开发前准备 → 2 本地运行 → 3 构建与自测 → 4 上传与发布 → 5 版号与商业化 → 6 QA 验收 → 7 常见坑排查 → 8 上线后观察
```

---

## 1. 开发前准备

1. **注册公众平台账号**：mp.weixin.qq.com → 立即注册 → 账号类型选"小游戏"，按主体类型（个人/企业）提交资料。
2. **获取 AppID**：注册后到"设置 → 基本设置 → 账号信息"查看 AppID（wx 开头），填入 `game/project.config.json` 的 `appid` 字段。
3. **无 AppID 兜底（测试号）**：本仓库默认配置 **`touristappid`** 测试号——无需注册即可在开发者工具本地运行，但**不能上传、不能提审**；正式提审前必须换回真实 AppID。
4. **个人主体 vs 商业主体**（影响能否商业化）：

| 事项 | 个人主体 | 企业/商业主体 |
|---|---|---|
| 注册小游戏 | 可 | 可（可选类目更多） |
| 分享 / 排行榜 / 开放数据域 | 可用 | 可用 |
| 流量主（广告变现） | **不可开通** | 满足平台活跃门槛后可开通 |
| 虚拟支付 / 内购 | 不可 | 需版号后方可 |
| 版号申报 | 基本不可行 | 需企业资质申报 |

> 结论：**个人主体用于开发调试；商业化（广告/内购）必须转企业主体并取得版号。**

---

## 2. 本地运行三步

1. **下载微信开发者工具**：https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html （稳定版 Stable）。
2. **导入工程**：工具 → 项目 → 导入项目 → 目录选 `star-ring-slingshot/game/` → AppID 填 `touristappid`（或正式 AppID）→ 确认 `compileType` 为 "game"、入口为 `game.js`。
3. **运行与真机**：模拟器直接运行 → 点"预览"生成二维码、微信扫码真机打开；或点"真机调试"连真机看 console / 网络 / 性能。

**浏览器试玩版（web/）验证**：
- 直接双击打开 `web/index.html`，或起静态服务（推荐，避免 file:// 下音频受限）：

```bash
cd star-ring-slingshot
python3 -m http.server 8000
# 浏览器打开 http://localhost:8000/web/
```

- `web/adapter.js` 模拟 wx API，用于验证玩法与视觉；**不代表真机行为**（见 §7）。

---

## 3. 构建与自测命令清单

> 均在 `star-ring-slingshot/` 目录执行：`node scripts/xxx.js`

| 命令 | 用途 | 预期输出 |
|---|---|---|
| `node scripts/gen-audio.js` | 程序生成 9 个音效（launch/capture/star/perfect/crash/shield/gameover/milestone/click）到 `game/assets/sfx/` 与 `web/assets/sfx/` | 打印"生成完成…（9 个音效）"；文件均为有效 WAV |
| `node scripts/gen-icons.js` | 生成 `game/assets/icon.png`（144×144）与 `share.png`（500×400） | 打印两个 PNG 路径 |
| `node scripts/build-web.js` | 将 `src/`（CommonJS）打包为 `web/bundle.js` 并同步音效 | 打印"打包完成：N 个模块" |
| `node scripts/test-logic.js` | Node 跑 world 模块确定性单测（入轨数学 / 充电-弹射-逃逸 / 过热坠毁 / 黑洞 / 计分 / Combo / 护盾 / 生成不重叠 / 同种子同结果） | "全部通过 (N/N)"；失败退出码非 0 |
| `node scripts/smoke-test.js` | mock canvas + wx，跑 20 秒完整模拟对局 | "冒烟通过，无异常" |

**提交前例行顺序**：先 `gen-audio` / `gen-icons`（补资产）→ `test-logic` + `smoke-test`（逻辑与冒烟）→ `build-web`（同步 web 版）。任一失败即拦截发布。

---

## 4. 上传与发布流程

1. **代码上传**：开发者工具右上角"上传"→ 填版本号（如 1.0.0）与备注。确认 `project.config.json` 的 appid 已是**正式 AppID**（touristappid 无法上传）。
2. **版本管理**：公众平台 → 管理 → 版本管理 → 开发版本 → "提交审核"。
3. **审核要点**（逐项自查）：
   - [ ] 分享文案 / 分享图无诱导措辞（禁止"分享才能继续/复活"类）；
   - [ ] 无强制分享门槛，分享为可选增值行为；
   - [ ] 隐私接口声明：后台填写《用户隐私保护指引》，覆盖好友排行榜（开放数据域）与广告接口；未调用 `wx.getUserInfo` 等非必要接口；
   - [ ] 广告组件资质：无流量主资质时 `AD_UNIT_ID` 保持为空（广告代码不加载），避免因广告资质被拒；
   - [ ] 类目与内容一致（休闲益智），名称/图标无侵权。
4. **发布**：审核通过（一般 1~7 天）后"发布"。**建议灰度**：选"分阶段发布"（如先 10% 流量）→ 观察 24h 崩溃率与数据正常 → 全量发布；异常时在版本管理回退上一版本。

---

## 5. 版号与商业化

- **现状提醒**：按现行监管，**无版号（ISBN）不能正式商业化发布**——不能接入广告变现与虚拟内购。本项目当前以"测试 / 试运营"形态开发，`AD_UNIT_ID` 默认空 = 广告与复活功能自动关闭，规避合规风险。
- **激励视频广告单元申请（流量主）步骤**（仅资质齐备后参考）：
  1. 确认主体为企业/组织且满足平台流量主门槛（如累计活跃用户达标，以平台当前规则为准）；
  2. 公众平台 → 推广 → 流量主 → 创建"激励视频"广告位；
  3. 将广告位 ID（adUnitId）填入代码 `AD_UNIT_ID`（`src/` 配置处）；
  4. 真机验证"观看完 → 复活（护盾=1、就近入轨）"链路后随包提审；
  5. 无版号 / 无资质期间**保持留空**，绝不预埋广告代码。

---

## 6. QA 验收清单

### 6.1 功能（真机逐项走查；方法列中 test-logic / smoke-test 指 §3 命令）

| 检查项 | 方法 | 通过标准 |
|---|---|---|
| 入轨 | 真机操作 + test-logic | 飞行进入 3.0R 捕获圈自动入轨；轨道稳定不抖动 |
| 充电 | 按住不放 | 轨道收缩速率 = 30 + 充电×70 px/s；HUD 充电条 0→1；过热（<1.5R）变红 |
| 弹射 | 松手 | 沿切线方向，速度 = 线速度×(1.15 + 1.45×充电)，含 0.45 径向分量保证逃逸 |
| 连弹 | 连续弹射 | 每次入轨 +1、上限 20；6 秒未入轨归 1；坠毁归 1 |
| 星星 | 收集 + 完美路径 | 得分 +50×Combo，四芒星爆闪 + 音效；完美弹射沿发射方向补 3 颗星 |
| 护盾 | 故意坠毁 | 初始 3 层；坠毁扣 1 并 1.8s 无敌、从最近安全星球重生；耗尽游戏结束 |
| 黑洞 | 靠近黑洞 | 无捕获圈、引力×3.2；进入 2.3R 事件视界被吸入坠毁 |
| 里程碑 | 飞满 1000m | +150×Combo、彩带 + 钟声音效；教学提示按里程碑浮现 |
| 暂停 | 点暂停 / 切后台 | 自动暂停，遮罩含继续 / 重开 / 回菜单；恢复不卡死 |
| 结算 | 游戏结束 | 得分 / 纪录徽章 / 统计正确；再来一次 / 分享 / 排行榜 / 复活入口齐全 |
| 排行榜 | 提交 + 查看 | setUserCloudStorage 提交 {score, maxcombo, distance}；开放数据域显示好友榜；失败有降级 UI 不崩溃 |
| 分享 | 菜单 + 结算页 | showShareMenu / onShareAppMessage 生效，文案轮换，share.png 正常显示 |
| 音效 | 触发各事件 | 9 个 wav 逐一对应且开关生效；首次播放不卡顿 |
| 震动 | 弹射 / 坠毁 / 完美 | wx.vibrateShort 生效，异常静默不报错 |
| 广告复活 | AD_UNIT_ID 空 / 有值两态 | 空：不显示复活按钮；有：看完视频 → 护盾=1、就近入轨 |

### 6.2 兼容 / 性能 / 网络 / 合规

| 维度 | 检查项 | 方法 | 通过标准 |
|---|---|---|---|
| 兼容 | iOS / Android 双端 | 真机走查 6.1 全项 | 两端行为一致、无崩溃 |
| 兼容 | 刘海 / 灵动岛安全区 | iPhone 真机 | 顶部 HUD / 底部结算按钮不被遮挡（读 safeArea 适配） |
| 兼容 | 屏幕比例 | 多机型 | 750 设计宽自适应，不拉伸变形、触控不错位 |
| 兼容 | 低端机帧率 | 性能面板 + 真机 | 目标 60fps；掉帧 >10% 时自动降粒子 / 关星云光晕 |
| 性能 | 首帧时间 | 工具"启动耗时"面板 | 无远程资源依赖，启动到可交互 <2s |
| 性能 | 包体大小 | 工具详情面板 | 主包 <4MB；本项目纯程序化资源应在几百 KB 内 |
| 性能 | 内存 | 长时间游玩观察 | 无明显泄漏，长时间运行内存平稳 |
| 性能 | 音频预加载 | 首次 touch 后 | touch 回调里预创建 InnerAudioContext，首音无延迟 |
| 网络 | 弱网 / 离线启动 | 飞行模式启动 | 纯本地资源可正常进入主菜单 |
| 网络 | 排行榜降级 | 断网看排行榜 | 显示降级文案，不白屏不崩溃 |
| 合规 | 防沉迷 | 后台确认 | 平台实名 / 未成年人时长限制已生效；不自采身份信息 |
| 合规 | 隐私 | 后台填写 | 《用户隐私保护指引》声明开放数据域与广告接口 |
| 合规 | 分享合规 | 文案走查 | 无诱导、无强制分享门槛（见 §4.3） |

---

## 7. 常见坑与排查

| 现象 | 可能原因 | 排查 / 解法 |
|---|---|---|
| 开放数据域排行榜不显示 | ① `game.json` 未配 `openDataContext: "open-data"`；② 主域未 postMessage 或时序不对；③ 从未 setUserCloudStorage 提交数据；④ 开放域未渲染在 shareCanvas | 逐项核对：配置 → 提交数据成功后再 postMessage 触发刷新 → 真机查看（工具内开放域部分 API 不支持） |
| 音频不播放（真机） | iOS 需用户手势后才可播放；InnerAudioContext 实例过多；src 路径错误 | 首次 touchstart 回调里创建 / 恢复音频；用单例池复用、限制同时实例数；src 用相对路径（`assets/sfx/xxx.wav`）并检查 404 |
| HUD 被刘海 / 灵动岛遮挡 | 未适配安全区 | 用 `wx.getWindowInfo().safeArea` 给顶部 / 底部留 padding |
| iOS 重力 / 方向异常 | 屏幕方向未固定 | `game.json` 已设 `deviceOrientation: "portrait"`，确认未改动；本作无陀螺仪依赖 |
| 工具正常、真机异常 | 工具是 wx API 模拟实现（音频 / 开放域 / 性能差异大） | 一律以真机 + 真机调试为准，工具仅用于快速迭代 |

---

## 8. 上线后观察

**核心指标**：次日留存、人均单局时长、人均启动次数、分享率（分享次数 / 启动）、广告 eCPM 与人均广告次数（开通后）、崩溃率与 90 分位帧率。

**迭代建议**（对应 GDD §8 路线图）：
- 验证核心循环手感：充电速度（0.85/s）与难度曲线（黑洞概率斜率）用 A/B 调参；
- 按路线依次推进 V1.1 皮肤系统 → V1.2 双人同屏 → V1.3 每日挑战（固定种子可复测）→ V1.4 局内道具；
- 每次迭代重复 §3 自测 + §6 验收，再走 §4 灰度发布。

> 发布即起点：用数据反哺调参，小步灰度，快速迭代。
