# 《星穹对撞》部署上线与测试验收（QA）文档

> 版本 v1.0 · 配套文档：开发权威规格见 [02-gamedesign.md](./02-gamedesign.md)（GDD）
> 适用对象：开发 / 运营。所有数值、规则与 GDD 冲突时以 GDD 为准。

---

## 1. 开发前准备

1. **注册**：微信公众平台（mp.weixin.qq.com）注册账号，服务类目选 **小游戏**（休闲类）。
2. **AppID**：公众平台 →「开发管理 → 开发设置」获取小游戏 AppID；开发者工具建项目时选择「小游戏」并填入。
3. **touristappid 兜底**：账号审核通过前，把 `game/project.config.json` 的 `appid` 改成 `"touristappid"`（或工具内选游客模式），可本地运行 / 预览 / 真机调试，但**不能上传发布**，且开放数据域排行榜取不到好友关系链。
   - ⚠️ 当前仓库 `project.config.json` 填的是 `wxce7c6c389b10adc7`：确认它属于你的主体并完成小游戏类目认证后再发布；临时调试可改回 `touristappid`。
4. **主体选择**（决定能否变现）：

| 能力 | 个人主体 | 企业主体 |
|---|---|---|
| 注册门槛 | 身份证 | 营业执照 + 认证 |
| 流量主（广告） | ❌ 不可开通 | ✅ 非个人 + 累计独立访客达标后可开通 |
| 虚拟支付（内购） | ❌ | ✅（需符合平台规范） |
| 版号申请 | ❌ 基本不可行 | ✅ 可申请（休闲类免版号备案政策以平台最新规则为准） |
| 建议 | 纯免费传播验证玩法 | 计划变现/长期运营时选用 |

---

## 2. 本地运行三步（微信端 + Web 端）

**微信端（3 步）**
1. 下载安装**微信开发者工具**（稳定版，项目类型选「小游戏」）。
2. 「导入项目」→ 目录选 `star-collide/game/` → AppID 填实际号或 `touristappid` → 自动识别 `compileType: game`。
3. 工具栏「预览」生成二维码手机扫码试玩；「真机调试」连真机看日志；模拟器按住 Alt/Option 可模拟多指触控。

**Web 端验证**（无微信环境快速冒烟）
1. `node scripts/build-web.js`（生成 `web/bundle.js` 并同步音效）。
2. 直接双击打开 `web/index.html`，或推荐：`cd star-collide && python3 -m http.server 8080` → 访问 `http://localhost:8080/web/`（避免 file:// 限制）。
3. 浏览器控制台无报错、能完成一局（web 版走 adapter.js 模拟 wx）。

---

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

| 脚本 | 命令（在 star-collide/ 下） | 用途 | 预期输出 |
|---|---|---|---|
| gen-audio.js | `node scripts/gen-audio.js` | 程序化生成音效到 `game/assets/sfx/`（hit_lo/hit_mid/hit_hi、wall、score、powerup、well、serve、win、lose、click 等，以实际为准） | 逐个 wav 生成提示，文件存在 |
| gen-icons.js | `node scripts/gen-icons.js` | 生成 `assets/icon.png`(144)、`share.png`(500×400) | 图片生成提示，尺寸正确 |
| build-web.js | `node scripts/build-web.js` | 把 game/ 的 CommonJS 打包为 `web/bundle.js` 并复制音效 | `web/bundle.js 打包完成：N 个模块` |
| test-logic.js | `node scripts/test-logic.js` | world 逻辑单测：反弹角度、回合加速与封顶、得分发球、双球分裂、引力井弹道、AI 预测与限速、五局终局、同种子一致性 | 全部 PASS，0 failed |
| smoke-test.js | `node scripts/smoke-test.js` | mock canvas + wx 冒烟：双人/AI 完整一局到终局，菜单/对局/结算三态绘制与像素铺满断言 | `冒烟测试通过：菜单/对局/结算/重开/暂停/单人AI/帮助/音效 全链路 OK · …` |

**提交前例行顺序**（任一失败即停，不带红提交）：
`gen-audio → gen-icons → build-web → test-logic → smoke-test → 工具预览 → 真机调试`

---

## 4. 上传与发布流程

1. **上传**：开发者工具右上「上传」→ 填版本号（如 1.0.0）与变更备注。
2. **体验版**：公众平台 →「管理 → 版本管理」→ 将上传版本设为体验版，邀请成员扫码回归 QA 清单。
3. **提交审核**：填类目（休闲）、版本描述；重点核对：
   - **分享合规**：文案/图不涉诱导（不写"转发解锁""分享得道具"等），分享图用 `share.png`。
   - **隐私指引**：「设置 → 用户隐私保护指引」如实声明（本游戏不收集个人信息，也要声明"不收集"，否则拒审）。
   - **广告资质**：V1 未接广告 → 广告相关资质项**留空**，不勾选"含广告"。
4. **灰度 → 全量**：审核通过后先「灰度发布」按比例放量（5% → 10%），观察崩溃率/评分/投诉，再「全量发布」。
5. 后续每次改版重复：自测（§3）→ 上传 → 提审 → 灰度 → 全量。

---

## 5. 版号与商业化

- **硬约束**：无版号游戏**不得商业化发布**——不能开通虚拟支付（内购），广告变现（流量主）也受限制。V1 纯免费、无内购无广告，靠分享传播冷启动。
- **提醒**：计划接广告/内购前，先确认主体为企业 + 完成版号/备案合规，否则审核与结算环节会被拒。
- **流量主激励视频（规划，V1 不接）**：败方结算页看广告追平 1 分。申请步骤简述：
  1. 主体为非个人且累计独立访客达标 → 公众平台开通**流量主**；
  2. 创建**激励视频**广告位，拿到广告位 ID；
  3. 代码 `wx.createRewardedVideoAd({adUnitId})` 加载 → 展示 → `onClose` 里校验 `res.isEnded` 为 true 才发分（防中途关闭薅羊毛）；
  4. 未开通时该按钮隐藏或提示"暂未开放"。

---

## 6. QA 验收清单

### 6.1 功能

| 检查项 | 方法 | 通过标准 |
|---|---|---|
| 分屏归属 | 双人各按自己半场拖动/长按 | 上下半场互不干扰，按**按下时位置**归属 |
| 拖动 | 半场内拖动星舰 | 水平跟随、限速 1000 px/s、不穿墙出界 |
| 反弹角度 | 边缘/中央分别命中 | 边缘大斜角、中央直弹（偏移/半舰宽 ×70°） |
| 回合加速 | 连续对打 | 每次命中 +6%，上限 1400 px/s，回合数显示正确 |
| 得分发球 | 球越过底线 | 对方 +1；得分方发球，1s 倒计时后随机 ±25° 发射 |
| 双球道具 | 星舰接金色菱形 | 球数<2 分裂第二颗球（速度镜像）；已有 2 球 → 直接 +1 分 |
| 引力井 | 满能量长按 0.3s | 半径 95px 漩涡持续 2s，球受 1500 px/s² 向心加速度；冷却 3s；能量条满才可放 |
| AI 三档 | 选简单/普通/困难各打一局 | 追踪速度 0.55/0.9/1.25 呈明显梯度 |
| 五局终局 | 打到 5 分 | 触发终局 → 结算页（胜者/统计/再来一局/分享/排行榜） |
| 排行榜 | 双人/单人各打几局 | 开放数据域按 maxrallies 排序正确，结算页可进 |
| 分享 | 结算页点分享 | 转发卡片出 share.png，文案轮换、无诱导 |
| 音效 | 触发各事件 | 10 个左右音效在对应事件播放（hit 随回合切 3 档） |
| 震动 | 命中星舰 | `wx.vibrateShort` 触发，异常静默不崩 |
| 广告追平 | 结算页 | V1 无广告：按钮隐藏或提示未开放，不留死链 |

### 6.2 兼容

| 检查项 | 方法 | 通过标准 |
|---|---|---|
| iOS / Android | 各 1~2 台真机跑全流程 | 无崩溃、操作手感一致 |
| 刘海安全区 | 刘海/挖孔机真机 | 比分、按钮不落入安全区（读 `getSystemInfoSync().safeArea`） |
| 多分辨率 | 320~430 逻辑宽真机/模拟器 | 设计宽 750 等比缩放，高 1334~1624 自适应，无黑边/裁切 |
| 低端机帧率 | iPhone 6 / 千元安卓 | 双球 + 引力井同屏帧率 ≥ 30fps |

### 6.3 性能

| 检查项 | 方法 | 通过标准 |
|---|---|---|
| 首帧 | 冷启动计时 | 启动到可交互 < 3s，无白屏 |
| 主包体积 | 上传后看包体报告 | < 4MB（超了压缩图片/精简代码） |
| 内存 | 反复开合结算 20 局 | 内存稳定不持续增长 |
| 音频预加载 | 开局前就绪 | 比赛中断音不卡顿、首响无延迟 |

### 6.4 网络

| 检查项 | 方法 | 通过标准 |
|---|---|---|
| 离线启动 | 飞行模式冷启动 | 可进菜单并本地对局，不发请求阻塞 |
| 排行榜降级 | mock 关系链 API 失败 | getFriendRank → getFriendCloudStorage 兜底 → 提示"暂无法获取"，不白屏 |

### 6.5 合规

| 检查项 | 方法 | 通过标准 |
|---|---|---|
| 防沉迷 | 未成年人登录 | 按平台要求接入实名/时长限制 |
| 隐私 | 提审材料核对 | 无未声明的数据收集、无敏感权限申请 |
| 分享 | 文案/图抽查 | 无诱导、无违规内容 |
| 商业合规 | 全局搜索 | 无版号阶段无任何支付/广告入口 |

---

## 7. 常见坑与排查

- **多指触控干扰**（最高频坑）：同屏双人两只手指同时操作，必须用 `touch.identifier` 区分每个触点并**独立追踪** move/end；半场归属在 `touchstart` 按触点初始位置判定并**锁定该触点整局**，中途不得改判；`touchend` 和 `touchcancel` 都要清理触点，否则幽灵触点导致星舰乱跑。
- **半场边界**：按下点恰在中线 → 约定归属下方半场（统一规则），双人用例必须覆盖。
- **音频**：未预加载 → 首响延迟；iOS 需在手势后 `play()` 一次解锁；`obeyMuteSwitch: false` 保证有声音；页面切换/对局结束要销毁 InnerAudioContext 防泄漏。
- **安全区**：菜单/比分按钮避开刘海与底部手势条；web 版无 safeArea 走默认值（platform.js 已兜底 375×667）。
- **方向锁定**：`game.json` 已设 `deviceOrientation: "portrait"`；个别机型仍横屏时查工具"不推荐横屏"设置与 game.json 是否被改。
- **工具与真机差异**：开发者工具帧率/字体/音频与真机不同，`getFriendRank` 等真实 API 在模拟器返回空——**以真机调试为准**；web 版 adapter 未覆盖全部 wx API，排行榜/震动在 web 只验证降级路径。

---

## 8. 上线后观察

**核心指标**（埋点用 `wx.reportAnalytics` 或自建埋点上报局数/模式/时长/分享点击）：

| 指标 | 定义 | 参考目标 |
|---|---|---|
| 双人局占比 | 双人局数 / 总局数 | > 50%（验证"一机双人"成立） |
| 人均局数 | 日活人均对局数 | ≥ 3 |
| 回访 | 次留 / 7 留 | 次留 ≥ 15% |
| 分享率 | 分享人数 / 活跃 | ≥ 10%（结算页是主分享入口） |

**迭代建议**（按 GDD §9 路线推进）：V1.1 **皮肤**（星舰涂装/球颜色，胜场解锁 → 拉回访）→ V1.2 **每日挑战**（固定种子目标回合数 → 拉日活）→ V1.3 **特殊球**（分裂/追踪/慢速球道具池扩展）→ V1.4 **异地联机**（房间码 + 微信云开发）。每次迭代先跑 §3 自测 + §6 清单再走 §4 发版流程。
