《去赶海吧》:用 Cocos Creator 写一款「大鱼吃小鱼」io 微信小游戏
- 约 9 分钟读完 - 全文 3302 字《去赶海吧》:用 Cocos Creator 写一款「大鱼吃小鱼」io 微信小游戏
最近把自己做的一款微信小游戏整理开源了,写篇文章介绍一下玩法和实现上的一些技术点,希望能给同样在做休闲小游戏的朋友一些参考。
- 开源地址:https://gitee.com/soulnew/fishPick
- 技术栈:Cocos Creator 2.3.4 + TypeScript
- 平台:微信小游戏
游戏叫《去赶海吧》,核心玩法一句话概括:摇杆控制赶海人在海里吃海鲜攒贝壳,分数即体型,大鱼吃小鱼,活到最后吃鸡。
一、玩法介绍
1.1 核心循环
玩家通过全屏虚拟摇杆控制角色游动,摄像机始终跟随主角。地图上持续刷新各种海洋生物,吃到就得分(贝壳),分数直接决定体型和战斗力:
- 两个玩家相遇,分高者吃掉分低者——这是 io 类游戏的经典规则;
- 被吃的玩家死亡后会爆出金币(一半的分数拆成最多 10 条金币鱼散布在周围),形成"击杀后抢遗产"的爽点;
- 分数变成负数会直接死亡,所以海里的"毒蛇"(-2 分)和"海胆"(-1 分)是要躲着走的。
graph LR
A[摇杆控制游动] --> B[吃海鲜攒贝壳]
B --> C{遇到其他玩家?}
C -->|我分高| D[吃掉对方]
C -->|我分低| E[触发逃跑 AI / 被吃]
D --> F[对方爆金币]
F --> B
B --> G[实时排行榜 Top5]
1.2 鱼的种类与概率设计
海里的鱼不只是"分",还混入了道具和陷阱,让取舍更有策略性:
| 类型 | 效果 | 概率(实现值) |
|---|---|---|
| 加速道具 | 速度 ×1.5,持续 10 秒 | 5% |
| 护盾道具 | 免疫被吃 / 免疫负分鱼,10 秒 | 5% |
| 鱼、虾、蟹 | +1 分 | 约 35% |
| 水母、海星 | +2 分 | 20% |
| 乌龟 | +3 分 | 10% |
| 海豚 | +4 分 | 5% |
| 毒蛇 | -2 分 | 10% |
| 海胆 | -1 分 | 10% |
负分鱼 10 秒后会自动消失,道具的剩余时间通过节点透明度渐变来提示,玩家一眼就能看懂状态。
1.3 四种游戏模式
主菜单提供四个入口,共用同一套核心机制:
- 无尽模式:死了可以用激励视频复活,AI 每 5 秒重生补齐,刷分为主;
- 挑战模式:“吃鸡"玩法——剩余玩家数低于阈值后进入 10 秒倒计时,活到最后获胜;
- 贪吃蛇模式:吃到的鱼变成尾巴跟随身后,撞到别人的尾巴即死;
- 人挤人模式:群体 Steering Behaviors 的实验玩法,20 个跟随者 + 1 个领队挤作一团。
挑战模式和贪吃蛇模式开局前有一个伪联机匹配面板:“搜索附近玩家…“的省略号动画,12 个头像从 288 张头像图集中不重复随机抽取、按 0~6 秒随机延迟逐个亮起——营造真人匹配的感觉,实际对手全是 AI。这是小团队没有联机服务器时的一个低成本体验方案。
1.4 AI 设计
AI 不是无头苍蝇,每个 AI 出生时会随机分到三种性格之一:
- 进攻型:主动出击追杀人;
- 吃鱼型:闷头吃海鲜发育;
- 闲逛型:随机游走为主。
配合两个细节参数避免行为抖动:被追时有 2 秒"逃跑冷却”(不会在追/逃之间反复横跳),追击最长持续 7 秒(避免死追不放)。AI 找鱼也不搜全图,而是在自己屏幕范围内找鱼最多的格子再过去吃——这个格子划分和碰撞检测用的是同一套空间网格,下面会讲。
二、技术亮点
整个项目是单场景架构(主场景约 400 个节点),28 个 TypeScript 脚本,代码量不大,但性能优化和 AI 上的几个点值得展开说说。
2.1 空间哈希网格:把碰撞检测从 O(n) 降为局部查询
地图上同时存在几百条鱼,如果每帧让每条鱼和所有玩家做碰撞检测,微信小游戏的低端机上肯定扛不住。做法是把地图划分为 20×20 的网格:
graph TD
A[鱼出生/移动] --> B[按坐标计算所在格子<br/>key = x_y]
B --> C[挂到对应桶 BlockManager]
D[碰撞检测] --> E[只查玩家 AABB 覆盖的格子<br/>getCoveredBlock]
E --> F[与桶内鱼做精确检测]
C -.鱼移动跨格.-> G[updatePosofFish 迁移桶]
- 碰撞检测时只查询玩家包围盒覆盖的几个格子里的鱼,而不是全图扫描;
- 鱼移动跨格时从一个桶迁移到另一个桶;
- 这套网格还顺手解决了另外两个需求:视野裁剪(视野外格子里的鱼直接隐藏)和上面提到的 AI 找鱼最密集区域。
再配合降频采样——每 4 帧才做一次全量碰撞检测——CPU 占用就控制得很好了。
2.2 对象池:向 GC 抖动说不
鱼、金币、飘分数字这些高频创建销毁的节点全部走对象池:Map<string, Node[]> 按预制名分桶,全局上限 500。小游戏平台上 GC 卡顿是卡顿的主要来源之一,对象池是性价比最高的优化。
2.3 Steering Behaviors:教科书式的群体行为实现
“人挤人"模式的背后是把 Reynolds 那套经典的 Steering Behaviors 完整实现了一遍(Gamer.ts,约 400 行):
graph TD
subgraph Steering Behaviors
A[Seek 追踪] --> H[AccumulateForce<br/>力累加 + 截断]
B[Flee 逃跑] --> H
C[Arrive 减速到达] --> H
D[Wander 随机游走] --> H
E[Alignment / Cohesion / Separation<br/>群体三规则] --> H
F[Pursuit 带预测提前量的追击] --> H
H --> I[EnforceNonPenetrationConstraint<br/>防穿透约束]
I --> J[最终速度/朝向]
end
其中 Wander 用的是"jitter + 圆半径 + 投影距离"的经典做法,Pursuit 会根据目标速度算提前量。所有行为输出的力先累加、再按 max_force 截断,最后加一个防穿透的分离约束——这套结构在后面的项目里基本可以原样搬走。
2.4 屏外敌人指示器:io 游戏的标配 UX
强敌在屏幕外时,玩家需要感知威胁方向。实现方式是给每个屏外 AI 分配一个钉在屏幕边缘的箭头 + 分数标签:
- 用
atan2计算敌人相对主角的方向角; - 按屏幕对角线角度判断箭头该落在上下左右哪条边上;
- 求射线与屏幕边界的交点作为箭头位置;
- 8 种随机颜色区分不同敌人。
代码不长,但体验提升非常明显——被高分玩家接近时能提前看到箭头逼近,紧张感就出来了。
2.5 运行时程序化动画
美术资源只有每张皮肤 2 帧的游动图,动画是运行时用 cc.AnimationClip.createWithSpriteFrames 动态创建的 2 帧循环。换皮肤只需要换图集里的起始帧索引(100/102/104…),不用为每套皮肤手工做 Animation 资源。6 套皮肤 = 12 帧图 + 一段代码,维护成本几乎为零。
2.6 事件总线与模块解耦
全局只有一个 50 行的事件总线(on/off/fire)加一个聚合单例,音乐、广告、面板切换、碰撞结果全部走事件通信。单场景多面板的结构下,这种轻量解耦比引入框架更实用。
2.7 微信小游戏生态全套集成
- 分包加载:字体、头像、音频、图集拆成 4 个分包,loading 场景按包显示进度;
- 广告变现:Banner + 激励视频复活;
- 社交:
wx.showShareMenu分享、好友排行榜(开放数据域 +wx.postMessage通信)、wx.login登录; - 轻量后端:服务器只负责登录、存分数、拉排行三个接口,玩法完全单机,部署成本极低。
三、整体架构一览
graph TD
subgraph 场景层
L[loading.fire<br/>分包加载 + 剧情动画] --> F[FirstPannel.fire<br/>单场景多面板]
end
subgraph 玩法层
F --> GM[gameManager<br/>刷鱼/模式管理/降频碰撞]
GM --> P[Player / BasePlayer<br/>得分/体型/道具/死亡爆币]
GM --> Fish[Fish<br/>概率产出]
GM --> Snake[贪吃蛇尾巴 lerp 跟随]
end
subgraph 数据与系统层
GM --> BM[BlockManager<br/>20×20 空间网格]
GM --> CM[CollideManager]
GM --> PM[PoolManager<br/>对象池上限500]
GM --> SM[ScoreManager<br/>实时排行]
GM --> EV[CustomEventListener<br/>事件总线]
end
subgraph 平台层
EV --> WX[分包/广告/分享/好友排行/登录]
EV --> SRV[轻量后端<br/>login / saveScore / getRank]
end
四、写在最后
这个项目验证了一个想法:没有联机服务器,用"伪匹配 + 性格化 AI"也能撑起 io 游戏的核心体验。代码里当然也留下了一些遗憾,比如数值全部硬编码没有配置表、BasePlayer.scoreChange() 里有一条永远执行不到的 else-if 分支(score>=10 先命中,后面的大体型缩放被挡住了)——这些都在代码里原样保留着,权当给读者的"找茬彩蛋”。
源码在 https://gitee.com/soulnew/fishPick,欢迎 Star,也欢迎提 Issue 交流。