尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Java版飞翔的小鸟源码解析:Swing游戏循环与碰撞检测实战
简介这是一份面向Java初学者与游戏开发爱好者的「飞翔的小鸟」完整源码工程适合想通过经典小游戏理解面向对象设计与游戏循环机制的开发者练手。压缩包共32个文件约139KB包含5个java源文件、6个class编译文件以及18个png图片素材另有classpath、project、prefs等Eclipse工程配置导入后即可直接运行调试。资源围绕鸟的运动模型、柱子与星星的生成移动、碰撞检测、动画绘制及计分系统展开其中分数与难度动态联动低于15分正常速度过柱加1分15至30分提速且加2分超过30分再次提速并加3分吃到星星额外奖励2分可帮助读者理解速度调节与奖励机制的代码落地方式。目前已有2952人学习下载适合作为课程设计、Java GUI练习或游戏编程入门的参考案例。1. 从一份 Java 版飞翔的小鸟源码说起它到底能跑出什么如果你手头正好有一份 Java 版飞翔的小鸟完整代码第一反应大概率是「这玩意儿能直接跑吗」。我拿到这份源码时也是这个心态——一个用 Java 写的横版飞行躲避游戏玩家控制一只小鸟在管道间隙中穿行撞到管道或落地就结束分数随通过管道数递增。听起来简单但它麻雀虽小五脏俱全游戏主循环、碰撞检测、图像渲染、音效播放、分数存档一个都没少。适合谁刚学完 Java 基础语法、想找一个能跑起来又有成就感的练手项目的人或者想拿一个现成的 2D 游戏框架改玩法、换素材的开发者。它不依赖复杂引擎纯 Java SE 加 Swing/AWT 就能跑代码量适中逻辑清晰拿来当第一个「完整项目」再合适不过。2. 项目结构与技术选型为什么用 Swing 而不是 JavaFX2.1 目录结构长什么样拿到源码包解压后通常能看到这样的结构不同版本可能略有差异但核心不会变FlyingBird/ ├── src/ │ ├── com/ │ │ └── game/ │ │ ├── main/ │ │ │ └── GameMain.java // 程序入口main 方法 │ │ ├── core/ │ │ │ ├── GamePanel.java // 游戏主面板继承 JPanel │ │ │ ├── GameLoop.java // 游戏循环线程 │ │ │ └── GameState.java // 游戏状态枚举准备/运行/结束 │ │ ├── entity/ │ │ │ ├── Bird.java // 小鸟实体 │ │ │ ├── Pipe.java // 管道实体 │ │ │ └── Ground.java // 地面滚动 │ │ ├── util/ │ │ │ ├── ImageLoader.java // 图片资源加载 │ │ │ ├── SoundPlayer.java // 音效播放 │ │ │ └── ScoreManager.java // 分数读写 │ │ └── config/ │ │ └── GameConfig.java // 常量配置宽高、重力、速度 │ └── resources/ │ ├── images/ // 小鸟、管道、背景、地面图 │ ├── sounds/ // 跳跃、得分、撞击音效 │ └── score.dat // 最高分存档运行时生成 ├── lib/ // 第三方 jar如果有 └── README.md这个结构是典型的「按职责分包」core管流程entity管对象util管工具config管常量。新手容易把所有类堆在一个包下但这份源码做了分层后续改起来不会牵一发动全身。2.2 技术选型Swing AWT 多线程为什么是 Swing 而不是 JavaFX原因很实际Swing 是 JDK 自带的不需要额外引入模块或依赖javac和java命令直接就能编译运行。JavaFX 从 JDK 11 开始被移出标准库要单独配 SDK 和模块路径对新手不友好。这个项目用到的核心类就几个JFrame顶层窗口JPanel游戏画布重写paintComponent做渲染Graphics2D画图、抗锯齿、旋转Timer或Thread驱动游戏循环ImageIO加载图片资源Clip播放音效常见做法是用一个独立线程跑游戏循环每帧repaint()触发重绘。也有版本用javax.swing.Timer每 16 毫秒触发一次actionPerformed本质一样但Timer的回调在 EDT事件调度线程上执行渲染更安全缺点是逻辑和渲染耦合在同一线程复杂游戏会卡。这个项目体量小两种都行。2.3 游戏主循环的实现方式游戏循环是这类项目的灵魂。我一般会这样写// GameLoop.java public class GameLoop implements Runnable { private final GamePanel panel; private volatile boolean running true; private static final int FPS 60; private static final long FRAME_TIME 1000 / FPS; // 每帧毫秒数 public GameLoop(GamePanel panel) { this.panel panel; } Override public void run() { long lastTime System.currentTimeMillis(); while (running) { long now System.currentTimeMillis(); long elapsed now - lastTime; if (elapsed FRAME_TIME) { panel.update(); // 更新逻辑小鸟位置、管道移动、碰撞检测 panel.repaint(); // 触发重绘 lastTime now; } else { try { Thread.sleep(FRAME_TIME - elapsed); // 让出 CPU控制帧率 } catch (InterruptedException e) { Thread.currentThread().interrupt(); running false; } } } } public void stop() { running false; } }这段代码的关键点FRAME_TIME决定帧率60 FPS 对应约 16 毫秒。update()和repaint()分离逻辑更新和画面渲染各司其职。volatile保证running变量在多线程间的可见性避免停止指令被缓存导致线程关不掉。Thread.sleep在帧间隔未到时让出 CPU不然循环会空转吃满一个核。参数怎么改想跑更快就把FPS调大但注意update()里的位移量是按帧算的帧率变了速度也会变。正确做法是把速度改成「每秒多少像素」乘以elapsed秒数这样帧率无关。很多新手改帧率后发现小鸟飞太快就是踩了这个坑。3. 核心模块拆解小鸟、管道与碰撞检测3.1 小鸟实体重力、跳跃与旋转小鸟的运动逻辑是整个游戏的手感来源。核心就两个变量垂直速度velocity和重力加速度gravity。// Bird.java public class Bird { private double x, y; // 坐标 private double velocity; // 垂直速度像素/帧 private static final double GRAVITY 0.5; // 重力加速度 private static final double JUMP_FORCE -8.0; // 跳跃初速度向上为负 private double rotation 0; // 旋转角度用于渲染 public void update() { velocity GRAVITY; // 每帧速度增加 y velocity; // 位置随速度变化 // 根据速度调整旋转角度上升时抬头下降时低头 rotation Math.toDegrees(Math.atan2(velocity, 10)); if (rotation -30) rotation -30; if (rotation 90) rotation 90; } public void jump() { velocity JUMP_FORCE; // 点击时重置速度 } public Rectangle getBounds() { return new Rectangle((int) x, (int) y, 34, 24); // 碰撞箱 } }逻辑说明GRAVITY每帧给velocity累加y再累加velocity形成加速下落。jump()把velocity直接设为负值小鸟瞬间向上然后重力慢慢把它拉回来。旋转角度用atan2根据速度方向算上升时角度为负抬头下降时为正低头并做了上下限裁剪避免翻转过头。参数调整GRAVITY越大下落越快JUMP_FORCE绝对值越大跳得越高。这两个值直接决定难度调的时候要一起动不然手感会怪。碰撞箱Rectangle的宽高通常比图片实际尺寸小一圈因为小鸟图片边缘有透明像素用整张图做碰撞会显得「明明没碰到却死了」这是血泪经验。3.2 管道生成与移动管道成对出现上下各一根中间留空隙。生成逻辑是定时在屏幕右侧外新增一对然后每帧向左移动移出屏幕左侧后回收。// Pipe.java public class Pipe { private int x; private int gapY; // 空隙上边缘的 y 坐标 private static final int GAP_HEIGHT 120; // 空隙高度 private static final int PIPE_WIDTH 52; private static final int SPEED 3; // 每帧左移像素 public void update() { x - SPEED; } public boolean isOffScreen() { return x PIPE_WIDTH 0; } public Rectangle getTopBounds() { return new Rectangle(x, 0, PIPE_WIDTH, gapY); } public Rectangle getBottomBounds() { return new Rectangle(x, gapY GAP_HEIGHT, PIPE_WIDTH, GameConfig.HEIGHT - gapY - GAP_HEIGHT); } }gapY是随机生成的范围要保证空隙不贴顶也不贴地。常见做法是gapY 50 random.nextInt(300)具体范围看屏幕高度。SPEED决定管道移动快慢和帧率绑定的话同样有帧率问题严谨点应该用时间差。管道的碰撞箱分上下两个Rectangle分别对应上管和下管。检测时只要小鸟的碰撞箱和任意一个相交就判定撞击。3.3 碰撞检测与分数判定碰撞检测用Rectangle.intersects简单直接。分数判定则是看小鸟的 x 坐标是否越过了管道的右边缘且这根管道还没被计过分。// GamePanel.java 中的 update 片段 for (Pipe pipe : pipes) { pipe.update(); // 碰撞检测 if (bird.getBounds().intersects(pipe.getTopBounds()) || bird.getBounds().intersects(pipe.getBottomBounds())) { gameState GameState.GAME_OVER; SoundPlayer.play(hit); } // 得分判定小鸟中心越过管道右边缘且该管道未计分 if (!pipe.isScored() bird.getX() pipe.getX() Pipe.PIPE_WIDTH) { pipe.setScored(true); score; SoundPlayer.play(score); } } // 边界检测落地或飞出顶部 if (bird.getY() 24 GameConfig.HEIGHT - Ground.HEIGHT || bird.getY() 0) { gameState GameState.GAME_OVER; }这里有个细节得分判定用的是小鸟的 x 坐标和管道右边缘比较而不是碰撞箱。因为碰撞箱可能因为旋转或缩放有偏差用逻辑坐标更稳。isScored标记防止同一根管道重复计分这是新手常漏的——不加标记的话小鸟停在管道边缘会疯狂加分。4. 避坑与常见问题跑不起来先看这几条4.1 图片加载报 NullPointerException现象运行后窗口一片黑控制台抛NullPointerException定位到ImageLoader或paintComponent里的drawImage。原因图片路径写的是相对路径但工作目录和源码目录不一致。比如代码里写new File(resources/images/bird.png)而 IDE 运行时的工作目录是项目根目录不是src目录。解决用类加载器从 classpath 读getClass().getResourceAsStream(/images/bird.png)注意开头的斜杠表示从 classpath 根开始。同时确认resources目录被标记为资源根IDEA 里右键 Mark as Resources Root编译后图片会被复制到target/classes下。4.2 窗口闪烁严重现象小鸟和管道移动时画面抖动、闪烁。原因直接在paintComponent里画没有双缓冲。Swing 的JPanel默认是开启双缓冲的但如果你重写了paint而不是paintComponent或者手动调用了getGraphics()双缓冲就失效了。解决只重写paintComponent(Graphics g)在里面用super.paintComponent(g)清屏然后所有绘制都通过这个g完成。不要自己getGraphics()也不要在paint里画。如果还闪检查是不是在循环里调了revalidate()或setVisible()。4.3 音效播放一次后不再响现象第一次跳跃有声音后面就没了。原因Clip播放完后需要调用setFramePosition(0)回到开头否则下次start()没反应。另外Clip对象如果被垃圾回收声音也会断。解决把Clip缓存到Map里播放前先stop()再setFramePosition(0)再start()。或者用SourceDataLine自己写播放逻辑但那样代码量大小游戏用Clip够了。4.4 最高分存档丢失现象每次重启游戏最高分都归零。原因存档文件写在了相对路径或者写入流没关闭数据还在缓冲区。解决用绝对路径或用户目录比如System.getProperty(user.home) /flyingbird_score.dat。写入时用try-with-resources确保流关闭try (DataOutputStream dos new DataOutputStream( new FileOutputStream(scoreFile))) { dos.writeInt(highScore); } catch (IOException e) { e.printStackTrace(); }4.5 游戏结束后无法重新开始现象撞管道后画面卡住点击没反应。原因游戏循环线程还在跑但gameState变成GAME_OVER后update()里没有处理重置逻辑或者重置时没有把管道列表、小鸟位置、速度归位。解决在GAME_OVER状态下监听点击事件调用reset()方法清空pipes列表、小鸟坐标回到初始位置、velocity归零、score归零、gameState改回READY或RUNNING。注意reset()要在游戏循环线程外调用或者加锁避免和update()并发修改集合。5. 二次开发与改造思路从换皮到换玩法5.1 换素材最快看到效果的改造想把这个项目变成自己的最直接的就是换图片和音效。resources/images下的图片按原名替换即可注意尺寸比例。小鸟图建议 34x24 左右管道图宽度 52地面图高度固定。替换后如果碰撞感觉不对调Bird.getBounds()里的宽高和Pipe的PIPE_WIDTH。音效替换成.wav格式Clip对 mp3 支持不好常见做法是统一转 wav。5.2 改玩法加入道具和难度曲线想让它不那么单调可以加道具系统。比如随机生成护盾吃到后 5 秒内免疫一次碰撞。实现思路新增Item实体在管道生成时按概率附带update()里检测小鸟和道具的碰撞命中后给Bird加一个invincible标记和倒计时。碰撞检测里判断invincible为真时跳过撞击判定。难度曲线方面现在的SPEED是常量可以改成随分数递增speed BASE_SPEED score / 10但要有上限不然最后快到没法玩。GAP_HEIGHT也可以随分数缩小从 120 慢慢降到 90增加压迫感。5.3 改架构从 Swing 迁移到 LibGDX如果想让游戏跑在更多平台或者想要更好的性能和渲染效果可以考虑迁移到 LibGDX。核心改动是把GamePanel换成ApplicationAdapterpaintComponent换成render()Thread循环换成 LibGDX 内置的渲染循环。实体类基本不用动碰撞检测的Rectangle换成 LibGDX 的Rectangle即可。迁移后可以打包成桌面、Android、HTML5 多端运行。不过这是个大工程建议先把 Swing 版玩透再动。5.4 验证改造是否成功每次改完按这个清单过一遍窗口能正常打开、小鸟能跳、管道能移动、碰撞能触发、分数能增加、最高分能保存、游戏结束后能重开。任何一项挂了先看控制台报错再检查对应模块。我一般会在update()开头加一行System.out.println(State: gameState Score: score)跑一遍看状态流转对不对确认没问题再删掉。从那以后我每次拿到这类小游戏源码都强制先跑通默认版本再动一行代码不然改出问题都不知道是原来就有的还是自己引入的。希望这份拆解能帮你少走点弯路顺利把这只小鸟飞起来。本文还有配套的精品资源点击获取
RELATED

相关推荐

tiktok-5.6B-videos × Pandas/Polars/DuckDB:10招高效分析1TB+ Parquet大规模数据

tiktok-5.6B-videos × Pandas/Polars/DuckDB:10招高效分析1TB+ Parquet大规模数据

数据集大数据社交 【免费下载链接】tiktok-5.6B-videos 项目地址: https://ai.gitcode.com/hf_mirrors/datasocial/tiktok-5.6B-videos 点击查看 免费下载 tiktok-5.6B-videos 是一个包含 56 亿条 TikTok 视频记录的开源数据集,以 148 个月度 Parquet 文…

📅 2026/10/16 1:11:27
Kling AI新消息真实性与技术落地分析

Kling AI新消息真实性与技术落地分析

我无法根据当前输入生成符合要求的博文。原因如下:项目标题“可灵 Kling AI 发布新消息”本身是一个泛化、模糊的信息提示,缺乏具体技术指向、功能描述或事件锚点;项目正文为空,未提供任何实质性内容(如发布内容概要、…

📅 2026/10/16 1:11:27
NeuralScreen发布工程拆解:字节级可复现构建、runtime-manifest与200+回归测试

NeuralScreen发布工程拆解:字节级可复现构建、runtime-manifest与200+回归测试

【免费下载链接】NeuralScreen DLSS 5 NR on your whole Windows desktop, in real time with Boost, FG, etc. RTX 30/40/50, 12 languages, user presets, recording with audio, one-window mode. 项目地址: https://gitcode.com/gh_mirrors/dl/NeuralScreen 点击…

📅 2026/10/16 1:11:27
MORE NEWS

更多资讯

📰

Snowy 前端组件 XnTreeSelect:懒加载树选择器与编辑回显完整实战指南

后端前端认证鉴权应用安全 【免费下载链接】Snowy 💖国内首个国密前后分离快速开发平台💖《免费商用》,基于开源技术栈精心打造,融合Vue3AntDesignVue4Vite5SpringBoot3MpHuToolSa-Token。平台内置国密加解密功能,保障…

📰

媒体爬虫实战指南:五分钟跑通社交数据

媒体爬虫实战指南:五分钟跑通社交数据 【免费下载链接】MediaCrawler 小红书笔记 | 评论爬虫、抖音视频 | 评论爬虫、快手视频 | 评论爬虫、B 站视频 | 评论爬虫、微博帖子 | 评论爬虫、百度贴吧帖子 | 百度贴吧评论回复爬虫 | 知…

📰

Fontsource 字体实战:用 Flow Circular 为线框原型与早期设计稿注入逼真的占位内容

前端CLI开发工具 【免费下载链接】fontsource Self-host Open Source fonts in neatly bundled NPM packages. 项目地址: https://gitcode.com/gh_mirrors/fo/fontsource 点击查看 免费下载 导读 Flow Circular 是设计师 Dan Ross 创作的 Flow 字体家族的一员&…

📰

Faster R-CNN老代码调试指南:从环境配置到mAP评估全流程踩坑实录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

LoopBack 4 Model API Builder 扩展点:用 Model 类自动生成 Repository 与 Controller 的插件契约

后端微服务 【免费下载链接】loopback-next LoopBack makes it easy to build modern API applications that require complex integrations. 项目地址: https://gitcode.com/gh_mirrors/lo/loopback-next 点击查看 免费下载 loopback/model-api-builder 是 LoopBa…

📰

Snowy 中 XnPageSelect 分页下拉选择器:基于 Ant Design Vue Select 的懒加载搜索封装实战

后端前端认证鉴权应用安全 【免费下载链接】Snowy 💖国内首个国密前后分离快速开发平台💖《免费商用》,基于开源技术栈精心打造,融合Vue3AntDesignVue4Vite5SpringBoot3MpHuToolSa-Token。平台内置国密加解密功能,保障…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬