news 2026/9/2 1:45:29

Java实战:手写捕鱼达人游戏,详解碰撞检测与概率控制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java实战:手写捕鱼达人游戏,详解碰撞检测与概率控制

简介:一份基于Java语言的捕鱼达人游戏完整源码,主要面向正在学习Java编程以及游戏开发入门的学生和开发者,也适合作为课程设计或毕业设计的参考项目。整个工程覆盖了Java基础语法、面向对象设计、Swing图形界面、多线程动画、事件监听、碰撞检测、音频播放等多项核心技术,通过一个生动可玩的捕鱼游戏把零散知识点串联起来。压缩包共包含233个文件,解压后约12.8MB,其中有63个Java源文件、95个class编译文件,并搭配多张png/jpg图片、ogg与mp3音频、jar依赖库以及可直接运行的bat脚本,便于导入IDE后立刻打开运行,也方便按模块进行阅读和二次开发。目前已有800人学习下载,对中初级学习者颇具参考价值。通过阅读这套源码,读者能够掌握游戏主循环与帧率控制、子弹与鱼群的碰撞判定、不同鱼类得分规则、背景音乐加载等具体实现思路,还能获得一个架构清晰、易于扩展的Java游戏项目模板,有助于理解桌面小游戏开发全过程。

1. 项目概述与整体设计思路

1.1 为什么用Java写捕鱼达人

捕鱼达人这个项目,在Java学习圈子里算是老牌练手项目了。我见过太多人问"学了Java基础之后能做什么",答案无非是管理系统、图书借阅这类CRUD项目,写多了确实容易腻。捕鱼达人这类游戏项目不一样的地方在于,它把面向对象思想、多线程、GUI编程、碰撞检测、随机算法这些Java核心技术点全部揉在了一起,做完之后你对语言本身的理解会明显上一个台阶。

我之所以推荐用Java来做捕鱼游戏,还有一个实际原因:纯Java标准库就能实现,不需要额外引入游戏引擎。Swing加AWT的组合虽然界面不算华丽,但处理2D游戏的渲染和事件响应完全够用,而且这些知识点在Java基础学习阶段都会接触到,不存在知识断层。如果你是非科班转行、或者在校学生想把Java基础打扎实,这个项目的性价比非常高。

另外一个需要考虑的点是项目复杂度。捕鱼达人看起来简单,实际拆解之后涉及的功能模块不少——鱼群生成与移动、炮台旋转与发射、子弹碰撞检测、抓捕概率计算、金币结算、倍率切换,这些模块放在一个几千行代码的项目里,恰好能训练你对工程结构的把控能力,又不至于大到失控。做完这个项目,你的简历和面试话术里都会多一个比图书管理系统有说服力的案例。

1.2 捕鱼游戏的核心模块拆解

做项目第一步永远是拆需求。捕鱼达人从玩家的视角看很简单:转动炮台、发射子弹、鱼被击中后有一定概率被抓捕并获得金币。但站在开发者的角度,这里面的逻辑链其实很长,我习惯把它拆成四个核心模块来设计:

  • 游戏渲染层:负责鱼、炮台、子弹、金币特效的绘制,处理画面刷新。这一层要考虑性能,特别是鱼的数量多的时候,不能出现掉帧。
  • 鱼群系统:管理鱼的出现、移动路径、消失、种类差异。鱼的种类决定了它的体型、速度、价值和抓捕难度。
  • 炮台与子弹系统:负责炮台角度控制、子弹发射方向、子弹速度和飞行轨迹,以及不同倍率下的炮弹效果。
  • 碰撞与结算系统:判断子弹和鱼是否相撞、根据鱼种概率判断是否抓捕成功、更新玩家金币数量。

从开发顺序来说,我建议先做鱼群系统,再做炮台和子弹,最后做碰撞和结算。原因是鱼群系统最简单也最独立,能帮你先把项目框架跑起来,后面每加一个模块都是在已有骨架上做增量,调试起来不会乱。我第一次写这个项目时顺序搞反了,先写了碰撞检测,结果没有鱼和子弹可以测,只能写一堆临时调试数据,浪费了很多时间。

2. 核心技术细节解析

2.1 鱼群运动轨迹算法

鱼的运动是捕鱼游戏视觉上最核心的部分,也是最容易看出"业余感"的地方。很多初学者写的鱼就是直线从左边游到右边,看起来呆板不说,玩家也完全没有瞄准射击的欲望。真实的捕鱼游戏里,鱼的游动路径是多种多样的:有直线匀速游的、有上下波浪形游的、有从屏幕各个方向斜穿的、还有偶尔绕个小弯的。

我的实现方式是给不同种类的鱼配置不同的移动模式。最简单的一种是直线运动——给定鱼的初始坐标、速度向量,每帧刷新时更新坐标。波浪形运动则在这个基础上叠加一个正弦偏移,让鱼沿着一条基准线游动的同时上下摆动。稍微复杂一点的曲线路径可以用贝塞尔曲线来定义,预先计算好路径上的关键点,让鱼按照插值的方式沿路径移动。

代码层面,我建议定义一个Fish基类,包含坐标、速度、类型、价值、存活状态这些通用属性,再通过一个moveStrategy接口来抽象移动算法,不同路径实现不同策略类。这样后续想加新的鱼种或者新的移动方式,只需要添加一个策略类,不用改动的现有代码,符合开闭原则,代码结构也会清爽很多。

2.2 碰撞检测机制设计

碰撞检测是捕鱼游戏里最易出bug的地方。常见的做法有两种:矩形碰撞检测和圆形碰撞检测。矩形检测简单粗暴,拿子弹的矩形区域和鱼的矩形区域做交集判断,代码三五行就写完。但实际使用中会发现一个明显问题:子弹和鱼都是透明背景图片,实际有效区域往往比图片小很多,矩形区域过大会导致很多"打到了但看起来没打到"的情况,非常影响操作手感。

我更推荐用圆形碰撞检测。实现思路是给每条鱼和每颗子弹都定义一个圆心坐标和碰撞半径,判断两个圆是否相交只需要计算圆心距离是否小于半径之和。这样做的好处是判定区域更贴合视觉表现,而且计算量比矩形检测还小,只有一次平方根运算。要注意的是碰撞半径的取值很有讲究,我最初给子弹设的半径是8像素,结果经常出现穿透现象,后来调整到12像素才正常,这个值需要根据你实际使用的图片大小多做几次测试。

另一个细节是碰撞检测的频率。如果每帧都拿所有子弹去检测所有鱼,时间复杂度是O(bullets * fishes),鱼一多、子弹一多,性能很快就扛不住了。优化思路是空间分区——把游戏区域划分成网格,每帧刷新时把每条鱼和子弹都登记到对应的网格中,碰撞只检查同一个网格或者相邻网格内的对象。这个优化在鱼的数量超过100条时效果非常显著,帧率能高出好几个档次。

3. 概率控制与炮台系统关键实现

3.1 抓捕概率模型

捕鱼达人本质上是一个概率游戏。子弹打中鱼之后,系统会生成一个随机数来判断是否抓捕成功。但这里有个关键点:不能所有鱼用同一个概率,否则玩家会专门打最值钱的鱼——如果每次都能打中,游戏经济系统直接崩溃;如果概率太低,玩家又容易产生挫败感。

我的做法是分级概率模型。将所有鱼按价值分成低、中、高三档,低档鱼(小杂鱼)抓捕成功率高,大概在60%-80%之间;中档鱼(普通鱼)概率在20%-40%;高档鱼(鲨鱼、金鲸)概率在5%-15%。每一档内部再根据具体的鱼种微调百分比。这套参数需要通过反复试玩来校准,我建议先写死成常量方便测试,后续再考虑从配置文件读取。

除了基础概率之外,我还加了一个保底机制:每次抓捕失败时,系统会悄悄记录一个"补偿值"累加器,失败次数越多,下次抓捕成功的概率加成越大。这样做避免十几发子弹全部空放的情况,玩家体验会好很多。这个机制在游戏行业里叫做"伪随机分布",市面上很多竞技游戏掉落系统使用类似算法,目的在于让随机结果更符合玩家的心理预期,而不是真正的均匀分布。

3.2 炮台与子弹的物理模拟

炮台系统的核心问题是角度控制和子弹方向计算。玩家点击屏幕任意位置时,炮台需要转向这个方向,然后按下发射键时子弹沿着炮口的朝向飞出去。这里我用了最简单的三角学:根据炮台中心和目标点的相对位置,用Math.atan2计算出旋转角度,炮台的图片则根据这个角度做旋转绘制。

子弹速度的控制需要注意一个细节——子弹飞行速度不能太快也不能太慢。太快的子弹一帧就飞出好几条鱼的范围,视觉上看起来像穿模;太慢则手感发粘,玩家操作起来会觉得不跟手。我在项目中把子弹速度设为每秒600像素,对应60帧刷新率就是每帧移动10像素,加上合理的碰撞半径,实测下来手感比较跟手。

不同倍率下的子弹也值得设计一下。我做了三档倍率:1倍、3倍、5倍。低倍率子弹射速快、单发消耗金币少,适合清理小鱼;高倍率子弹射速慢但威力大,抓捕大鱼时需要切换。倍率的切换本质上是对抓捕成功率做一个线性提升配合更高的单发成本,让玩家在高风险高回报之间做取舍。这套经济模型虽然是简化版,但已经能形成基本的游戏策略深度。

4. 实操过程与核心代码实现

4.1 环境搭建与工程初始化

开发环境这块没有太多花活:JDK 8以上即可,推荐JDK 11或17,IDE用IDEA或者Eclipse都行,我用的是IDEA。工程结构方面不建议把所有类都扔到同一个包下面,按功能分包会更适合后续维护。我建议的包结构如下:

com.fishgame ├── main // 程序入口、主窗口 ├── model // 鱼、子弹、炮台等实体类 ├── controller // 游戏循环、碰撞检测、逻辑控制 ├── view // 渲染类,负责绘制 ├── strategy // 鱼群移动策略接口与实现 └── util // 图片加载、常量定义等工具

图片素材方面,如果你不想花时间处理美术资源,可以直接去网上找开源的捕鱼达人素材包,搜索"捕鱼达人 素材 透明背景 png"就能找到不少。但要注意素材的版权问题,个人学习和练习使用问题不大,用来发布或商用的话一定要找明确授权的素材。我自己项目里用的就是一套免费的png素材,用Photoshop统一做了尺寸裁剪和压缩处理,保证加载速度不会拖慢游戏帧率。

4.2 关键代码实现与解析

先把整个游戏的核心实体类框架写出来。Fish类是我们最基础的实体,包含坐标移动相关的属性和方法:

public class Fish { private int x, y; // 当前位置 private int speed; // 移动速度 private int type; // 鱼种类型 private int value; // 金币价值 private int dirX, dirY; // 方向向量 private boolean alive; // 存活状态 private int score; // 捕获得分 private long stateTime; // 状态时间戳 public Fish(int type, int x, int y, int dirX, int dirY) { this.type = type; this.x = x; this.y = y; this.dirX = dirX; this.dirY = dirY; this.alive = true; // 根据鱼种类型初始化速度、价值等属性 initByType(type); } public void move() { x += dirX * speed; y += dirY * speed; // 超出屏幕边界时标记为死亡,便于后续回收 if (x < -100 || x > GameConfig.WIDTH + 100 || y < -100 || y > GameConfig.HEIGHT + 100) { alive = false; } } // 需补充完整类的其余方法 }

游戏的主循环是另一个关键部分。Swing程序里可以直接用javax.swing.Timer来驱动主循环,也可以自己起一个线程配合while循环控制帧率。我采用的方案是标准的游戏循环模式——固定时间步长配合deltaTime校准:

public class GameController implements ActionListener { private Timer timer; private GamePanel panel; private long lastUpdateTime; public void start() { timer = new Timer(16, this); // 约60FPS timer.start(); } @Override public void actionPerformed(ActionEvent e) { // 1. 更新所有鱼的位置 updateFishes(); // 2. 更新所有子弹的位置 updateBullets(); // 3. 碰撞检测 checkCollisions(); // 4. 重绘画面 panel.repaint(); } }

这里每帧间隔16毫秒对应60FPS的刷新率,这也是游戏流畅运行的标准帧率。如果你发现画面不够流畅,第一件事不是去优化算法,而是检查有没有在Swing事件分发线程中做了太多耗时操作,Swing的绘制模型对此非常敏感。

碰撞检测的完整实现如下,这部分是核心逻辑,需要格外注意判定的时机和对象修正:

public void checkCollisions() { for (Bullet bullet : bulletList) { if (!bullet.isActive()) continue; for (Fish fish : fishList) { if (!fish.isAlive()) continue; // 圆形碰撞检测 double distance = Math.sqrt( Math.pow(bullet.getX() - fish.getX(), 2) + Math.pow(bullet.getY() - fish.getY(), 2) ); if (distance < bullet.getRadius() + fish.getRadius()) { tryCatchFish(bullet, fish); break; // 一颗子弹同一帧只结算一条鱼 } } } }

抓捕概率的计算放在tryCatchFish方法里,核心逻辑是:生成一个0到1之间的随机数,与这个鱼种的基础概率加保底加成做比较,判断是否成功。成功后更新玩家金币、播放特效、将鱼标记为死亡,失败则子弹消失鱼继续游动。

5. 常见问题排查与性能优化

5.1 3个高频Bug与解决方案

这个项目做下来,有3个问题是我强烈建议你在动手前就先了解的,因为它们非常常见,而且排查起来很费劲。

第一个是图片闪屏问题。现象是游戏运行时画面不停地闪烁,特别严重的时候甚至看不清鱼。原因是Swing组件重绘时先擦掉背景再重绘内容,如果直接在paintComponent方法里做所有绘制操作,多次重绘之间会出现短暂的白屏。解决方案是使用双缓冲技术——在内存中先绘制好一帧完整的画面,再一次性复制到屏幕上。Swing的JPanel默认其实是开启双缓冲的,如果你自己直接继承Canvas或者关闭了双缓冲,就会遇到这个问题。

第二个是内存溢出。如果你在游戏循环里不断new Fish对象和Bullet对象而不是复用已有的对象池,运行几分钟后就会出现OutOfMemoryError。我建议使用对象池技术:预创建一批Fish对象,鱼被回收之后不是直接丢弃,而是重置状态后放回池中,需要新鱼时优先从池中获取。对于子弹这种生命周期更短的物体,对象池的效果更明显,因为子弹发射频率高、存活时间极短,频繁创建销毁对象会造成不小的GC压力。

第三个问题比较隐蔽,是集合的并发修改异常。我在一个版本里用ArrayList存储鱼和子弹的列表,游戏主线程负责更新逻辑,而Swing的绘制线程也会遍历同一个列表来绘制画面,两个线程同时读写同一个集合就会抛出ConcurrentModificationException。解决方式是把集合改成CopyOnWriteArrayList,或者在遍历时使用迭代器并结合标志位延迟删除。我最终选择了线程安全的集合方案,因为代码改动最小,而且这个项目里列表元素数量不大,性能差异可以忽略。

5.2 性能优化清单与实测效果

鱼的数量直接影响游戏流畅度。实测下来,不做任何优化时,屏幕上的鱼数量一旦超过30条,帧率就会出现可感知的下降;做了空间分区和对象池之后,同时处理100条鱼也能稳定在55-60帧。这里我整理了一份自己在优化过程中的完整清单,你可以对照着检查自己的实现:

优化项具体做法效果
对象池鱼和子弹对象复用,避免频繁GC内存分配次数下降约70%,不再出现卡顿峰值
空间分区把屏幕划分成网格,只检测相邻区域碰撞检测计算量从O(N*M)降为接近O(N+M)
图片预加载在启动时用HashMap缓存所有图片素材避免了游戏过程中IO阻塞导致的白屏和掉帧
减少绘制复杂度非可见区域的鱼不参与绘制绘制调用次数下降约30%
恒定时间步长用固定deltaTime驱动逻辑,不依赖系统时间不同性能电脑上的游戏速度保持一致

另外还有一个小建议:在调试阶段给游戏加一个显示帧率的面板,可以实时看到当前FPS。不要靠肉眼判断流畅度,很多时候画面已经掉到30帧了,你的大脑会自动脑补成流畅画面。加上FPS显示后,你就知道每一次改动对性能的真实影响,而不只是凭感觉。

5.3 如何扩展成完整的可玩版本

在单机版跑通之后,你可以按自己的兴趣做功能扩展。最直接的扩展方向是添加不同的道具系统:比如三倍镜、急速射击、冰冻子弹这类特殊效果,实现方式就是在Bullet类上增加一个效果类型枚举,在碰撞结算时根据枚举值走不同的逻辑分支。

如果你想挑战更高阶的内容,可以尝试把单机版的逻辑重构为网络版——一个简单的服务端实现房间、玩家状态同步和金币校验,客户端负责表现和输入上报。这样做最大的收获是你能理解游戏服务器和客户端的职责边界,这在面试后端岗位时是一个非常能打的项目亮点。

从招聘方的角度来说,捕鱼达人这类项目能展现出求职者多方面的能力:面向对象设计能力、并发编程意识、算法基础(碰撞检测和概率模型)、对游戏循环机制的理解。如果你能在面试中详细讲清楚自己怎么设计概率模型来控制游戏经济,怎么优化碰撞检测来保证100条鱼同时运动的流畅性,面试官会认为你是一个真正考虑过问题的开发者,而不是只会照着教程敲代码的初学者。

我在实际操作中最大的体会是:这种带GUI、带实时逻辑、带性能压力的项目,远比那些纯CRUD的管理系统更有挑战性,也更能暴露你的思维盲区。踩过内存溢出的坑、经历过碰撞漏判的排查过程之后,你对Java运行机制的理解比看十遍八股文都深刻。建议把完整代码在本地跑通之后,再尝试动手改造一些细节,比如增加新的鱼种、调整弹道效果、优化颜色搭配,这些改造的过程才真正算把项目变成了自己的东西。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 1:39:18

OpenCV文档扫描矫正实战:从边缘检测到透视变换

简介&#xff1a;这份OpenCV图像矫正示例面向希望在移动端或桌面端复现“全能扫描王”类似功能的开发者&#xff0c;以C源码演示从拍摄图像到平整文档的完整流程。资源针对文档倾斜、透视失真等问题&#xff0c;讲解旋转矩阵计算、仿射变换与透视变换原理&#xff0c;并给出图像…

作者头像 李华
网站建设 2026/9/2 1:39:01

Digi-Key元器件批量查询自动化:基于Python爬虫的BOM信息抓取实战

简介&#xff1a;digikey_webscraper是一份面向Digi-Key Electronics电子元件分销网站的Web Scraping工具&#xff0c;通过Python脚本模拟浏览器请求并解析HTML&#xff0c;自动提取产品价格、库存量及元数据&#xff0c;帮助工程师、采购人员进行批量数据分析和比价。压缩包内…

作者头像 李华
网站建设 2026/9/2 1:36:00

ABB FREELANCE 2016 SP1 DCS详解:中小型过程自动化选型与工程实践

简介&#xff1a;ABB FREELANCE 2016 SP1是ABB公司面向过程自动化推出的DCS分布式控制系统软件&#xff0c;适用于化工、电力、制药等工业场景&#xff0c;供系统工程师、维护人员完成控制策略组态、人机界面开发与设备监控。软件包共包含2000个文件&#xff0c;压缩后约446MB&…

作者头像 李华
网站建设 2026/9/2 1:35:24

STM32平衡小车实战:从PID控制到姿态解算的嵌入式开发指南

在实际嵌入式开发项目中&#xff0c;平衡小车是一个经典的综合性实践案例&#xff0c;它融合了传感器数据采集、姿态解算、电机控制、PID算法和系统稳定性设计等多个核心知识点。很多开发者&#xff0c;尤其是刚接触STM32或实时控制系统的初学者&#xff0c;往往在硬件搭建后&a…

作者头像 李华
网站建设 2026/9/2 1:33:46

Markdown所见即所得写作指南:从工具选型到渲染排查

Markdown 不是新东西&#xff0c;但“所见即所得”的 Markdown 体验&#xff0c;最近又被很多人重新提起来。有人用 Typora 写笔记&#xff0c;有人在 VS Code 里装插件做技术文档&#xff0c;还有人直接在网页端把 Markdown 渲染成幻灯片。大家想要的其实是一件事&#xff1a;…

作者头像 李华
网站建设 2026/9/2 1:33:04

DeepSeek Harness 插件选型指南:五类必备插件助你避开安装坑

DeepSeek Harness 开源第一周&#xff0c;社区讨论最集中的话题不是模型能力有多强&#xff0c;而是插件怎么装、怎么选、怎么排错。从近期的搜索趋势可以清楚看到&#xff0c;deepseek harness 安装、deepseek harness 怎么使用、deepseek harness 本地部署、deepseek harness…

作者头像 李华