我是独立开发者。过去几个月,我一直在打磨我的类幸存者游戏《Just Dying》,并纠结一个核心问题:屏幕上能同时出现多少敌人,且帧率不低于60fps。我搭建了一个压力测试来寻找上限——它不是为了美观或平衡,只是为了突破极限。
最初的版本在200个敌人时就不行了。
接下来才是我真正想写的部分,因为出乎意料:问题从来不是我没法优化,而是我根本不知道该优化什么。
我花了数周盯着性能分析器的火焰图,却一无所获。我能看到帧率下降,却看不出是什么导致了下降。我试一些方法,性能提升5%,再看一眼,依然毫无头绪。这就是独自工作的真正困境:你不缺写代码的能力,而是缺一个能和你一起分析数据的人。
这时候Claude派上了用场。工作流程简单得近乎愚蠢:截取性能分析器的截图,然后问"告诉我这是什么"。由于孤立的截图缺乏上下文,我给游戏构建了独立的调试性能分析通道——按系统划分的每帧耗时、活动实体计数器——并把它的输出也喂给Claude。两者结合后,对话的性质变了。不再是"这正常吗?"而是"这个尖峰只在敌人数量增加时出现,且不随绘制调用扩展,所以不是渲染器的问题——看看单实体成本吧。"
从这四方面入手,才真正推动了性能提升:
- 敌人不再是独立的精灵。 它们现在处于粒子容器中,整个敌群只有一个批处理绘制调用;只有Boss和精英怪保持真正的精灵和各自的动画骨架。这是最大的胜利。代价是:群敌无法完成批处理粒子做不到的事情,而且每个新的视觉效果都要写两遍,分别对应两种路径。
- 碰撞检测移出主线程。 投掷物物理、命中检测和持续伤害都在Web Worker中运行;主线程只读取结果。我保留了旧版本作为备选,这是维护上的失误——我现在有了同一规则的两个实现,它们必须保持一致。
- 空间哈希 用于近邻查询,这是显而易见的优化。不那么明显的是:对返回的结果集进行池化。每帧每个查询都分配一个新集合,产生了足够多的垃圾,导致可见的垃圾回收卡顿。在火焰图中,这不会像你的问题,而像浏览器暂停了一瞬。我花了数月时间一直以为就是浏览器暂停。Claude发现了这个问题;独自一人我永远想不到。
- 还有那些不起眼的优化: 手动剔除(如果你直接渲染,Pixi 8不会为你剔除)、到处设置脏标记以阻止重复计算未变化的内容,以及消灭我能找到的每一处每帧分配。
达到最终数字花了一些时间,这在意料之中:每解决一个瓶颈,下一个就会暴露出来,你只能从头重新测量。但时间花在了修复上,而不是猜测上,这完全不一样。
压力测试结果:在我的笔记本上,1500+敌人稳定60fps,从200个提升而来。
我不认为自己靠自己能接近这个数字。不是因为Claude能写我不会写的代码,而是因为它能读懂就摆在我面前、而我却无法解读的数据。
我现在遇到的瓶颈:我的机器只是一个数据点。浏览器构建是最坏情况——底层没有原生应用来吸收糟糕的帧——所以我不知道在集成显卡的笔记本上能否保持这个水平。
如果你想测试极限:https://justdying.itch.io/just-dying —— 在浏览器中运行,无需下载。Steam上也有一个原生版本的Demo:https://store.steampowered.com/app/4646190/Just_Dying/
如果在你设备上帧率崩溃,请告诉我。你的GPU、浏览器以及大致发生的时间,就足够了。
评论 (0)