diff --git a/.opencode/skills/shmup-player-sprite/SKILL.md b/.opencode/skills/shmup-player-sprite/SKILL.md new file mode 100644 index 0000000..544796e --- /dev/null +++ b/.opencode/skills/shmup-player-sprite/SKILL.md @@ -0,0 +1,121 @@ +--- +name: shmup-player-sprite +description: 竖版射击游戏主角机三帧精灵(平飞/左滚转/右滚转)生产流水线。Use when 生成或调整玩家机 sprite、shmup aircraft sprite、bank/roll 滚转侧身帧、调试飞机贴图滚转效果、或为 1942 式竖版射击游戏制作新机型的三帧美术。关键词:主角机、玩家飞机、滚转身、bank frames、P-38、雷光、plane_funnel。 +--- + +# 竖版主角机三帧精灵生产流水线 + +为本项目(1942 式竖版射击)生产主角机三帧动画精灵:平飞基准帧 + 左/右滚转帧。 + +**核心思路:AI 只负责生成一张严格对称的平飞基准图;滚转帧用高度图浮雕投影确定性推导**,避免 AI 生图的不可控性。 + +## 工具位置 + +流水线脚本在本项目 `tools/gen_plane_sprites.py`(git 跟踪,单一数据源)。机型档案在 `tools/profiles/*.json`(内置 `p38`,另有 `plane_funnel`)。 + +迁移到其他项目:把 `tools/gen_plane_sprites.py` 拷过去,参照脚本内 `BUILTIN_PROFILES["p38"]` 写新机型的 JSON profile 即可。 + +## 快速开始 + +```bash +# 密钥:环境变量 APIYI_API_KEY,或 tools/.apikey(已 gitignore,勿提交) + +# 1. 生成基准图(唯一需要 AI 的一步) +python tools/gen_plane_sprites.py --profile tools/profiles/<机型>.json base +python tools/gen_plane_sprites.py check assets/raw/-base.png --symmetry # 对称度应 >95% + +# 2. 确定性生成左右滚转帧(零 API) +python tools/gen_plane_sprites.py --profile tools/profiles/<机型>.json relief + +# 3. 裁边 + 统一缩放 + 居中落地 assets/art/ +python tools/gen_plane_sprites.py --profile tools/profiles/<机型>.json process + +# 4. 校验三帧后 godot --import +python tools/gen_plane_sprites.py check assets/art/.png +``` + +## 几何模型(理解一切的基础) + +滚转侧身帧的正确语义:**飞机不动,相机在飞机 Up-Right 平面内从正上方移到右上方 θ 角处(等距、看向机心)**。正交投影下: + +``` +x' = x·cosθ − h·sinθ y' = y +``` + +三个视觉后果,缺一不可: +1. **翼展按 cosθ 压缩**——整机变窄,纵向(机头到机尾)长度不变 +2. **有高度的部件向画面左移** h·sinθ(θ=45° 时座舱盖偏移 ≈ 高度的 0.7 倍) +3. **朝向相机的侧面露出**——机身/发动机舱右缘出现一条暗色侧面带 + +反面辨析:只压缩(无侧面露出)看起来仍是正俯视;机翼一高一低(2D 斜切)是"相机自己 roll",不是相机移动。 + +## 关键步骤与校验标准 + +### 1. base — AI 文生图基准帧 + +Prompt 四个硬约束,少一个都不行: +- **正俯视**("Top-down view, seen from directly above") +- **严格左右对称**("PERFECTLY LEFT-RIGHT SYMMETRIC")——对称是 relief 高度图正确的前提,也便于镜像出右滚转帧 +- **机头正上**("nose pointing STRAIGHT UP") +- **透明背景**(gpt-image-1 原生支持 `background: "transparent"`) + +校验:`check --symmetry`,对称度 >95%、四角 alpha=0 才放行。不合格重新生成,不要带伤进入下一步。 + +### 2. relief — 高度图浮雕投影(推荐,确定性) + +零 API,从基准图严格推导: +1. **高度图 = 剪影距离变换**(chamfer distance transform):管状结构(机身/发动机舱/尾撑)自动获得圆顶高度,薄结构(机翼/尾翼)自动低平——无需手绘高度图 +2. **逐像素投影** `x' = x·cosθ − h·sinθ`:陡坡(朝相机的侧面)被行内拉伸,缝隙用加暗色填充 → 新侧面露出;背侧坡压缩覆盖 +3. **右滚转帧 = 左帧水平镜像**,两侧严格对称 + +调参旋钮(profile 的 `relief` 节,或 CLI `--angle/--height/--side-dark` 覆盖): + +| 参数 | p38 默认 | 作用 | +|---|---|---| +| `angle` | 45 | 相机偏角。越大翼展越窄、侧面越宽;48px 精灵建议 30-45 | +| `height` | 48 | 机身最大高度(原始图像素)。控制座舱/机脊的横向位移量 | +| `side_dark` | 0.4 | 露出侧面的最大暗度(0-1) | +| `slope_th` | 0.3 | 高度梯度阈值,超过才视为侧面加暗;调低则机翼边缘也带暗面 | + +### 3. process — 裁边缩放落地 + +**统一缩放系数是隐藏的关键约束**:以基准图外接框算出 `scale = min(W/bw, H/bh)`,三帧共用。各帧独立缩放会导致同一架飞机跨帧纵向尺寸不一致(视觉上尾翼"离机身更远")。机身纵向长度是滚转不变量,必须跨帧锁定。 + +### 备选:bank — AI 图生图滚转帧 + +如果一定要 AI 重绘滚转帧(追求真实光影细节),prompt 纪律: +- **直接描述飞机姿态**("banking LEFT at 20°, left wing lower, right side sliver visible"),**绝不描述相机移动**("camera moved" 会被画成前视 3/4 插画) +- 显式钉死:机头正上、机身竖直、翼尖同高、整机完整(枚举 `bank_parts` 所有部件)、螺旋桨机加"螺旋桨侧视不翻脸"(profile 的 `propellers: true`,喷气机设 false) +- 生成后用 `align` 校正朝向(模板匹配 ±90° 搜索;不行就 `align --rotate <度数>` 手动) +- **不要用二阶矩主轴法找角度**——滚转的机翼会带偏主轴 + +## 踩坑记录(血泪教训) + +| 坑 | 现象 | 解法 | +|---|---|---| +| 文生图直接说 "bank/roll" | 模型自由发挥:机翼消失、整机画斜 | 基准帧只生成平飞;滚转交给 relief | +| prompt 描述相机移动 | 画成前视 3/4 插画(螺旋桨翻脸) | 描述飞机姿态,不提相机 | +| 垂直剪切(shear)模拟滚转 | 飞机被拉斜,2D 平面感 | 那是相机自旋,几何错误 | +| 只水平压缩(无侧面) | 仍是正俯视,无滚转感 | 必须有高度驱动的侧面露出 | +| 逐帧独立缩放 | 尾翼看似离机身更远 | 统一缩放系数(锚定基准图) | +| 半分辨率高度图直接采样 | 机翼出现 2×2 方块暗纹 | 高度图双线性升回全分辨率再采样 | +| 陡坡均匀压暗 | 机身整体发暗发糊 | 渐变加暗:脊线亮→边缘暗,且仅陡坡 | + +## 产物约定 + +``` +assets/raw/{name}-base.png AI 原始基准图(保留,勿删——relief 的输入) +assets/raw/{name}-bank-left.png 滚转帧原始图 +assets/raw/{name}-bank-right.png +assets/art/{name}.png 最终三帧(48×56 或 profile 指定尺寸) +assets/art/{name}-bank-left.png +assets/art/{name}-bank-right.png +``` + +游戏侧接线约定(本项目 `scripts/player.gd` 已实现):三帧贴图切换 + 迟滞阈值(进入 0.45 / 退出 0.25),缺图回退平飞帧;滚转帧**不做**代码旋转/缩放变换。 + +## 环境依赖 + +- Python 3.10+,Pillow(仅标准库 + PIL,无 numpy/scipy) +- AI 步骤(仅 `base` / `bank`)需要 OpenAI 兼容图像接口(默认 apiyi,gpt-image-1) +- 密钥:`APIYI_API_KEY` 环境变量,或 `tools/.apikey`(**必须 gitignore**)