做捕鱼项目这些年,我最大的感受是:决定玩家去留的往往不是炮台多华丽、子弹多花哨,而是屏幕里那群鱼“游得对不对、死得爽不爽”。前几篇把捕鱼游戏的整体框架、炮台、子弹、网讲了一遍,这一篇集中拆“鱼”本身——鱼种怎么分类、鱼群怎么游、鱼怎么被打死、死了之后系统怎么结算,以及刷怪节奏和概率模型怎么配合。
这篇内容适合三类人看:刚入行的游戏策划、写捕鱼逻辑的客户端/服务端开发,以及做数值和运营活动的同学。我会按“从表现到机制、再从机制到数值”的顺序讲,尽量把每个决策背后的原因都交代清楚。
1. 鱼的定位:从“玩家眼中的分值”反推设计
1.1 基础鱼种怎么分级:小杂鱼、中档鱼和镇场大鱼
打开任何一款捕鱼游戏,玩家第一眼不会看你的代码,也不会关心你的RTP模型,他只会问三句话:这条鱼值多少分?好不好打?打死之后爽不爽?所以鱼的分类,本质上是从“玩家预期”反推出来的。
我习惯把基础鱼分成三档。第一档是1~3倍的小杂鱼,比如小丑鱼、小鲤鱼,体型小、游速快、成群出现,作用就是让玩家不断获得“打中了”的正反馈,把游戏节奏带起来。第二档是5~20倍的中档鱼,比如灯笼鱼、魔鬼鱼、海龟,体型明显变大,出现频率适中,打死一只会有比较强的“赚到了”情绪,这一档是玩家长时间在线的主要动力。第三档是50倍以上的大鱼和BOSS,比如金鲨、蓝鲸、章鱼王,出场频率低、游速慢、血厚,但一旦打死,屏幕特效和飘分数字能直接刺激整个房间的氛围。
这里有个很容易犯的错:把倍率当“血量”用。倍率只是奖励倍率,真正决定它难打的是捕获概率,不是你给它加了多少血。血条是表现层的,概率是数值层的,两者不要混。
| 定位 | 代表鱼种 | 倍率区间 | 游速 | 行为特征 | 单条产出预算占比 |
|---|---|---|---|---|---|
| 小杂鱼 | 小丑鱼、小鲤鱼 | 1~3 | 快,有停顿 | 成群结队,无规避 | 30% |
| 中档鱼 | 灯笼鱼、海龟 | 5~20 | 中等 | 2~3条一小队,偶尔变速 | 40% |
| 大鱼 | 金鲨、蓝鲸 | 50~200 | 慢,稳 | 单条巡游,血量厚重 | 20% |
| BOSS/事件鱼 | 章鱼王、远古巨龙 | 300以上 | 极慢 | 带技能,需要集火 | 10% |
我见过有些团队把70%的产出预算都放在大鱼上,想着“搏一搏单车变摩托”,结果玩家打半小时一条大鱼都打不死,负反馈拉满,第二天留存直接掉一截。大鱼是用来制造情绪峰值的,不是用来承接日常产出的。
1.2 特殊鱼:技能鱼、彩金鱼和事件鱼怎么加
基础鱼负责稳定节奏,特殊鱼负责打破节奏。常见做法是三种:技能鱼、彩金鱼、事件鱼。
技能鱼是指打死之后触发全屏效果,比如炸弹鱼、闪电鱼。这类鱼在设计时最重要的是“触发链路要短”:鱼的死亡动画播完立刻进入结算特效,不要让玩家等待。我之前见过一版,闪电鱼死亡后还要等0.8秒的爆开动画才开始劈闪电,玩家在等待期间什么都干不了,体感非常卡。正确的做法是死亡动画与闪电特效并行播放,同时结算。
彩金鱼更像一个“移动的运营活动”。它不常驻,由后台在特定时间段投放,身上挂着一个不断跳动的累计奖金数字,玩家打它不是为了它的固定倍率,而是为了那个奖金池。奖金池通常由全服玩家消耗的炮值按比例累积,比如每消耗1000金币注入3金币。这样设计的好处是把“个人消耗”和“全服共同目标”绑定,刺激玩家在大奖池出现时集中在线。
事件鱼则是跟节日、版本活动挂钩的,比如春节的财神鱼、周年庆的金龙。它一般有自己的独立概率和独立产出池,不占用常规鱼种的经济预算。接入方式最好做成配置表驱动,运营在后台配好生效时间、出现权重、技能类型,客户端拉配置后自动替换,不用发版。
2. 鱼的行为AI:从游动路径到受击反馈的状态机设计
2.1 为什么捕鱼游戏不用寻路,而是“预置路径+随机偏移”
很多没做过捕鱼的朋友第一次听到“鱼群AI”,第一反应是上A寻路。这是捕鱼项目里最典型的误区。A适合角色需要在复杂地形里绕障碍的场景,比如MOBA、RTS。但捕鱼游戏里的鱼几乎不需要智能避障,玩家要的是“可预测、有美感、有节奏”的游动。
我的做法是预置路径点系统。每条鱼或者每组鱼群在生成时绑定一条路径资源,路径由一系列控制点组成,可以是直线段、折线、贝塞尔曲线或者闭合环形。鱼沿着路径匀速移动,到达终点后按配置决定是消失、折返还是从另一条路径继续。
为了不让鱼游得太机械,会给每个控制点加一个随机偏移量,范围通常在20~80像素(2D)或0.5~1.5米(3D),同时让鱼的游动速度在一定区间内波动,比如基础速度是3米每秒,波动范围±20%。这样视觉上仍然是一条“稳定”的路线,但每条鱼的具体轨迹又不一样。
3D捕鱼还要额外注意一个问题:鱼的转向半径。鱼不像车,不会原地转弯,尤其是大鱼。转弯半径太小会出现瞬间扭头穿模的“僵尸转”。我一般会给大鱼单独配置一个较大的转向半径,并在转向时叠加身体朝向的插值,让鱼头先转、身体随后跟上,这样才像一条“有质量的鱼”。
2.2 鱼的动画状态机:受击反馈是捕鱼手感的核心
鱼的状态机其实很简单:游动(Swim)→ 受击(Hit)→ 死亡(Dead),加上一个待机(Idle)用作出生和特殊技能前摇。复杂的是这4个状态之间的“衔接手感”。
游动状态就是常规的游泳动画,循环播放。但当鱼的子弹打到鱼身上时,鱼必须立刻响应。这里的关键是“受击反馈要明显但不能打断节奏”。我常用的方案是:受击时鱼播放一个极短的“顿挫动画”,时间控制在0.1~0.2秒,然后回到游泳状态,同时鱼身闪现一层白色高光,持续0.2秒左右。顿挫动画不是真的做一个新动画,而是在游泳动画基础上做时间缩放和位移偏移,比如把播放速度降为原来的30%,同时朝子弹来的方向偏移几个像素。这样玩家能明确感知“我打中它了”,但又不会因为鱼被完全定住而觉得假。
死亡状态要注意的是“死亡表现优先级”。普通鱼被网捕住后,播放死亡动画、爆金币、飘分,这些可以按顺序来;但特殊鱼(比如炸弹鱼)需要打断网的表现,优先播放自身技能。所以鱼的状态机里最好加一个“触发优先级”字段,比如炸弹鱼死亡时,技能触发优先级高于一切表现。
动画状态机这块还有一个隐藏雷区:客户端表现和服务器状态不同步。比如玩家炮台打出的子弹在客户端已经碰撞了鱼,但服务器还没返回结果,这时鱼不能提前播死亡动画;反过来,如果服务器已经判定死亡,但客户端还在播游泳动画,就会出现“鱼已经死了还在游”的穿帮。解决办法是在客户端做一个“表现缓冲队列”,收到服务器的击杀结果后,再按一个极短的延迟(通常50~120毫秒)播放死亡表现,尽量对齐其他玩家的视角。
3. 鱼群生成器:刷怪节奏是捕鱼的隐形关卡设计
3.1 出鱼结构决定了“玩家一网能兜到什么”
鱼群生成在很多项目里被简单做成“每隔几秒刷几条鱼”,这就浪费了刷怪节奏对玩家情绪的调控作用。我的理解是:鱼群生成器相当于一个自动运行的关卡脚本,它决定玩家在任何时间点面对的屏幕内容。
先定义一个“鱼群配置表”,每一项包含:鱼种ID、数量范围、路径ID、生成间隔、同屏数量上限、权重。系统按权重随机选取下一波刷什么。比如普通波里,小杂鱼权重60%,中档鱼30%,大鱼10%;每一波的生命周期由“鱼走完全程消失”或“到达目标点后自动消散”来控制。
我还会再加一个“同屏鱼数量上限”的硬约束。2D捕鱼通常是60~100条,3D因为镜头和空间更大可以到120~150条。超过上限时不再生成新的鱼群,直到部分鱼离开屏幕或被捕捉。这一步很关键,因为鱼太多一方面性能扛不住,另一方面玩家会“眼花到不知道打哪条”,反而降低爽快感。
鱼群的编队方式也要设计。小杂鱼适合6~10条排成一排或者一簇,让一网下去能捞到好几条,制造“赚了”的感觉;中档鱼2~3条一队,让玩家有瞄准的过程;大鱼和BOSS必须单独行动,给它们一种“很难靠近”的压迫感。编队的移动方式建议统一用“以队长路径为主,队员沿法线方向偏移”,这样整个鱼群能保持松散的整体感,而不是完全同步的生硬队列。
3.2 波次节奏:用“高潮波”和“喘息波”交替控制玩家情绪
光有随机刷怪还不够,需要有一个波次脚本在背景运行。我一般把波次分为三种:普通波、小高潮波、大鱼波。
普通波持续20~30秒,以小鱼加少量中档鱼为主,让玩家持续有收获。小高潮波通常持续5~8秒,会一口气刷出3~5条中档鱼和1条大鱼,让玩家有“换大炮打一波”的冲动。大鱼波是全屏事件,每90秒左右触发一次,刷出1条BOSS级鱼,同时搭配一圈中档鱼当作“护卫”。
三种波次的交替要遵循“强弱强弱”的节奏,不要让大鱼波连着出,也不要把普通波拖到1分钟以上。运营活动期间可以通过后台配置临时调整波次权重,比如“鱼潮活动”把大鱼波间隔从90秒缩短到40秒,但代价是每发炮弹的期望返还率要相应调低,否则经济系统会失控。
4. 碰撞、捕捉与结算:一颗子弹和一网鱼是怎么对上的
4.1 碰撞模型选型:球形、胶囊体还是多子碰撞体
客户端物理这块,我和很多人一样,最早用精确的像素级碰撞,鱼身体每个像素都参与检测,结果性能惨不忍睹。后来换成简化几何体,效果立刻好了一个量级。
2D捕鱼最常用的是圆形碰撞体和胶囊体碰撞体。小杂鱼用圆,半径大约是身体宽度的40%;中档鱼和大鱼用胶囊体,就是两个圆中间夹一个矩形,和鱼身的长条形状贴合度很好。3D捕鱼则统一用球体和胶囊体,配合一个“碰撞权重”字段,控制同一颗子弹能穿透几条鱼、最多穿透多深。
更关键的是“子弹和鱼谁检测谁”。不做性能优化的话,常见做法是每颗子弹和每条鱼两两检测,复杂度是O(子弹数×鱼数),同屏100条鱼、同时飞30颗子弹,一帧就是3000次检测,勉强能跑,但Plus版本总要为手机性能留余量。
我建议反过来:每颗子弹在飞行时按帧采样,记录其轨迹经过的碰撞体。因为子弹是直线飞行的(就算有弹道弧线也是分段直线),可以用射线检测或者连续扫描的方式拿到途经的鱼列表。这样检测次数从“子弹数×鱼数”降为“子弹数×穿越鱼数”,同屏鱼再多也不怕。
4.2 一网鱼的捕捉顺序:碰撞列表、上限截断与后端结算
子弹碰撞鱼之后,会发生两件事:子弹可能继续飞行去碰撞下一条鱼,同时网在这个点展开。网展开后,会根据网的半径捕捉范围内的所有鱼。这里有一个所有新手都会踩的坑:一张网拉起来,把半径内80条鱼全包进去了,按照概率逐条判定,最后全屏都是死亡动画,产出瞬间爆表。
解决方法是给每张网加上两个限制:最大捕捉数和判定顺序。最大捕捉数一般设置在8~16之间,超过部分即使视觉上在网内也不参与结算。判定顺序推荐“优先判定网中心附近的鱼,再按距离向外扩展”,这样至少保证玩家瞄准的那条鱼一定会被处理,而边缘的鱼只是视觉效果。
捕获的最终判定还得分“前端表现”和“后端逻辑”。一句话总结:表现看前端,结果看后端。客户端负责播动画、飘分、抖动,服务器负责真正决定这条鱼死不死。服务器在收到子弹请求后,生成随机数并与鱼的捕获率比对,一旦命中就扣减产出、广播击杀结果。这样玩家离线、断线、重连都不会出现“打死鱼没给钱”的黑账。
5. 数值模型与概率控制:让“运气”稳定在目标范围里
5.1 从目标RTP到单鱼捕获概率的计算过程
捕鱼游戏的数值核心是RTP(Return To Player,玩家返还率),也就是玩家消耗的金币中有多少比例会以奖励形式返还。它不是固定的,是一个期望值。
举个例子,假设我们设定某房间目标RTP=70%,玩家这个小时共消耗了10万金币,那么系统期望返还7万。这7万不能均匀撒,而要按鱼种分配。按前面表里的预算占比:小杂鱼分30%即2.1万,中档鱼分40%即2.8万,大鱼分20%即1.4万,BOSS分10%即0.7万。
然后计算概率。某档鱼的目标产出O、玩家在该档鱼的命中次数N、该档鱼的平均倍率M,那么单发命中后击杀概率p的期望公式是:
p = O / (N × M)
假设中档鱼平均倍率10倍,玩家这个小时命中了4000发中档鱼,目标是产出2.8万,那么p = 28000 / (4000 × 10) = 0.7,也就是中档鱼单发捕获率期望70%。大鱼如果平均倍率100倍,目标产出1.4万,命中200发,则p = 14000 / (200 × 100) = 0.7。
这里你会注意到,只要倍率和命中次数安排好,概率可以保持在一个视觉上“不算太坑”的水平。小鱼为什么感觉好打?不是给它设了高概率,而是它倍率低、命中次数高,每次命中都接近必中。实战中我还会给小鱼单独加一个“保底机制”:连续3发命中同一只小杂鱼未死后,第4发强制死亡。这能避免极端情况下玩家对小鱼连续miss导致暴躁。
5.2 动态概率调节:反作弊与体验保护的后台手段
纯静态概率有个硬伤:如果某个玩家运气特别好,在短时间内打出超出预期的产出,经济就会崩。所以线上捕鱼一定会做动态调节。
我的做法是给每个玩家维护一个“实时RTP窗口”,比如最近1小时的实际返还率。当它明显高于目标RTP时,下调大鱼和BOSS的捕获概率,但保持小鱼和中档鱼的概率不动;当它低于目标RTP时,适度上调中档鱼概率,给玩家一个“回血期”。调幅通常控制在±15%以内,否则玩家会明显感觉鱼“变难打”或者“太好打了”,都是负面体验。
还要防一种作弊场景:玩家用加速挂或脚本瞬间打出大量子弹。正常玩家一分钟最多打30~40发,脚本能打上百发。服务器端要加频率限制和消耗校准,一旦发现单发时间间隔异常,直接拒绝请求。这个属于安全模块,但需要在数值设计阶段就预留接口,否则后期加上去会很痛苦。
6. 捕鱼实战踩坑实录:卡墙、超算、并发与性能排查
6.1 鱼卡地形与路径死区
3D捕鱼最烦的问题就是鱼游着游着插进了海底山体里。原因百分之八十是路径点离障碍物太近,鱼在转向时由于转向半径的存在,船身或尾部被挤进了地形。
排查思路分三步:第一步,在路径编辑器里把鱼的碰撞半径可视化显示出来;第二步,对每条路径做一次自动检测,凡是路径点与最近障碍物的距离小于鱼的碰撞半径加转向余量,就报警;第三步,运行时加一个“卡死检测”,鱼连续性移动距离低于阈值超过3秒,自动把鱼拉回最近的有效路径点。上线后还要在后台统计卡死重置次数,超过设定值就说明该路径配置有问题,需要人工调整。
6.2 一网捞出50条鱼:判定顺序与上限的坑
我见过线上事故:炸弹鱼爆炸后,结算逻辑把全屏300条鱼全部遍历了一遍,明明给每条鱼单独判概率,最后一条没死,但结算数量过大导致客户端卡顿,后台入账延迟,玩家投诉“打到鱼不给钱”。
问题出在“判定顺序”和“结算限流”上。炸弹鱼这类全屏结算,也必须遵守单次结算存活上限,比如最多只处理40条鱼的死亡表现,剩余鱼统一走“沉默结算”——后端入账,但客户端只播放常见的死亡特效,不再逐个播飘分。另外,并发击杀同一批鱼时,每条鱼的结算状态要用一个全局自增ID做幂等,避免同一条鱼既被炸弹结算又被另一颗子弹结算,造成重复发放。
6.3 性能优化:对象池、视野剔除与合批
捕鱼游戏的性能瓶颈几乎都在客户端渲染上。鱼多了、粒子特效多了、飘分多了,低端机立刻掉帧。我常用的三板斧是对象池、视野剔除、动画合批。
对象池是必须的。鱼、子弹、网、飘分数字、死亡特效全部用池化,不Instantiate不Destroy。鱼池建议预热50~100个,子弹池50个,特效池30个。池化之后GC压力和耗时都大幅下降。
视野剔除分两层:2D用屏幕矩形裁剪,跑到屏幕外的鱼直接休眠;3D用视锥剔除加距离剔除,镜头拉远时默认不渲染鱼身上的高模细节,换成低模或直接取消失。动画合批则要求美术资源规范统一:同一种鱼的骨骼动画尽量共用同一张图集、同一套材质,这样合批率才上得去。
最后说一个我自己的习惯:上线前用自动化脚本连续模拟了一个小时高密度射击,同时把客户端帧率、内存、服务器入账延迟全部打点统计,生成一条“单位时间实际奖励/理论奖励”的曲线。如果这条曲线离配置值偏差超过5%,我基本不会让版本出门。捕鱼游戏拼到最后,比的就是这种细节上的稳定性。