乐于分享
好东西不私藏

OpenClaw手记第二季(八)射击乐园

OpenClaw手记第二季(八)射击乐园

今年四月,跟朋友出去玩,我家小朋友看别的哥哥玩射击类游戏,眼馋得不行,也想上手。商场里那种街机他够不着、节奏也快,回家路上他还在念叨。

我跟他认真对了一遍需求——不是敷衍「以后给你买」,而是当成一个小项目来聊:

  • 难度要低:反应不用那么快,别几枪就 Game Over; 

  • 不要太血腥、不要太吓人:不要写实枪械、不要喷溅特效,画面尽量轻松; 

  • 能自己玩一会儿:鼠标就能操作,规则几句话讲明白。

聊完我心里就有数了:反正大模型额度闲着也是闲着,这个需求也不困难,不如自己做一个「射击乐园」——名字也故意取得温和一点。技术栈选(Cursor)熟悉的 React + TypeScript + Vite + Tailwind,纯前端 Canvas,不碰后端;鼠标移动准星、点击射击,固定靶 / 移动靶 / 弹出靶加分,漏靶扣生命、累计命中升级关卡。代理式编程写代码、修 Bug、写文档、部署上线,是手段;初衷始终是孩子能安心玩几分钟

只不过这仍然比预想的一句话就能成功生成整个项目困难些,毕竟Composer 2 也就是个Kimi K2.5。彼时Cursor还没有内置浏览器截图查看运行是否正常的功能,因此虽然所有测试通过,真正跑起来却是一片黑屏。

Debug倒也不难,把现象告诉Cursor,它自己总是能分析出问题的,例如不同的函数打架,修复是极快的。

进入游戏界面后才是真正的考验,这里鼠标转动方向,点击射出子弹,虽然直观但无论如何背后也要有一个小小的引擎进行计算,算错了自然是不可避免的,例如在最开始的版本中,代码实现犯过以下错误:

  1. 角度定义反了(枪应指向准星,不是反过来)。

  2. 子弹 vy被 Math.abs写死,和瞄准方向脱节。

  3. Canvas CSS 缩放 vs 内部像素,鼠标坐标未换算。

还是那句话,Agent觉得的“对”跟我们人类觉得的“对”往往不一致。好在Cursor性格不错,聊几次之后就能把这些问题再次修复。最终成品形式如下:

其中这些靶子形状的怪物是慢慢下落的,笑脸形状的怪物有的会左右移动,有的就呆在原地等着挨打,总之我觉得是没有什么难度的。

按开发者的标准,这一轮算收尾了。
但真正让人记得住的,是把它端给孩子试玩之后的那几分钟。

我家小朋友对失败非常抵触:漏掉一个靶、生命往下掉,他的反应不是「再来一局」,而是整个人神经紧绷,心脏突突突直跳。
他对画面上往下落的目标也害怕——哪怕我已经把风格收得很轻、没有血腥、没有吓人特效,那些移动的、下坠的东西,在他眼里仍然像一种压力。

所以,即使这已经是我认知里能做出来的最简单的射击游戏——节奏慢、规则短、鼠标点一点就行——他还是很干脆地说:

「不玩这个!再做个别的!」

那一刻既感人,也挺让人清醒。
感人在于:我确实是为他花时间、对需求、一行行改;清醒在于:「最简单」是父亲的尺度,不是孩子的尺度。眼馋哥哥玩的手游,不等于能接受爸爸写的网页小游戏;我想要的是「像哥哥那样酷」,他需要的是「不会像考试一样失败、不会像怪物一样掉下来」。

没关系,既然能讲明需求,就能改进,无非是再做一个更加贴合需求的游戏而已,能有多难呢?

另外,大家有空闲的时候也可以试试看,评评理,这游戏能有多难呢……

http://124.221.66.169/shooting/