java Flappy Bird小游戏二次开发

java Flappy Bird小游戏二次开发

引言

Flappy Bird 是一款广为人知的经典小游戏,以其简单的操作方式和高难度挑战吸引了全球数百万玩家。游戏的核心机制非常直接——玩家通过点击屏幕使小鸟飞翔,避免碰撞到上下移动的管道,同时尽可能地飞行得更远。这种看似简单的游戏设计隐藏了深层的挑战性和上瘾性,让人不禁一试再试。

尽管 Flappy Bird 的玩法简单,但其背后的代码实现和游戏设计思想却值得深究。对于游戏开发初学者和爱好者来说,复现 Flappy Bird 不仅是一种技术上的练习,也是理解游戏设计精髓的一种方式。本文将探讨如何在 Java 中实现 Flappy Bird 的基本功能,并进一步对游戏进行改进,增加新的特性来提升游戏的趣味性和挑战性。

原始代码概览

点击查看代码

package org.wf.game.flappybird;

import java.awt.Color;

import java.awt.Font;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.event.KeyAdapter;

import java.awt.event.KeyEvent;

import java.awt.event.MouseAdapter;

import java.awt.event.MouseEvent;

import java.awt.image.BufferedImage;

import java.io.File;

import java.io.IOException;

import java.util.Random;

import javax.imageio.ImageIO;

import javax.swing.JFrame;

import javax.swing.JPanel;

public class FalappyBirdGame {

public static void main(String[] args) {

// 定义画框

JFrame jf = new JFrame("bird_game");

jf.setSize(432, 674);

jf.setAlwaysOnTop(false);

jf.setLocationRelativeTo(null);

jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

jf.setResizable(false);

Sky sky = new Sky();

jf.add(sky);

// 显示画框

jf.setVisible(true);

sky.action();

}

}

// 天空类

class Sky extends JPanel {

private static final long serialVersionUID = 1L;

BufferedImage bgBufferImage; // 背景图片

Ground ground = new Ground(); // 地面

Column column = new Column(350); // 钢管

Column column2 = new Column(600); // 钢管

static Bird bird = new Bird(); // 小鸟

int score = 0; // 游戏得分

BufferedImage startBufferImage; // 开始准备界面

boolean isStrat; // 是否开始游戏

BufferedImage overBufferImage; // 游戏结束界面

boolean isOver; // 游戏是否结束

public Sky() {

super();

// 读取图片

File bgImage = new File("images/bg.png");

File starImage = new File("images/start.png");

File overImage = new File("images/gameover.png");

try {

bgBufferImage = ImageIO.read(bgImage);

startBufferImage = ImageIO.read(starImage);

overBufferImage = ImageIO.read(overImage);

} catch (IOException e) {

e.printStackTrace();

}

}

// 绘制界面方法

@Override

public void paint(Graphics graphics) {

// 画背景

graphics.drawImage(bgBufferImage, 0, 0, null);

// 获取新的画笔对象

Graphics2D gg = (Graphics2D) graphics;

gg.rotate(-bird.ratation, bird.bird_x, bird.bird_y);

// 画小鸟

graphics.drawImage(bird.biBufferImage, bird.bird_x - bird.bird_width

/ 2, bird.bird_y - bird.bird_height / 2, null);

gg.rotate(bird.ratation, bird.bird_x, bird.bird_y);

// 画钢管

graphics.drawImage(column.coBufferImage, column.column_x - column.width

/ 2, column.column_y - column.height / 2, null);

graphics.drawImage(column2.coBufferImage, column2.column_x

- column2.width / 2, column2.column_y - column2.height / 2,

null);

// 画地面

graphics.drawImage(ground.grBufferImage, ground.ground_x,

ground.ground_y, null);

// 画文字

graphics.setColor(Color.BLUE);

graphics.setFont(new Font("楷体", Font.ITALIC, 30));

graphics.drawString("分数:" + score, 100, 600);

// 画开始准备图片

if (!isStrat && !isOver) {

graphics.drawImage(startBufferImage, 0, 0, null);

}

// 画结束界面

if (isOver) {

graphics.drawImage(overBufferImage, 0, 0, null);

}

}

// 游戏启动逻辑

public void action() {

// 添加鼠标监听器

MouseAdapter adapter = new MouseAdapter() {

@Override

public void mousePressed(MouseEvent e) {

// System.out.println("点击了鼠标");

/*

* 若游戏结束重新开始游戏,游戏恢复初始状态

* 若未结束:鸟飞起来

*/

if (isOver) {

bird = new Bird();

ground = new Ground();

column = new Column(350);

column2 = new Column(600);

score = 0;

isOver = false;

isStrat = false;

} else {

bird.refly();

isStrat = true;

}

}

};

this.addMouseListener(adapter);

// 添加键盘监听器

KeyAdapter keyAdapter = new KeyAdapter() {

@Override

public void keyPressed(KeyEvent e) {

char charA = e.getKeyChar();

if (charA == 'w') {

if (bird.bird_y > 20) {

bird.bird_y -= 20;

}

} else if (charA == 's') {

if (bird.bird_y < 465) {

bird.bird_y += 20;

}

} else if (charA == 'a') {

if (bird.bird_x > 20) {

bird.bird_x -= 20;

}

} else if (charA == 'd') {

if (bird.bird_x < 395) {

bird.bird_x += 20;

}

} else if (charA == ' ') {

/*

* 若游戏结束重新开始游戏,游戏恢复初始状态

* 若未结束:鸟飞起来

*/

if (isOver) {

bird = new Bird();

ground = new Ground();

column = new Column(350);

column2 = new Column(600);

score = 0;

isOver = false;

isStrat = false;

} else {

bird.refly();

isStrat = true;

}

}

super.keyPressed(e);

}

};

this.addKeyListener(keyAdapter);

this.requestFocus();

while (true) {

// 判断游戏是否开始

if (isStrat && !isOver) {

ground.move();

column.move();

column2.move();

bird.change();

bird.move_go();

}

// 判断撞击障碍

if (bird.bird_x - bird.bird_width / 2 == column.column_x

+ column.width / 2

|| bird.bird_x - bird.bird_width / 2 == column2.column_x

+ column2.width / 2) {

score++;

}

if (bird.hit(ground) || bird.hit(column) || bird.hit(column2)) {

isStrat = false;

isOver = true;

}

try {

Thread.sleep(20);

} catch (InterruptedException e) {

e.printStackTrace();

}

repaint();

}

}

}

// 地面类

class Ground {

int ground_x, ground_y; // 地面的坐标

BufferedImage grBufferImage; // 地面图片

public Ground() {

super();

File grImage = new File("images/ground.png");

try {

grBufferImage = ImageIO.read(grImage);

} catch (IOException e) {

e.printStackTrace();

}

ground_y = 500;

}

// 地面动画方法

public void move() {

ground_x--;

if (ground_x < -110) {

ground_x = 0;

}

}

}

// 钢管类

class Column {

int column_x, column_y; // 钢管的中心坐标

int width, height; // 宽度高度

int gap = 140; // 钢管的空隙

Random random = new Random();

; // 随机坐标

BufferedImage coBufferImage; // 钢管图片

public Column(int x) {

super();

File coImage = new File("images/column.png");

try {

coBufferImage = ImageIO.read(coImage);

} catch (IOException e) {

e.printStackTrace();

}

column_x = x;

column_y = random.nextInt(180) + 150;

width = coBufferImage.getWidth();

height = coBufferImage.getHeight();

}

// 钢管动画方法

public void move() {

column_x--;

if (column_x < -width / 2) {

column_y = random.nextInt(180) + 150;

column_x = 432 + width / 2;

}

}

}

// 鸟类

class Bird {

int bird_x = 60, bird_y = 300; // 鸟的中心点坐标

int bird_width, bird_height; // 鸟的宽度,高度

double speed = 20; // 速度

double g = 4; // 加速度

double s; // 运动距离

double t = 0.3; // 运动时间

BufferedImage biBufferImage; // 鸟图片

BufferedImage[] images = new BufferedImage[8];

int bird_icon = 0;

public Bird() {

super();

for (int i = 0; i < images.length; i++) {

File biImage = new File("images/" + i + ".png");

try {

images[i] = ImageIO.read(biImage);

} catch (IOException e) {

e.printStackTrace();

}

}

biBufferImage = images[0];

bird_width = biBufferImage.getWidth();

bird_height = biBufferImage.getHeight();

}

// 小鸟展翅动画方法

int index = 0;

public void change() {

index++;

biBufferImage = images[index / 3 % 8];

}

// 小鸟移动的方法

double ratation; // 倾斜角度

public void move_go() {

double v0 = speed;

s = v0 * t - 0.5 * g * t * t;

double vt = v0 - g * t;

speed = vt;

bird_y = bird_y - (int) s;

ratation = s / 16;

if (bird_y <= bird_height / 2) {

bird_y = bird_height / 2;

}

}

// 重新飞翔

public void refly() {

speed = 20;

}

// 撞击地面

public boolean hit(Ground ground) {

return bird_y + bird_height / 2 >= ground.ground_y;

}

// 撞击钢管

public boolean hit(Column column) {

int left_x = column.column_x - column.width / 2 - bird_width / 2;

int right_x = column.column_x + column.width / 2 + bird_width / 2;

int top_y = column.column_y - column.gap / 2 + bird_height / 2 - 5;

int down_y = column.column_y + column.gap / 2 - bird_height / 2 + 5;

if (bird_x > left_x && bird_x < right_x) {

if (bird_y > top_y && bird_y < down_y) {

return false;

} else {

return true;

}

} else {

return false;

}

}

}

主要组成部分

Flappy Bird 游戏主要由以下几个核心组件构成:

游戏窗口(JFrame):作为游戏的主体框架,负责展示游戏内容并处理基本的窗口事件。

游戏画面(Sky 类):继承自 JPanel,负责游戏背景、小鸟、钢管和地面的绘制,以及游戏状态的管理。

小鸟(Bird 类):游戏的主角,通过玩家的点击或按键操作控制其上升和下落,模拟飞行行为。

钢管(Column 类):作为游戏中的障碍物,以固定间隔出现,玩家需要操控小鸟穿越钢管之间的空隙。

地面(Ground 类):在游戏画面的底部滚动,形成小鸟飞行的视觉参考。

关键功能

游戏循环:通过 Sky 类中的 action 方法实现,负责游戏状态的更新、绘制和重绘,以及游戏逻辑的循环执行。

事件监听:游戏监听鼠标和键盘事件,允许玩家通过点击或按键来控制小鸟的飞行。

碰撞检测:游戏实时检测小鸟与钢管或地面的碰撞,一旦发生碰撞,游戏结束。

得分机制:玩家每成功穿越一对钢管,得分增加。分数显示在游戏窗口的一角。

原作者对于游戏的基本功能已经实现,因此我将给游戏添加更多功能作为目标进行程序的改进

改进目标

我的改进目标主要集中在两个方面:

随着分数的提高,逐渐增加游戏速度,以提高游戏的挑战性。

让钢管的空隙大小随机变化,增加游戏的不可预测性,让玩家每次玩游戏时都有不同的体验。

加快游戏速度

首先,在 Sky 类中定义一个表示游戏速度的变量,此变量将用于控制地面、钢管以及小鸟的移动速度。

class Sky extends JPanel {

// 其他代码保持不变

private double speedMultiplier = 1.0; // 游戏速度倍数,初始为 1.0

// 其他方法保持不变

}

并且我们需要添加一个方法来根据当前分数调整 speedMultiplier。

public void updateGameSpeed() {

// 根据分数调整游戏速度,例如每增加10分,速度提升10%

speedMultiplier = 1.0 + score / 10 * 0.1;

}

接下来,我们需要修改 Ground 和 Column 类的 move 方法,使它们的移动速度能够根据 Sky 类中的 speedMultiplier 变量进行调整。

// Ground 类

public void move(double speedMultiplier) {

ground_x -= speedMultiplier;

if (ground_x < -110) {

ground_x = 0;

}

}

// Column 类

public void move(double speedMultiplier) {

column_x -= speedMultiplier;

if (column_x < -width / 2) {

column_y = random.nextInt(180) + 150;

column_x = 432 + width / 2;

}

}

然后,在 Sky 类中,当你调用这些 move 方法时,传递 speedMultiplier:

// Sky 类中的 action 方法或类似的更新逻辑部分

ground.move(speedMultiplier);

column.move(speedMultiplier);

column2.move(speedMultiplier);

这样,随着玩家分数的增加,游戏的难度也会逐渐提升,使游戏更具挑战性和趣味性。

随机的间隙大小

在 move 方法中更新 gap 的值,以便每次钢管重新出现时都有一个新的随机空隙:

public void move() {

column_x--;

if (column_x < -width / 2) {

column_y = random.nextInt(180) + 150;

column_x = 432 + width / 2;

// 每次钢管重新出现时更新空隙大小

this.gap = random.nextInt(51) + 100; // 例如,空隙大小介于 100 到 150 之间

}

}

结语

在本次探索和改进 Flappy Bird 游戏的旅程中,我们深入了解了游戏的基础代码结构,实现了两项关键的改进:随着玩家分数的增加逐步加快游戏速度,以及让钢管的空隙大小随机变化。这些改进不仅增加了游戏的挑战性,也为玩家带来了新鲜感和更多的乐趣。

通过这次经验,我们看到了即使对于简单的游戏,通过细微的调整也能大大增强游戏体验。这不仅考验了我们对游戏机制的理解,也锻炼了我们的编程技能,特别是在处理游戏逻辑和用户交互方面。

相关推荐

【攻略】關於基格爾德•細胞 @Pokemon GO 哈啦板
365bet亚洲官方网址

【攻略】關於基格爾德•細胞 @Pokemon GO 哈啦板

2025-09-30 👁️ 8966
方舟生存进化渡渡鸟怎么驯服 渡渡鸟饲料怎么做
bat365app手机版

方舟生存进化渡渡鸟怎么驯服 渡渡鸟饲料怎么做

2025-12-29 👁️ 8717
鹡鸰写意花鸟画法步骤图示解析_手机网易网
梦幻西游怎么回到帮派?快速解决方法分享!
bat365app手机版

梦幻西游怎么回到帮派?快速解决方法分享!

2025-08-09 👁️ 1645
处女膜破裂后的疼痛通常持续多久
365bet亚洲官方网址

处女膜破裂后的疼痛通常持续多久

2025-08-29 👁️ 7000
合肥五大小商品批发市场 合肥较大的小商品批发市场在哪 合肥小商品批发市场有哪些地方