海面渲染解决的是“好看”,不是“能冲”

做冲浪游戏的团队,第一周通常就能把海面做得很漂亮。图形学里成熟的方案是FFT海面谱,也就是Tessendorf方法:用海洋统计谱生成成千上万个不同频率的小波,叠加出一片随风起伏的开阔海面;近处再补几条Gerstner波,让波峰更尖、更有体积感。这套方法在开阔海域几乎无可挑剔,但它模拟的是深水里的风浪:浪不会真正“破”,也没有一个固定的位置让人起乘。

冲浪玩家关心的恰恰是另一件事:下一道浪从哪里开始卷,能卷多长,浪壁够不够陡,有没有肩部可以跑。一片统计意义上正确的海面,放进冲浪比赛里可能完全没法用——要么所有浪同时碎成一条白线(closeout),要么根本不碎,玩家趴在板上划水,划到计时结束。

所以在im体育的AI冲浪模块里,我们把问题拆成两层:渲染层继续用谱方法负责远处海面的质感;玩法层单独生成“可冲的浪”,它的每一个参数都会进入比赛逻辑。下面按一道浪真实的生命顺序来讲:它从哪里来,在哪里破,被什么修饰,按什么节奏到达,最后怎么判断它值不值得放进赛场。“海浪本身就是比赛地图”这句话,要从浪的源头开始落实。

第一步:涌浪在离岸很远的地方就定下了基调

海岸边能冲的浪,大部分不是本地风吹出来的,而是远处风暴生成、传播了几百甚至上千公里的涌浪(Swell)。传播过程中,短周期的杂乱风浪逐渐衰减,留下的是周期较长、排列整齐的波列。生成器最先确定的,就是这组涌浪的几个参数:

  • 周期:相邻两道浪经过同一点的时间间隔。周期越长,浪携带的能量越多,“感觉到”海底的深度也越深,到岸边时往往更有力、更陡。8秒左右的涌浪和16秒的涌浪,即使离岸浪高相同,到岸后的形态也会差别很大。
  • 方向:涌浪相对海岸与礁石的来向。同一块礁石,西南向涌浪可能打出一条向右剥离的长浪,正西向则可能让浪正面拍在礁石上直接闭合。
  • 离岸浪高:深水中的有效浪高,是后面所有浅化与破碎计算的起点。
  • 方向离散度:涌浪是否来自一个很集中的方向。离散度小,浪线整齐;离散度大,浪线上会交错出现多个峰值。

这些参数在im体育里来自世界天气系统:某片海域几天前的风暴,会在今天变成海岸的涌浪。因此它们是可以被“预报”的,玩家和AI对手都能在出发前看到类似“周期14秒、西南偏南、离岸1.6米”的浪况信息,这和真实冲浪者看预报的方式一致。预报告诉你今天大致会有什么浪,但不会告诉你下一道浪的细节。

第二步:海底地形决定浪在哪里破、怎么破

涌浪进入浅水后开始“触底”:传播速度下降、波长缩短、浪高上升,波峰逐渐前倾。当水深大约降到浪高的1.3倍附近时,浪就会失稳破碎。这条经验关系意味着,决定破浪点(Break Point)位置的其实不是海面,而是海底。

我们为每个冲浪点保存一张水深图,并标注底质:沙底、礁石或卵石。三类地形的行为明显不同:

  • 礁石浪(Reef Break):海底形状稳定,破浪点每天几乎在同一位置,浪可以很陡、很空心,但落水代价高。
  • 岬角浪(Point Break):涌浪绕过岬角时发生折射,沿岸线一路剥开,能提供很长的骑乘距离。
  • 沙滩浪(Beach Break):沙坝会随风暴和季节移动,所以破浪点在游戏里也允许缓慢漂移,今天的好峰值下周可能换了位置。

浪不是在一个点上同时碎掉的。浪线上最先破碎的位置叫峰值(Peak),破碎沿着浪线向一侧或两侧推进,形成可以滑行的浪壁(Wave Face)和尚未破碎的肩部(Shoulder)。这里有一个比浪高更关键的量:剥离角(Peel Angle),即破碎线与浪峰线之间的夹角。剥离角太小,破碎推进得比任何冲浪者都快,整道浪等于闭合;剥离角适中,破碎速度和冲浪者的滑行速度相当,这就是能“追着浪跑”的好浪;剥离角过大,浪又会显得软、推力不足。

折射也在这一步计算。涌浪在浅水区会向较浅的一侧弯折,礁石前端的浅滩会把能量聚到一个点上,这就是很多著名浪点有明确峰值的原因。生成器不需要逐帧求解完整的流体方程,但需要在水深图上做一次射线式的折射估算,得到每段岸线上的浪高分布和破碎顺序。

第三步:风和潮汐在同一道浪上继续动刀

涌浪和地形决定了浪的“骨架”,风和潮汐改变的是它的表面和位置。

风(Wind)。离岸风从陆地吹向海面,会顶住浪唇,让浪壁保持光滑、推迟破碎,形成更陡、更干净的形状;向岸风则把浪顶提前推倒,浪面起碎纹,破碎变成一段段不规则的白水。侧岸风介于两者之间,往往让浪的一侧更好冲。风在游戏里除了改变浪形,还直接进入操控:强离岸风下起乘时,浪唇喷出的水雾和气流会把板头往上掀,玩家需要更晚、更用力地压板。

潮汐(Tide)。潮位变化就是水深变化。按前面那条关系,水深一变,破浪点就会前后移动。以礁石浪为例,低潮时礁石离水面更近,浪破得更急、更空心,也更容易把人拍到礁石上;高潮时水深增加,有些中等浪干脆不破,或者破得软绵绵。一个浪点可能只在中潮上涨的两小时里状态最好,这是真实冲浪里非常重要的节奏,我们希望它也成为游戏的节奏。潮位在一天里怎样轮换、怎样避免浪循环重复,海浪生成那篇有更细的拆解。

海流(Current)。浪把水推上岸,水需要回流,于是形成离岸流。它会把趴板划水的玩家往外带,也会在浪壁上制造局部起伏。我们把离岸流当作一种可读的地形特征:有经验的玩家会借它省力划到外面的排队区(Lineup),新手则会发现自己划了半天还在原地。

第四步:浪组节奏让“等浪”成为比赛的一部分

真实海面上的浪并不是等间隔、等高度到达的。几组周期接近、方向相近的涌浪叠加后,会形成振幅忽大忽小的波群:一组三到七道较大的浪(Set)过去之后,是一段相对平静的间歇(Lull)。浪组间隔可能是几分钟,也可能更久。

如果游戏按固定节拍发浪,玩家很快会数出节奏,比赛就变成背答案。我们的做法是用两到三个周期接近的涌浪分量叠加出包络,再在包络上加有限的随机扰动:浪组的平均间隔和平均规模可以从预报推出来,但具体下一组有几道、第几道最大,玩家只能靠观察外海海面的隆起来判断。

这一步决定了冲浪比赛的大量策略空间:是抢浪组的第一道,还是等后面更大的那道;间歇期要不要拿一道小浪先保底;什么时候该划回排队区。它也约束了AI对手——它们和玩家一样看不到下一组浪的“答案”。

第五步:生成出来的浪,要先过可冲性评估

参数全部确定之后,生成器得到的是一道候选浪。它不会直接进入赛场,而是先经过一轮可冲性(Rideability)评估。这一步回答的问题很朴素:一个中等水平的玩家,在这道浪上有没有可以完成的起乘,以及一段可以滑行的路线?

评估项 来自哪些参数 对玩法的影响
破浪高度与陡度 离岸浪高、周期、水深、风起乘难度、是否有管浪机会
剥离角与剥离速度 涌浪方向、折射、地形走向能否追上破碎线,还是直接闭合
可用浪壁长度 地形长度、潮位 一道浪能做几个转向动作
浪面平整度 风向、风速转向稳定性、速度损失
破碎后的水深与底质 潮位、礁石或沙底 落水风险与恢复时间

评估不是为了把所有浪都修成好浪。真实海边本来就有闭合浪、软浪和危险浪,它们是选浪判断的一部分。我们的原则是:一个浪组里闭合浪的比例可以很高,但整个比赛时间窗口里必须存在一定数量达到可冲下限的浪;如果某个天气组合下整片浪点都不可冲,赛事系统就不会在这里、这个时段安排比赛,而是把它作为“今天浪不好”的世界状态呈现出来,让玩家去别的浪点碰运气。

一个设计示例:同一片礁石的上午、中午和下午

用一个模拟示例说明这些步骤怎么叠加。假设海岸边有一块向右剥离的礁石,某天预报是周期14秒、西南向、离岸1.5米的涌浪。

  1. 上午9点,低潮,微弱离岸风。礁石前端浅,峰值靠外,浪陡而空心,剥离角偏小,大约每三道浪就有一道在中段闭合。适合技术好的玩家冒险抢管浪,新手落水概率很高。
  2. 中午12点,潮位涨到中潮,离岸风依然轻。破浪点向内移动了一段,剥离角变大,浪壁变长,是一天里最好冲的窗口。
  3. 下午3点,接近高潮,海风转为向岸。一部分中等浪在礁石上方不破,破的那部分浪面起碎纹,路线更短,单道浪的得分上限明显下降。

同一组涌浪,三个时段是三张不同的地图。比赛安排在中午,玩家的主要压力是和对手抢最好的那几道浪;安排在上午,就成了一场风险管理比赛。即使玩家很熟悉这块礁石,也需要根据当天的潮汐和风重新判断,而不是回忆上周同一时间的浪。

生成方式怎样影响AI对手和玩家路线

当浪成为地图,AI对手的强度就必须建立在“读浪”上。我们的设计是:AI冲浪选手只能读取玩家也能看到的信息——预报、外海海面的隆起、前一道浪的破碎位置、潮位和风向;它不知道随机种子,也不会提前拿到下一组浪的参数。AI比新手强,是因为它更会判断浪组、选更好的起乘位置、更少在闭合段上浪,而不是浪偏向它,或者它的板子在关键时刻悄悄变快。

对玩家来说,可冲性评估的输出也不是一条“正确路线”。同一道浪,有人从峰值深处起乘去赌管浪,有人从肩部起乘换取更长的转向空间,这些选择会在同一道浪的不同路线里继续展开。生成器负责的,是让每道浪都有足以支撑这些选择的结构。

回到标题:海面可以靠成熟的渲染方案做得很漂亮,但下一道浪长什么样,需要从涌浪、地形、风、潮汐一路推下来,再经过可冲性校验。这也是im体育在冲浪项目上投入最多的地方——我们希望玩家在排队区等浪时盯着的是海,而不是计时器。