fix: 修复迁移后的基础UI显示问题

This commit is contained in:
cancerai committed 2026-10-25 16:03:05 +08:00
1 parent 18017f819a
commit a93e06f60c
68 files changed
+2293 -8404

No files matched your search

+5 -1
View File
@@ -62,11 +62,15 @@
## Flutter 移植版(`app/`,脱胎于本 web 源码)
目标是 android+macos+linux+windows 四平台 100% 复刻 UI/功能/手感。核心约定:
- **渲染**:自定义 `Ticker` + `CustomPaint`,信箱缩放 800×608 → 适配屏幕;`GameCanvas` 用 `SizedBox.expand`(不要用 `Size.infinite`,零尺寸会让 scale=0 整屏消失)。
- **渲染**:自定义 `Ticker` + `CustomPaint`;**全屏铺满视口**(用户明确不要信箱黑边):`stageMetrics(Size)` 保证 800×608 完整可见、另一维随屏幕比例加宽(横屏手机=高度定标 608、vw≈1318),paint 前 `world.setViewport(vw,vh)` 同步给 Camera;render/drawTitleBg 一律用 `viewW/viewH`,别写回常量 W/H。`GameCanvas` 用 `SizedBox.expand`(不要用 `Size.infinite`,零尺寸会让 scale=0 整屏消失)。
- **系统返回手势 = 游戏内上一页**(用户明确不要退出 App):`PopScope(canPop:false)` → `_handleSystemBack()`(playing→暂停、paused→继续、result→关卡选择、settings/levels/editor→标题、标题页忽略)。
- **循环**:`World.tick(dt)` 固定 60Hz 累加器(`STEP=1000/60`),`Player.update(1)` 定步长 —— 手感 1:1 的关键,别改成 dt 缩放。
- **输入**:Flutter 按键经 `LogicalKeyboardKey.debugName` 去空格 → 喂给 `Input.onKey()`,得到与 web `KeyboardEvent.code` 完全相同的字符串(`ShiftLeft/KeyZ/KeyR/Backspace/Escape`)。
- **`Positioned` 必须是 `Stack` 的直接子级**(血泪):把它写在 `ValueListenableBuilder.builder` 的返回值里会丢弃定位数据 → 子树横向塌缩为 0 宽 → 表现为"黑屏"。一律写成 `Positioned(child: ValueListenableBuilder(...))`。
- **凡是"某状态决定某子树显隐",该状态必须可监听**:`World.state` 已改由 `ValueNotifier<String> stateN` 承载(曾在 `build()` 里读普通字段导致 HUD 永不出现)。
- **paint/build 阶段绝不能写被监听的 notifier**(血泪):曾在 `world.render()`(paint 阶段)里推 toast → 控制台刷 "Build scheduled during frame"。通则:一切 host 回调(onToast/onHud/onShowScreen…)只允许在 `World.tick()`(Ticker 回调 = transientCallbacks 阶段)里发,render() 只画不通知。
- **菜单屏用 `_FitScroll` 防小逻辑高度溢出**(screens.dart):横屏真机逻辑高度仅 ~360-400px,静态 Column 必溢出;`_FitScroll` 内部**禁止 Spacer/Expanded/Flexible**(无界高度会抛异常),列表屏用 ListView 自滚。测试注意:`setSurfaceSize` 设的是**逻辑**像素,模拟真机要设 ~800×360 而不是物理分辨率。
- **横屏锁定**:AndroidManifest `screenOrientation="sensorLandscape"`(冷启动首帧即横屏)+ main.dart 对 android/iOS `SystemChrome.setPreferredOrientations`;manifest 改动必须重装 App 才生效(hot reload 无效)。
- **音频不能拖垮游戏**:`Sfx` 有 `broken` 永久静音开关;`AppError.reportZone` 会把 audioplayers 的插件噪声降级为日志。
- **测试**:widget 测试永远覆盖不到 `main()` 的 bootstrap;断言要用 `tester.getSize()` 查**可见尺寸**(`find` 不查可见性)。游戏有常驻 Ticker → **禁用 `pumpAndSettle`**。原生构建需真实机器;本沙箱 Flutter 写不了自己的 `/opt/flutter/bin/cache` 缓存。
- 手感回归请跑 `flutter test`(含 `test/gameplay_test.dart` 的跳初速/重力/松手-guard/死亡重生断言)。