下面我将为你提供一个使用 Java Swing 库编写的、可运行的 Fly Bird 游戏的完整代码,Swing 是 Java 的标准 GUI 工具包,非常适合制作这种简单的2D游戏。

游戏核心机制
- 小鸟:受重力影响会不断下落,点击鼠标或按空格键,小鸟会获得一个向上的速度。
- 管道:从屏幕右侧以恒定速度向左移动,管道中间有固定的间隙,玩家需要控制小鸟穿过这个间隙。
- 碰撞检测:如果小鸟撞到了管道、地面或天花板,游戏结束。
- 计分:每成功穿过一组管道,分数加一。
完整 Java 代码
你可以将以下所有代码复制到一个名为 FlyBird.java 的文件中,然后直接编译并运行。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Random;
/**
* Fly Bird Game (类似 Flappy Bird)
* 使用 Java Swing 实现
*/
public class FlyBird extends JPanel implements ActionListener, KeyListener {
// 游戏窗口尺寸
private final int WIDTH = 400;
private final int HEIGHT = 600;
// 游戏状态
private boolean isGameOver = false;
private int score = 0;
// 小鸟属性
private int birdX = WIDTH / 4;
private int birdY = HEIGHT / 2;
private int birdRadius = 10;
private int birdVelocity = 0;
private final int gravity = 1;
private final int jumpStrength = -10;
// 管道属性
private final int pipeWidth = 60;
private final int pipeGap = 150; // 管道间隙
private final int pipeSpeed = 3;
private final int pipeSpacing = 200; // 管道之间的水平间距
private ArrayList<Pipe> pipes;
private Random random;
// 计时器
private Timer gameLoop;
public FlyBird() {
// 设置窗口
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setBackground(Color.CYAN);
setFocusable(true);
addKeyListener(this);
// 初始化游戏元素
pipes = new ArrayList<>();
random = new Random();
// 创建初始管道
for (int i = 0; i < 3; i++) {
addPipe(WIDTH + i * pipeSpacing);
}
// 启动游戏循环
gameLoop = new Timer(20, this); // 每20毫秒 (50 FPS) 更新一次游戏状态
gameLoop.start();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// 如果游戏结束,显示游戏结束画面
if (isGameOver) {
g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.BOLD, 40));
g.drawString("Game Over", WIDTH / 2 - 100, HEIGHT / 2);
g.setFont(new Font("Arial", Font.PLAIN, 20));
g.drawString("Score: " + score, WIDTH / 2 - 40, HEIGHT / 2 + 40);
g.drawString("Press SPACE to Restart", WIDTH / 2 - 100, HEIGHT / 2 + 80);
} else {
// 绘制小鸟
g.setColor(Color.YELLOW);
g.fillOval(birdX - birdRadius, birdY - birdRadius, birdRadius * 2, birdRadius * 2);
// 绘制管道
for (Pipe pipe : pipes) {
// 上管道
g.setColor(Color.GREEN);
g.fillRect(pipe.x, 0, pipeWidth, pipe.topHeight);
// 下管道
g.fillRect(pipe.x, pipe.topHeight + pipeGap, pipeWidth, HEIGHT - pipe.topHeight - pipeGap);
}
// 绘制分数
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 30));
g.drawString("Score: " + score, 10, 40);
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (!isGameOver) {
// 1. 更新小鸟位置
birdVelocity += gravity;
birdY += birdVelocity;
// 2. 更新管道位置
for (int i = 0; i < pipes.size(); i++) {
Pipe pipe = pipes.get(i);
pipe.x -= pipeSpeed;
// 检查是否成功通过管道
if (!pipe.passed && pipe.x + pipeWidth < birdX) {
pipe.passed = true;
score++;
}
// 移除屏幕外的管道并添加新的
if (pipe.x + pipeWidth < 0) {
pipes.remove(i);
addPipe(pipes.get(pipes.size() - 1).x + pipeSpacing);
i--; // 因为列表大小改变了,所以需要调整索引
}
}
// 3. 碰撞检测
checkCollisions();
}
// 重绘画面
repaint();
}
private void addPipe(int x) {
int topHeight = random.nextInt(HEIGHT - pipeGap - 100) + 50; // 随机生成管道高度
pipes.add(new Pipe(x, topHeight));
}
private void checkCollisions() {
// 检查是否撞到地面或天花板
if (birdY + birdRadius > HEIGHT || birdY - birdRadius < 0) {
isGameOver = true;
}
// 检查是否撞到管道
for (Pipe pipe : pipes) {
// 检查小鸟的x坐标是否在管道的x范围内
if (birdX + birdRadius > pipe.x && birdX - birdRadius < pipe.x + pipeWidth) {
// 检查小鸟的y坐标是否在管道间隙之外
if (birdY - birdRadius < pipe.topHeight || birdY + birdRadius > pipe.topHeight + pipeGap) {
isGameOver = true;
}
}
}
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
if (isGameOver) {
// 重新开始游戏
resetGame();
} else {
// 小鸟跳跃
birdVelocity = jumpStrength;
}
}
}
private void resetGame() {
isGameOver = false;
score = 0;
birdY = HEIGHT / 2;
birdVelocity = 0;
pipes.clear();
for (int i = 0; i < 3; i++) {
addPipe(WIDTH + i * pipeSpacing);
}
}
// 以下方法为接口实现,此处未使用
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {}
public static void main(String[] args) {
// 在事件调度线程中创建和显示GUI
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Fly Bird");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(new FlyBird());
frame.pack();
frame.setLocationRelativeTo(null); // 居中显示
frame.setVisible(true);
});
}
// 管道内部类
private class Pipe {
int x;
int topHeight;
boolean passed = false;
public Pipe(int x, int topHeight) {
this.x = x;
this.topHeight = topHeight;
}
}
}
如何运行
- 保存文件:将上面的代码保存为
FlyBird.java。 - 打开终端/命令行:导航到你保存文件的目录。
- 编译代码:运行
javac FlyBird.java。 - 运行游戏:运行
java FlyBird。
游戏窗口将会弹出,你可以点击鼠标或按空格键来控制小鸟飞行。
代码结构解析
FlyBird类:继承自JPanel,是游戏的主要画布和逻辑中心。main方法:程序的入口,它创建了一个JFrame(窗口),并将FlyBird实例添加到窗口中,使用SwingUtilities.invokeLater确保GUI在正确的线程上创建。- 构造函数
FlyBird():- 设置窗口大小和背景色。
- 初始化小鸟、管道和随机数生成器。
- 创建并启动
Timer。Timer是Swing中实现动画和游戏循环的核心,它会定期触发actionPerformed事件。
paintComponent(Graphics g)方法:- 这是绘制所有游戏元素的地方。
- 如果游戏结束,它会显示 "Game Over" 和分数,并提供重新开始的提示。
- 如果游戏正在进行,它会绘制小鸟(黄色圆形)、管道(绿色矩形)和当前分数。
actionPerformed(ActionEvent e)方法:- 这是游戏的主循环,由
Timer每隔20毫秒调用一次。 - 更新状态:更新小鸟的位置(应用重力)和所有管道的位置。
- 计分:检查小鸟是否成功通过了一组管道。
- 管道管理:移除已经离开屏幕的管道,并在右侧生成新的管道,保持屏幕上始终有固定数量的管道。
- 碰撞检测:调用
checkCollisions方法。 - 重绘:调用
repaint()请求系统重新调用paintComponent,更新画面。
- 这是游戏的主循环,由
checkCollisions()方法:- 检查小鸟是否与地面、天花板或任何管道发生碰撞,如果碰撞,则设置
isGameOver = true。
- 检查小鸟是否与地面、天花板或任何管道发生碰撞,如果碰撞,则设置
keyPressed(KeyEvent e)方法:- 处理键盘输入,当按下空格键时,如果游戏结束,则调用
resetGame;否则,给小鸟一个向上的速度(模拟跳跃)。
- 处理键盘输入,当按下空格键时,如果游戏结束,则调用
resetGame()方法:重置所有游戏状态(分数、小鸟位置、管道等),以便可以重新开始游戏。
Pipe内部类:一个简单的数据类,用于存储单个管道的x坐标、顶部管道的高度以及是否已被小鸟通过(用于计分)。
(图片来源网络,侵删)
如何扩展和改进
这个版本是一个基础框架,你可以在此基础上添加更多功能,
- 开始界面:在游戏开始前显示一个 "Start Game" 的按钮或提示。
- 音效:使用
javax.sound库添加跳跃、得分和游戏结束的音效。 - 难度递增:随着分数增加,可以增加管道的移动速度或减小管道间隙。
- 背景图片/动画:用图片替换纯色背景,并添加云朵等动画元素。
- 更复杂的小鸟动画:根据小鸟的速度和方向绘制不同的姿态。
- 最高分记录:使用文件存储和读取最高分。

