# Design · Slope Rig 斜板引球

## 为什么选「两阶段建造 + 模拟」loop

- 513 系列前 60 个原型全部为「单阶段实时」（操作即反馈）
- **两阶段 loop** 完全空白：编辑 → 模拟 → 看结果 → 重排，这是 Incredible Machine / Crayon Physics / Brain It On! 品类的核心循环
- 该 loop 的爽点路径与其他原型完全不同：
  - 其他原型：操作 → 即时反馈 → 复购
  - 本作：思考 → 编辑 → **等待模拟** → 进/不进 → 想出更优解 → 重排
- 这种「思考密集 + 模拟揭晓」节奏适合 3-5 分钟一关、关后即满足，与 hyper-casual 的快爽形成代际差异

## 为什么 input 选「拖拽两点定线段」

- 与 03-color-trace（按住拖染路径）大类同（按住拖拽）但**实质完全不同**：
  - 03：连续轨迹采样（每帧位置都记录，得到曲线）
  - 本作：只记起点 + 终点（得到直线段）
- 与 27-hex-match（拖块到网格）大类同但**实质完全不同**：
  - 27：拖一个离散块放到格子（位置量化）
  - 本作：拖出一条线段（任意角度任意长度连续）
- 拖拽两点定线段是 Crayon Physics 直系，但本作避开「曲线画线」（更简单的反射几何 = 玩家心智模型更清晰）

## 为什么 action 选「2D 刚体物理（重力 + 反射）」

- 513 系列已有反弹相关原型：
  - 10-brick-paddle：球-砖块矩形碰撞 + 简单上下反弹
  - 05-hex-bounce：六向反弹（射击方向）
  - 20-flip-ball：双弹板上抛
- 但**自由几何反射 + 持续重力 + 摩擦衰减**（即真正的「2D rigid body」感）**513 首次**
- 这套物理与玩家画的板**完全耦合**：玩家可以画任意角度的板，看到任意角度的反射 = 物理是玩家创造的而非内置关卡
- 该 action 的可玩性是「物理一致性」给的：因为重力 + 反射规则简单可预测，所以玩家能事前推理「我画这条板球会被弹到哪」

## 数值设计

### 物理常数

| 参数 | 值 | 理由 |
|---|---:|---|
| `g` | 0.30 | 不太轻飘，也不太砸（玩家有反应时间） |
| `restitution` | 0.62 | 弹但不弹太疯（连续 3 板后球还能往下走） |
| `friction` | 0.86 | 反弹有衰减，避免永动机 |
| `wallRest` | 0.5 | 固定墙比玩家板更"硬"，避免在两面墙间无限弹 |
| `minSpeed` | 0.35 | 阈值低于此判为静止 |
| `stuckFrames` | 240 | 4 秒（60fps），给玩家时间看清「球停了 = 我设计的板不对」 |

### 关卡难度曲线

| 关 | 板数上限 | 障碍 | 必须用到的"招" |
|---|---:|---|---|
| 1 | 1 | 0 | 教学：一条板就能解 |
| 2 | 2 | 横挡板 | 学拐弯（一板把球弹向左，另一板把球弹向下） |
| 3 | 2 | 竖墙 | 学绕过竖墙（高板把球弹向竖墙之外） |
| 4 | 3 | 双横挡板 + 中间窗 | 学穿过窄窗（球必须垂直对准窗中心） |
| 5 | 3 | 双重折弯 | 学多板组合（每板修正一次方向） |
| 6 | 3 | 双层窗（4 横挡板 + 2 窗） | 综合：球要穿过 2 个窗才能到底 |

### 板数限制 = 解谜深度

不限板数 = 玩家可堆 100 块板暴力解 = 失去解谜趣味。  
强制上限 = 玩家必须思考最优配置。

## 路径推演（典型解法）

- **关 1**：球从 (120, 60) 落，画一条 (180, 300)→(380, 460) 的右下斜板，球弹到右下落入篮筐
- **关 3**：球从 (80, 60) 落，竖墙挡在 (220, 200-420)，玩家需要：
  - 板 A 在 (60, 200)→(200, 300)：把球弹到右上方（绕过竖墙顶）
  - 板 B 在 (340, 350)→(440, 450)：把球弹到底部右下篮筐
- **关 6**：双层窗最骚解法是「球-板-窗-板-窗」垂直 3 段，每段都对准窗中心

## 视觉哲学

- **背景渐变深蓝**：让金色篮筐与橙色板视觉跳出
- **球留拖尾**：14 帧 alpha 衰减位置队列 → 玩家看清反弹路径
- **板用粗橙线 + 圆头**：圆头提示「可点击删除」+ 圆润感
- **编辑预览板用虚线**：与已落地的实线区分
- **篮筐呼吸光晕**：sin 波 alpha 0.65-1.0 → 永远是视觉中心
- **碰撞粒子**：6 板 / 4 墙 个 → 反馈到位但不过载
- **PERFECT 通关爆 32 粒**：金 / 橙 / 绿 三色随机 → 庆祝感强烈

## 模式切换的 UI 提示

- HUD 右侧的 `EDIT / SIM` pill 颜色不同：
  - EDIT：橙色背景（与板同色 = 编辑期）
  - SIM：绿色背景（与球同色 = 模拟期）
- 模式切换瞬间整个画面"情绪"变化，玩家明显感到「我现在不能再画了」

## 边界情况处理

- **球出屏 4 边**：判 LOSE（包括上边，避免反弹太疯飞出顶部后挂起）
- **球卡住**（速度持续 < 0.35）：240 帧后判 LOSE（避免无限等待）
- **画太短的板**（拖动距离 < 30 px）：判定为"点击删除"，不当板放置
- **板数达上限再画**：忽略 + flashTip 提示
- **WIN 后再 Space**：被 disabled 拦截

## 数据结构

```js
LEVELS[6] = [{ spawn:{x,y}, basket:{x,y,w,h}, walls:[{x,y,w,h}...], maxBoards, tip }]
ball = { x, y, vx, vy, r=9, trail:[{x,y}...] }   // SIM 期才存在
boards = [{ x1, y1, x2, y2 }]                     // 玩家画的板（按关重置）
particles = [{ x, y, vx, vy, life, color }]      // 碰撞粒子
PHYS = { g, restitution, friction, wallRest, minSpeed, stuckFrames }
```

## 已知边界 / 没做

- 未做：声音（acceleration_rules 不强求；后续可加 WebAudio 反弹音）
- 未做：关卡完成历史 / 最少板数记录（首版不引入存储复杂度）
- 未做：撤销（删除已经够用）
- 未做：球之外的物体（铁球、轻球、磁球，留作变体）
- 未做：可动障碍 / 弹簧板 / 传送门（留作扩展）
- 未做：关卡选择菜单（首版严格线性，避免菜单复杂度）

## 性能预算

- 6 关最多：4 玩家板 + 4 固定墙 + 1 球 + ≤ 32 粒子 = 每帧 ≤ 9 碰撞检测 + ≤ 32 粒子更新 << 1 ms
- 60 fps 稳定在桌面 / 移动 / 2× 加速
- 单 canvas 单 layer 渲染
