我花一天做了个格斗游戏原型,判定全对,然后被「美术」劝退了

我花一天做了个格斗游戏原型,判定全对,然后被「美术」劝退了

源码仓库(v1.0.0)


一个 1321 行的单文件 HTML,零依赖、无构建,双击即玩。里面有关羽和赵云,能走能打能放大招,判定框可视化、帧数据面板、逐帧步进都齐了。

然后用户打开看了一眼,说:火柴人真的很丑,没办法玩下去了。

项目在这一句之后结束。

这篇不写「我多努力」,只写三件事:格斗游戏的代码到底难在哪、成本到底贵在哪、以及什么情况下应该在第三天就停手。

一、先说结论

格斗游戏的成本结构是「代码可复用、美术严格线性」。

代码那一半是一次性的:判定系统、状态机、帧数据表、命中结算,做完之后每加一个角色只是往表里填数据。美术那一半是严格线性的:一个角色的逐帧动作要 300~600 张图,加一个角色就是加三百张图,一点杠杆都没有。

而这个项目里还有一条更硬的限制:AI 出不了跨帧一致的逐帧动画。生成模型没有跨帧记忆,第 1 帧袖口三道褶、第 8 帧变成五道;视频模型能出流畅动作,但输出是带压缩的像素视频,没有透明通道,导不进游戏。

所以「用 AI 省钱做格斗美术」这条路,在当前技术下是堵死的。

二、判定跟美术一点关系都没有

这是我在动手前最想验证的一件事。结论:贴在上面的是关羽的立绘还是一块红矩形,判定代码一行都不用改。

格斗游戏的命中判定是「三框制」:

框 作用 谁被检测
hitbox 攻击框 这一帧能打到哪 只在出招的 active 帧存在
hurtbox 受击框 这一帧能被从哪打到 始终存在,随姿态变化
pushbox 推挤框 身体占位,防止两人重叠 始终存在,通常比受击框窄

每逻辑帧跑一遍,顺序固定:读输入 → 推帧 → 各取当前帧的框列表 → 两两矩形相交 → 命中结算。

为什么用矩形不用像素:要可预测、要公平。 像素级判定会让「差一像素没打到」变成常态,玩家会认为是 bug。格斗游戏几十年都用矩形,这是刻意的选择。

三、动作是怎么实现的:帧数据表

这是整个项目最核心的结构。不是「定时切图」,而是把图片索引写进一张表,由状态机逐帧读取。

关羽的轻击,在代码里长这样:

javascript 复制代码
light: {
  frames: [
    { dur: 5,  pose: 'lWind' },
    { dur: 3,  pose: 'lHit',  fx: 'slash',
      hit: { ox: 30, oy: -152, w: 78, h: 62,
             dmg: 8, hitstun: 15, kb: 3.0, stop: 6 } },
    { dur: 10, pose: 'lRec' }
  ]
}

三个字段就是全部:

  • dur:这一帧停几个逻辑帧。它同时决定了动作快慢和判定窗口的长度。
  • pose:姿态名,指向姿态库里的一组骨骼数值。
  • hit:这一帧有没有判定框。为空就是没有。

大招同理,只是数值更极端:

javascript 复制代码
special: {
  label: '青龙偃月斩',
  frames: [
    { dur: 14, pose: 'sWind'  },
    { dur: 4,  pose: 'sWind2', fx: 'charge' },
    { dur: 5,  pose: 'sHit',   fx: 'slashBig',
      hit: { ox: 24, oy: -172, w: 132, h: 124,
             dmg: 24, hitstun: 34, kb: 9.5, stop: 14, heavy: true } },
    { dur: 22, pose: 'sRec' }
  ]
}

起手 18 帧(14+4)、判定 5 帧、收招 22 帧。判定框 132×124,比轻击的 78×62 大一倍多。stop: 14 是命中定格 14 帧------这是打击感的第一来源,比画面重要。

四、五个决定成败的结构

1. 逻辑时钟唯一。 固定 60 逻辑帧/秒,用 accumulator 累积,渲染可以插值。60Hz 和 144Hz 显示器上判定结果完全一致。不这么做,联机永远对不上,不同机器手感也不一样。

javascript 复制代码
const STEP = 1 / 60;
acc += dt;
while (acc >= STEP) { step(); acc -= STEP; }

2. 帧数据表驱动,逻辑与表现分离。 加招式只加数据,不改代码。这也是换美术时判定完全不用动的原因。

3. 姿态库按角色覆盖。 公共姿态 + 每角色一套覆盖,姿态里加一个 wAng 字段直接控制武器朝向:

javascript 复制代码
// 关羽:刀劈走弧线,刀从脑后抹到身前
lWind: { lean:-12, hipY: 94, armF: {x: -12, y: -78}, wAng: -128 },
lHit:  { lean: 24, hipY: 88, armF: {x:  50, y:  22}, wAng:   46 },

// 赵云:枪刺走直线,收枪腰侧再平推

同一套渲染器,画出两种兵器性格。

4. 命中按帧号去重,不是布尔。 这一条我在第一版就写错了:

javascript 复制代码
// ✗ 错误:一个招式只能命中一次,多段技被压成一段
if (att.hasHit) return;

// ✓ 正确:按帧号去重,不同帧可以各自命中一次
if (att.hitDone[att.frameIdx]) return;

三段连刺(6+6+11 伤害)靠的就是这个。改成帧号去重之后,实战 9 秒掉血构成是 23 + 23 + 5×5------两次完整三连刺加五次轻刺,逐段生效。

5. 特效层不参与判定,也不受 hitstop 影响。 否则打击定格的那几帧里,刀光和火花一起冻住,视觉反馈反而变弱。

五、三个真实踩到的坑

坑 1:判定窗口会跨精灵帧边界

帧数据表按「图」索引,一张图通常停 4 个逻辑帧(67ms)。但格斗游戏轻攻击的 active 只有 2~4 逻辑帧。

如果美术把 12 帧的轻拳平均分成三张图 [4,4,4],命中帧覆盖逻辑帧 58,而真实 active 是 4 5 帧,帧数据表就表达不出来了。玩家的体感是「有时打得中有时打不中,而且看到的帧数和手感对不上」。

修法:命中帧那张图的 duration 必须精确等于该招的 activeFrames 。轻拳应该是 [3,2,7] 而不是 [4,4,4]。只有循环帧才用长 duration。

这一条美术侧零成本,但如果不写进契约,换美术那天会集体爆雷。

坑 2:后台标签页把 requestAnimationFrame 节流了

我在调判定的时候,截图看到 AI 像静止一样,12 秒只走了 20 帧,第一反应是「AI 逻辑写坏了」。

实际上是浏览器把后台标签的 rAF 压到了 1 帧/秒,按键边沿也被吞掉,游戏逻辑本身没问题。

正确做法不是加大睡眠时间,而是:把标签切到前台,再用「暂停 + 逐帧」做确定性验证。靠截图连拍去抓 active 帧是靠不住的。

坑 3:火柴人待机时武器杵到了地上

武器朝向如果纯跟手臂向量,待机手位一低,青龙偃月刀就插进地面。武器角度得单独给字段(wAng),不能靠手臂推算。

这种「一眼假」的问题,光看代码永远发现不了,必须真的渲染出来看。

六、体量账

原型做完之后,我让三个人(策划、工程、美术)分别估了一遍,数字对不上,差了四五倍,最后对齐口径重算:

档位 角色 招式/角色 代码人日 美术人日 合计
垂直切片(灰盒) 1 1 18 0 18
最小可玩(纯灰盒) 2 5 93 0 93
最小可玩 + 正式美术 2 5 93 ≈60 ≈153
标准 4 9 183 120 ≈300
完整 8 10 280 376 ≈656

换算到日历时间才有意义:

累计人日 业余 1 人日/周 半职 3 人日/周 全职 5 人日/周
18 18 周 6 周 4 周
93 93 周 31 周 19 周
153 153 周(3 年) 51 周 31 周
300 300 周(6 年) 100 周(2 年) 60 周

关键事实:4 个角色时美术是代码的 1.9 倍,8 个角色时是 3.1 倍。 代码增长低于线性而且 AI 能提速,美术是严格线性、AI 提速为零。所以「多加一个角色」这个决定,成本几乎全在美术资产上。

七、那 KOF'97 是多少人做的

约 5070 人,10 12 个月,换算下来 1.3 万人日以上。是上面 2 角色原型的 85 倍。

但人数和工期只是表面。真正的差距在三个不在同一起跑线的地方:

  1. SNK 没有每年重画 28 个角色。 KOF'97 里绝大多数精灵是从 '96、'95 甚至饿狼传说搬过来的,有的连色都没改。他们从 1991 年就开始积累格斗精灵,到 1997 年是六年存量做增量。而你是在空地上盖第一层。
  2. 他们造的是自己家的硬件。 Neo Geo 是 SNK 自研平台,工具链、烧录卡、调试机都是内部的,美术从第一天就按硬件规格画,不会出现「画完了发现塞不进去」。
  3. 那 50 个人是职业像素动画师。 一个人画 300~600 帧连贯动作是工种,几千小时训练,还有真人拍参考视频逐帧描的土办法。

八、现代小团队是怎么活的

没有一个成功的小团队,是靠「画 300 帧/角色」活下来的。他们全是靠换美术风格过关。

作品 团队 美术策略 结果
Skullgirls 十余人 高清手绘逐帧,一个角色上千帧 靠众筹 83 万美元才成,后续单角色预算约 25 万美元
Divekick 两人 极简几何图形 + 一根线 卖了几十万份
Nidhogg 两人 纯色剪影 口碑销量双赢
单人格斗(Footsies 等) 一人 方块、色块、极少动作 被格斗圈当训练工具

这就是美术受限的人撞上的墙:格斗游戏的美术成本,几乎不受「你画得多简」影响。 因为它要求的不是「好看」,而是每个动作的每一帧都能被独立引用,并且对得上判定框。这个要求把「减少画量」的路堵死了一半。

九、什么时候该停

回头看,这个项目最贵的一课不是技术,是顺序:

火柴人作为「验证手感的占位」在工程上完全合理,但它是给开发者看的占位,不是给玩家看的产品。 我把这两件事混为一谈了。

正确的做法是:动手前先用半天做一个角色的完整成品效果------哪怕是免费素材套用,或者水墨剪影这种用代码就能画好的风格------让需求方先在「丑不丑」上拍板,再谈架构。

因为功能验证通过 ≠ 项目成立。

至于那 1321 行代码,没有浪费。判定系统、状态机、帧数据表、多段判定、hitstop、调试层,这套骨架换任何美术只需要换渲染层。它是一份能一直用下去的东西,只是暂时用不上了。


如果这篇文章帮你省下了一个月的试错,那它就值了。

真正想清楚「要不要做」,比闷头做三个月更重要。

相关推荐
石小石Orz19 分钟前
AI 正在蚕食我们的编码能力
前端·后端·算法
陈随易1 小时前
专注飞机 FocusFlight,两个人把番茄钟做成了一趟航班
前端·后端·程序员
总要学点什么2 小时前
第98篇:三维特效-材质线效果——给一条线换十种材质,虚线的图案只填了一半
开发语言·前端·javascript·ecmascript·gis·webgl·材质
10share2 小时前
基于大模型的 i18n 方案:把中文原文当 Key,轻松搞定多语言维护
前端·vue.js
OpsEye2 小时前
混合云 AI 架构,如何统一做敏感内容拦截?
javascript·ai编程
Funlang4 小时前
给机器写的 JavaScript 造个笼子
开发语言·javascript·ecmascript
古茗前端团队4 小时前
由 key 引发的思考
前端·javascript·react.js
kyriewen4 小时前
我扫了 422 个仓库,近一半 CI 在裸装依赖
前端·javascript·程序员
鹏多多5 小时前
AI 到底能不能独立做个产品?我拿一个小程序验证了
前端·微信小程序·ai编程