我为JS13K制作了一款经典高尔夫游戏——这是一项要求整个游戏压缩后不超过13KB的比赛。游戏包含所有内容:3D渲染器、物理引擎、程序化球场生成器、音乐。所有代码都是基于我自己的引擎从零编写的。

渲染器工作原理

整个系统使用WebGL2,仅一个着色器。每个球洞一次性构建到单个静态顶点缓冲区中,一次绘制调用完成渲染,外加一个小的动态批次用于球、旗杆和轨迹。光照是预烘焙的,因为太阳在单个球洞过程中不会移动,所以顶点仅包含XYZ坐标、雾化标志和RGBA8颜色。随着比赛进行,太阳会逐渐降低,光照和调色板也会随之变化:从第1洞的春日草地过渡到第18洞的秋日日落。

地形是高度场,在可玩走廊内采用2码网格单元,向外网格间距逐渐增大,使周围地形隐没在雾中。法线通过采样高度函数获得,树影直接烘焙到顶点颜色中。树木、灌木、花朵和旗杆均使用相同的旋转体函数构建。树木是由八面体树冠和长方体树干组成的。

我最得意的技巧是水面处理。没有单独的水面网格或着色器。湖泊只是带有材质ID的地形顶点,顶点着色器检测该ID并用沿风向传播的波浪位移它们。波浪梯度重新点亮水面并添加高光反射——这是游戏中唯一依赖于摄像机视角的光照。植被通过另一个ID以相同方式随风摆动。

天空也是几何体,是与摄像机相对位置的圆盘。太阳、云朵和球的阴影均使用相同的柔和圆盘图元——一组带有透明度衰减的环圈。镜头光晕是位于太阳和屏幕中心之间的一串此类圆盘。球后的轨迹是一条丝带,完美击球后会呈现彩虹色。球是唯一移动的发光物体,因此每帧都会在CPU上使用与树木相同的函数重新计算光照。

风将整个场景串联起来。每个球洞只有一个风向和风速,所有元素都读取它:旗帜、云朵、植被和水面都响应风。球也会受到风的影响。物理引擎计算空速为球速减去风速,并以此计算阻力,因此高挑球在空中停留时比低飞球更容易被风吹偏——这与真实高尔夫运动一致。

除了视觉效果,我在游戏手感上投入了大量精力:控制滚动距离的下旋和上旋、改变球杆性能的球位状态,以及弹跳计。你可以在任何桌面或移动设备上玩。源代码已上传GitHub,欢迎查阅。

JS13k提交地址:https://js13kgames.com/2026/games/sunshine-golf-classic#play
视频演示:https://www.youtube.com/watch?v=nIiqMKTaW2M
GitHub:https://github.com/KilledByAPixel/Golf13K