首页 > 编程语言 >java Flappy Bird小游戏二次开发

java Flappy Bird小游戏二次开发

时间:2024-03-02 22:45:26浏览次数:30  
标签:java 游戏 column width 小游戏 二次开发 new bird ground

引言

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 方法实现,负责游戏状态的更新、绘制和重绘,以及游戏逻辑的循环执行。

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

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

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

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

改进目标

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

  1. 随着分数的提高,逐渐增加游戏速度,以提高游戏的挑战性。
  2. 让钢管的空隙大小随机变化,增加游戏的不可预测性,让玩家每次玩游戏时都有不同的体验。

加快游戏速度

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

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

标签:java,游戏,column,width,小游戏,二次开发,new,bird,ground
From: https://www.cnblogs.com/toner1ko/p/18049399

相关文章

  • Java学习笔记——第三天
    流程控制程序中最经典的三种执行顺序顺序结构:自上而下地执行代码,最普通的执行顺序。分支结构:根据条件,选择对应代码执行。循环结构:控制某段代码重复执行。分支结构if分支if分支的作用根据条件(真或假)来决定执不执行某段代码或执行哪段代码。if分支应用场景需要根据不同......
  • C语言贪吃蛇小游戏
    #include<stdio.h>#include<graphics.h>//里面有easyX图形库函数#include<conio.h>#include<time.h>#include<mmsystem.h>//#pragmacomment(lib,"winmm.lib")/******************************结构体*****************************......
  • 为什么要使用Java SPI机制
    JavaSPI(ServiceProviderInterface)最早是在JavaSE6中被引入的,作为一种标准的、用于在运行时发现和加载服务提供者插件的标准机制。以前的程序猿实现JDBC连接数据库都会自己写工具类加载不同厂商的驱动来实现数据库操作,但是随着JDBC4.0之后采用了JavaSPI机制,这部分工作就变的......
  • Java流程控制
    Scanner对象Java提供了一个工具类,可以获取用户的输入。java.util.Scanner是Java5的新特征。基本语法:Scannerscanner=newScanner(System.in);使用后关闭:scanner.close();通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前一般需要使用hasNext()与hasNex......
  • Java基础
    Java基础1、JVMvsJREvsJDK思路:可以从他们之间的关系回答,从小到大进行介绍它们之间的关系和不同,比如jvm<jre<jdk答案:JVM是运行Java字节码的虚拟机。JVM会根据不同的系统进行特定的设计(Windows、Linux、MacOS),目的是在不同的操作系统上使用相同的字节码可以得到相......
  • Java消息服务JMS
    一、JMS规范JMS是Java消息服务(JavaMessageService)应用程序接口的简写,是一个Java平台中关于面向消息中间件(MOM)的API,它为标准消息协议和消息服务提供了一组通用接口,包括创建、发送、读取消息等。用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一......
  • Java Pattern和Matcher处理正则表达式的用法
    转自:https://zhuanlan.zhihu.com/p/626497347 结论:Pattern与Matcher一起合作.Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持.单独用Pattern只能使用Pattern.matches(Stringregex,CharSequenceinput)一种最基础最简单的匹配。java正则表达式通过ja......
  • Java高频面试题
    Redis一、Redis的使用场景①缓存 ②分布式锁 ③限流 ④购物车 ⑤Token存储 ⑥点赞关注 ⑦短信验证码存储 ⑧分布式Session ⑨发布订阅 ⑩排行榜1、缓存 热点数据(经常查询,但不修改和删除)首选redis,性能高。 2、分布式锁注:锁,即在多线程环境下,对共享资......
  • 软件开发与创新第一周实验——关于“打怪小游戏”的试玩体验与修改
    一.“打怪小游戏”代码来源http://blog.csdn.net/zjx120307/article/details/126221342?spm=1001.2014.3001.5506二.运行环境及运行结果1.运行环境:Windows11操作系统,C++运行环境2.运行截图:(1)功能6演示(2)功能1演示(3)功能2演示(4)功能3演示(5)功能4演示原代码:点击查看代......
  • C写的贪吃蛇小游戏
    用了三四个小时跟视频写的小游戏,还是优点漏洞,明明加了音乐但是没声音,代码也没报错不知道啥原因;哪位大佬可以帮忙看看环境:win10,vs2017专业版用到的库://里面有easyX图形库函数#include#include//里面有easyX图形库函数#include#include#include//#pragmaco......