JAVA实现flappy bird游戏

图片素材

实现代码

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.io.IOException;

public class BackGroundView extends JPanel {
    BufferedImage bgImage; //背景图片
    BufferedImage groundImg;//地面图片
    BufferedImage imgStart;//开始游戏图片
    BufferedImage imgEnd;//GameOVer图片

    public int bg_width;//背景图片宽度
    public int bg_height;//背景图片高度
    public int ground_width;//地面图片宽度
    public int ground_height;//地面图片高度
    public int ground_x,ground_y;//地面绘制起始坐标
    public int speed = 0;//管道和地面移动的速度
    public int state = 0;//游戏状态,0表示未开始,1表示正在玩,2表示GameOver

    public static final int MOVE_SPEED1 = 50;// 地面及柱子移动初始速度,当积分累加,速度会递增
    public static final int jframeWidth = 432;//窗口宽度(bg.png宽度)
    public static final int jframeHeight = 644;//窗口高度(bg.png高度)

    public static final String PATH_PIC = "\\pictures\\";
    public static final String PATH_BG = PATH_PIC + "bg.png";//背景图片路径
    public static final String PATH_GROUND = PATH_PIC + "ground.png";
    public static final String PATH_IMGSTART = PATH_PIC + "start.png";
    public static final String PATH_IMGEND = PATH_PIC + "gameover.png";

    public static final int FONT_SIZE = 30;//得分字体大小
    public static final int SCORE_X = 20;
    public static final int SCORE_Y = 40;
    public int score;

    public JFrame jframeMain;
    public GameBoard gameBoard;
    public Bird bird;
    public Pipe pipe1,pipe2;

    public BackGroundView(){
        initFrame();//初始化窗口
    }

    public void initFrame(){
        jframeMain = new JFrame("Flappy Bird");
        jframeMain.setSize(jframeWidth, jframeHeight);
        jframeMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jframeMain.setLocationRelativeTo(null);//居中显示
        jframeMain.setResizable(false);//窗口Size固定

        gameBoard = new GameBoard();//初始化内部类GameBoard
        jframeMain.add(gameBoard);

        Thread moveAll = new Thread(gameBoard);
        moveAll.start();
    }

    //游戏面板
    class GameBoard extends JPanel implements Runnable{

        public GameBoard(){
            initGame();//初始化游戏
        }

        public void initGame(){
            //原则:先加载后绘制
            try{
                //1、加载背景图片
                bgImage = ImageIO.read(this.getClass().getResource(PATH_BG));//根据当前路径加载图片进内存
                //获取图片宽度与高度
                bg_width = bgImage.getWidth();
                bg_height = bgImage.getHeight();
                System.out.println("bg_width:" + bg_width + ",bg_height:" + bg_height);

                //2、加载地面图片,注意大的图片要先加载,否则会遮住之前加载的
                groundImg = ImageIO.read(this.getClass().getResource(PATH_GROUND));
                ground_width = groundImg.getWidth();
                ground_height = groundImg.getHeight();
                ground_x = 0;
                ground_y = bg_height - ground_height;
                System.out.println("ground_width:" + ground_width + ",ground_height:" + ground_height);
                System.out.println("ground_x:" + ground_x + ",ground_y:" + ground_y);

                //3、加载开始和结束图片
                imgStart = ImageIO.read(this.getClass().getResource(PATH_IMGSTART));
                imgEnd = ImageIO.read(this.getClass().getResource(PATH_IMGEND));

                //4、加载小鸟图片,并在绘制中绘制小鸟,在run中让小鸟飞动
                bird = new Bird();

                //5、加载管道图片,一个窗口中最多显示两个管道
                pipe1 = new Pipe(bg_width, bg_height, ground_height);
                pipe1.x = bg_width;//第一根管道位置
                pipe2 = new Pipe(bg_width, bg_height, ground_height);
                pipe2.x = bg_width + Pipe.PIPE_DISTANCE;//第二跟管道位置

            }catch (IOException e){ }

            //3、让地面移动
            speed = MOVE_SPEED1;//速度初始化
        }

        @Override
        public void run() {
            action();//处理键盘事件
            //移动
            while(true){
                try{
                    if (state == 0) {
                        groundMove();//地面移动,初始化窗口未开始游戏地面和小鸟就要动
                        bird.fly();//小鸟飞动
                    }else if (state == 1){
                        groundMove();//地面移动,初始化窗口未开始游戏地面和小鸟就要动
                        bird.fly();//小鸟飞动
                        bird.down();//仅游戏开始时下降
                        pipe1.move();//让两根管子动起来
                        pipe2.move();
                        //小鸟撞到地面,天空,柱子都GameOver
                        if (bird.hitPipe(pipe1) || bird.hitPipe(pipe2) || bird.hitGround(bg_height, ground_height) || bird.hitSky()){
                            state = 2;
                        }else {
                            if (bird.addScore(pipe1) || bird.addScore(pipe2)){
                                //每次通过一个得分加10分,速度也增加
                                score += 10;
                                speed += 2;
                            }
                        }
                    }else if (state == 2){

                    }
                    Thread.sleep(1000 / speed);//speed越大线程休眠时间越少,执行次数越多,速度就越快
                    this.repaint();//刷新,会自动重新调用paint()方法
                 }catch (InterruptedException e){ }
            }
        }

        public void groundMove(){
            ground_x--;//地面左移,可以实现小鸟右移
            if (ground_x == bg_width - ground_width + 9){//9为修正值,自己调的,保证移动更流畅
                ground_x = 0;
            }
        }

        public void action(){
            //设置监听事件
            this.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    super.mouseClicked(e);
                    switch(state){
                        case 0://游戏未开始点击,就切换为开始游戏
                            state = 1;
                            bird.x = Bird.BIRD_FLY_X;
                            bird.y = Bird.BIRD_FLY_Y;
                            break;
                        case 1://游戏开始
                            bird.up();//游戏中点击就是上升
                            break;
                        case 2:
                            //切换到未开始状态,得分清零,小鸟与管道位置重置
                            state = 0;
                            score = 0;
                            bird.x = Bird.BIRD_X;
                            bird.y = Bird.BIRD_Y;
                            pipe1.x = bg_width;
                            pipe2.x = bg_width + Pipe.PIPE_DISTANCE;
                            break;
                        default:
                            break;
                    }
                }
            });
        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);
            //System.out.println("paint方法被调用时间:" + getCurrentTime());
            g.drawImage(bgImage,0,0,null);//绘制背景

            if (state == 0){//游戏未开始
                g.drawImage(imgStart,0,0,null);
                g.drawImage(bird.img, bird.x, bird.y, null);
            }else if (state == 1){//游戏开始
                g.drawImage(bird.img, bird.x, bird.y, null);//点击开始后,初始坐标也同时变
                g.drawImage(pipe1.img, pipe1.x, pipe1.y, null);
                g.drawImage(pipe2.img, pipe2.x, pipe2.y, null);

            }else if (state == 2){//游戏结束
                g.drawImage(imgEnd,0,0,null);
            }
            g.drawImage(groundImg , ground_x, ground_y, null);//绘制地面

            //绘制分数
            Graphics2D gg = (Graphics2D) g;
            Font scoreFont = new Font("微软雅黑", Font.BOLD, FONT_SIZE);//得分字体
            //下面两句是抗锯齿模式,消除文字锯齿,字体更清晰顺滑
            gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
            gg.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
            gg.setFont(scoreFont);
            gg.setColor(Color.WHITE);
            gg.drawString("" + score, SCORE_X, SCORE_Y);
        }
        //当前时间
        public String getCurrentTime() {
            Date day = new Date();
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            return df.format(day);
        }

    }
    public void showView(){
        jframeMain.setVisible(true);
    }
}

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;

//小鸟类
public class Bird {
    public static final int BIRD_PIC_COUNT = 8;//小鸟图片个数,8张图片轮播形成飞行时的样子
    public static final int BIRD_X = 190;//初始化小鸟坐标(游戏未开始小鸟位置)
    public static final int BIRD_Y = 220;
    public static final int BIRD_FLY_X = 120;//开始游戏后小鸟初始坐标
    public static final int BIRD_FLY_Y = 240;
    public static final int BIRD_UP_SPEED = 6;
    public static int index = 0;//当前小鸟图片序号
    public int x,y;//小鸟坐标
    public int width;//小鸟宽度
    public int height;//小鸟高度

    public double g = 9.8;//重力加速度
    public double t = 0.05;//自然下降时间
    public double v,h;//下降速度与下降距离

    BufferedImage img;
    BufferedImage[] imgs = new BufferedImage[BIRD_PIC_COUNT];

    public Bird(){
            try{
                for (int i = 0; i < 8; i++) {
                    imgs[i] = ImageIO.read(this.getClass().getResource(BackGroundView.PATH_PIC + i + ".png"));
                }
                img = imgs[0];
                //获取小鸟宽高
                width = img.getWidth();
                height = img.getHeight();
                //初始化小鸟位置
                x = BIRD_X;
                y = BIRD_Y;
            }catch (IOException e){ }
    }

    // 小鸟飞翔的图片切换
    public void fly() {
        index++;
        // 小鸟图形切换的频率,index/x,x越大,翅膀切换频率越慢,index到48完成一次轮播
        img = imgs[index / 6 % BIRD_PIC_COUNT];//除以6是调整速度
        if (index == 6 * BIRD_PIC_COUNT) {
            index = 0;
        }
    }

    //上升
    public void up(){
        v = BIRD_UP_SPEED;//上升,鼠标点击小鸟上升20
    }

    //下降
    public void down() {
        v = v - g*t;// Vt=Vo-gt
        h = v - g*t*t/2;// h=Vot-gt²/2
        y = y - (int)h;
    }

    // 碰撞检测
    // 是否碰撞地面
    public boolean hitGround(int bg_height, int ground_height) {
        if (y + height >= (bg_height - ground_height)) {
            return true;
        }
        return false;
    }

    // 碰撞到舞台顶部
    public boolean hitSky() {
        if (y <= 0) {
            return true;
        }
        return false;
    }
    // 碰到柱子时的检测
    public boolean hitPipe(Pipe p) {
        // x方向小鸟和柱子碰撞的条件
        if ((x + width) >= p.x && x <= p.x + p.width) {
            if (y <= p.y + (p.height - Pipe.PIPE_GAP) / 2
                    || y >= p.y + (p.height + Pipe.PIPE_GAP) / 2 - height) {
                return true;
            }
        }
        return false;
    }

    // 增加积分,通过管道后调用该方法
    public boolean addScore(Pipe p) {
        if (x == p.x + p.width) {
            return true;
        }
        return false;
    }
}

public class Main {
    public static void main(String[] args) {
        new BackGroundView().showView();
    }
}
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

public class Pipe {
    public static int PIPE_GAP = 144;//中间可通过的缺口大小
    public static int PIPE_DISTANCE = 244;//管道之间的间距

    BufferedImage img;//管道图片
    public int x,y;//坐标
    public int width,height;//柱子宽高
    private int max, min;//保证管道完全能显示在屏幕上,所以要设置max与min
    Random random = new Random();//管道随机出现
    public int bg_width;

    public Pipe(int bg_width, int bg_height, int ground_height){
        this.bg_width = bg_width;
        try{
            img = ImageIO.read(this.getClass().getResource(BackGroundView.PATH_PIC + "pipe.png"));
            width = img.getWidth();
            height = img.getHeight();
            System.out.println("Pipe_width = " + width + ",Pipe_height = " + height);//width=74,height=1200
            x = bg_width;//管道在不在初始背景出现,在背景"右边"

            max = (height - PIPE_GAP) / 2;//图片有上下两管道,这个max表示一个管道的高度
            min = (height - PIPE_GAP) / 2 - (bg_height - PIPE_GAP - ground_height);//管道出现的最小长度
            y = -(min + random.nextInt(max - min));//管道随机出现的坐标

        }catch (IOException e){ }
    }

    //游戏开始,柱子就要向左移动
    public void move(){
        x--;
        //若柱子走出最左边窗口,管道就重新初始化
        if (x == -width){
            x = bg_width;
            y = -(min + random.nextInt(max - min));//管道随机出现的坐标
        }
    }

}

 游戏界面与实现效果

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值