Java实战之飞翔的小鸟小游戏

作者:qq_42236679 时间:2022-10-04 20:37:20 

前言

一个简单的单机小游戏:flypybird ,用来巩固java基础。

涉及主要知识点:JFrame 、 JPanel 、 继承、 键盘/鼠标监听 、 多线程 、 工具类设计

 提示:这是大致的实现过程,实际实现过程有一定的修改,具体以源码为准。

一、大体思路

1、首先要有一个框架,作为主程序入口,这里使用 JFrame  类。

2、然后需要有一个画布,用来把游戏场景画上去,然后在上面添加键盘/鼠标监听来控制,这里使用的是 JPenal 类。

3、需要创建几个类:小鸟、地面、障碍物柱子、一个获取图片的工具类

4、然后逐步添加到画布中,实现对应的功能

二、具体步骤

2.1 创建窗体类

相当于窗户的框架,有了框架才能装玻璃。然后也是主程序执行的入口

2.1.1 具体代码


public class MainFrame extends JFrame {

/* 图标 */
   BufferedImage Icon;

/*
    * 构造器用来初始化框架*/
   public MainFrame() throws IOException {
       /* 设置图标 */
       Icon = ImageUtil.getImage("bird1_1.png");
       setIconImage(Icon);
       /* 设置关闭 */
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       /* 设置标题 */
       setTitle("飞翔的小鸟");
       /* 设置尺寸*/
       setSize(298, 550);
       /* 设置大小不可变 */
       setResizable(false);
       /* 设置窗体居中 */
       setLocationRelativeTo(null);
   }

/*
    * 主程序
    * */
   public static void main(String[] args) throws IOException {

MainFrame mainFrame = new MainFrame();
       mainFrame.setVisible(true);

}
}

2.1.2 效果展示

Java实战之飞翔的小鸟小游戏

2.1.3 小结

大体框架做好,考虑到后面还需要使用比较多的图片,因此接下来先建一个工具类,用来获取图片资源。

三、创建一个获取图片的工具类

3.1 具体代码


/*
* 工具类,用来获取图片
* */
public class ImageUtil {
   public static BufferedImage getImage(String name) throws IOException {

return ImageIO.read(new BufferedInputStream(new FileInputStream("birdGame/flyBird/" + name)));
   }
}

3.2 小结

图片获取方式改为用工具类获取,只需要输入图片名+格式。后期方便减少重复代码的书写。

四、创建画布

使用 Jpanel 类,创建画布(相当于玻璃) ,就能在上面画游戏的画面了。后期还需要在上面添加鼠标/键盘监听。

4.1 具体代码


public class GameJPenal extends JPanel {
   /*
    * 各种参数应该设置在这
    * */
   BufferedImage bg;

/*
    * 构造方法用来完成数据的初始化
    * */
   public GameJPenal() throws IOException {
       bg = ImageUtil.getImage("bg_day.png");
   }

/*
    * 开始游戏的方法
    * */
   public void start() {
       gameStart = true;
       Thread myThread = new Thread(new MyThread());
       myThread.start();
   }

//绘制的方法
   @Override
   public void paint(Graphics g) {
       g.drawImage(bg, 0, 0, 288, 512, null);     //背景

}
}

4.2 效果展示

先在main方法中创建对象,把画布添加到框架里面,注意要重新在最后设置可见,否者看不到背景


/*
    * 主程序
    * */
   public static void main(String[] args) throws IOException {

MainFrame mainFrame = new MainFrame();

GameJPenal gameJPenal = new GameJPenal();

mainFrame.add(gameJPenal);  /* 画布添加到框架上 */

mainFrame.setVisible(true);

gameJPenal.requestFocus(); /* 请求屏幕焦点 ,否则无法实现键盘监听 */

}

接下来就可以运行,效果如下

Java实战之飞翔的小鸟小游戏

4.3 小结

这里需要注意一个点就是请求屏幕焦点:后期如果要做键盘监听的话必须有焦点,否则无法实现键盘控制

五、把地面画上去

5.1 代码


public class Ground {
   BufferedImage land;
   /*  x,y 是地面在画布上的坐标 */
   private int x;
   private int y;
   private final int SPEED = 4;  //控制地面移动的速度

public Ground() throws IOException {
       land = ImageUtil.getImage("land.png");
       x = 0;
       y = 512 - land.getHeight();
   }

/*
    * 地面移动的效果
    * */
   public void move() {
       if (x == (288 - land.getWidth())) {
           x = 0;
       }
       x-=SPEED;
   }

/*
   * get方法
   * */
   public int getX() {
       return x;
   }

public int getY() {
       return y;
   }
}

接下来就是把地面的图片用画笔画上去

 

g.drawImage(land.landImg, land.x, land.y, land.w, land.h, null);

六、创建一个新线程让画面动起来

先把变量添加到 GamePanel 类


Land land;    //地面
   Thread newThread;  //线程
   boolean gameStart; // 游戏状态变量,准备状态为 false ,游戏开始为 true
   boolean gameOver;  //状态变量, 游戏开始为false ,游戏结束为true

在GamePanel 构造器里面初始化变量,创建一个新线程,用死循环不断调用地面移动的方法,然后重画画面,实现移动的效果

这里还加了俩个游戏的状态变量,主要是为了方便实现游戏的准备画面和游戏结束后可以重新开始游戏:


boolean gameStart;  //游戏准备状态
boolean gameOver;   //游戏结束状态

/*
    * 新开一个线程
    * */
   class MyThread implements Runnable {
       @Override
       public void run() {
           while (true) {
               ground.move();
               c0.move();
               c1.move();
               bird.fly();
               bird.move();
               isGameOver();
               repaint();
               if (gameOver) {
                   return;
               }
               try {
                   Thread.sleep(20);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }
   }

小结:

这时候画面已经能动起来了,接下来就是把其它的对象创建出来添加进去:鸟 、 障碍物(柱子)

七、创建柱子类

 和创建地面类似,需要有他的坐标点和高度,同时为了画面柱子的连续性,需要俩组不同的柱子,


/*
* 柱子类
* */
public class Column {

private final int SPEED = 2;
   int w1;
   int h1;
   int x1;
   int y1;
   BufferedImage img1;

int w2;
   int h2;
   int x2;
   int y2;
   BufferedImage img2;

/*
    * 构造方法
    * 初始化对象*/
   public Column(int i) throws IOException {

img1 = ImageUtil.getImage("pipe_down.png");
       w1 = img1.getWidth() / 2;
       h1 = img1.getHeight();
       x1 = 288 + i * 150;
       y1 = -100 - i * 20;

img2 = ImageUtil.getImage("pipe_up.png");
       w2 = img2.getWidth() / 2;
       h2 = img2.getHeight();
       x2 = 288 + i * 150;
       y2 = h2 - i * 25 -20;
   }

/*柱子移动的方法
    * */
   public void move() {

if (x1 == -w1) {
           x1 = 288;
       }
       if (x2 == -w2) {
           x2 = 288;
       }
       x1 -= SPEED;
       x2 -= SPEED;
   }
}

八、实现柱子在画布上的移动

和地面的初始化一样,在画布上添加成员变量,进行初始化,接下来用画笔在画布上画出来,在线程里面的死循环调用它移动的方法,实现柱子的移动

小结:

接下来就是把小鸟添加到画布上来

九、创建小鸟类

小鸟的x轴位置其实是固定的,因此小鸟只需要能在y轴移动就行,这一部分可以通过鼠标活着键盘监听来控制

小鸟有一个扇翅膀的动作,通过把图片存在一个集合里面,通过循环,每次画一个图片,就能实现扇翅膀的效果

难点:小鸟的上下移动的方法的设计

这里通过设计一个类似抛物线的动作,每次按下键盘Up,就改变它的上抛的初速度,让小鸟往上飞,经过时间 t 开始做落体运动


public class Bird {
   private double v0;  /* 小鸟运动的初速度 */
   double t;  /* 往上运动的时间 */
   int s;   /* 往上运动的路程 */
   int x;
   int y;
   int g;   /* 重力 */
   BufferedImage img;
   ArrayList<BufferedImage> list;  /* 装三张不同动作的图片 */

public Bird() throws IOException {
       g = 3;
       v0 = 5;
       t = 0.3;
       img = ImageUtil.getImage("bird1_1.png");
       x = 100;
       y = 200;
       list = new ArrayList<>();
       list.add(ImageUtil.getImage("bird1_0.png"));
       list.add(ImageUtil.getImage("bird1_1.png"));
       list.add(ImageUtil.getImage("bird1_2.png"));
   }

/*
    * 鸟飞的动作,通过改变每次画不同动作的图片来实现
    * */
   int i = 0;
   public void fly() {
       if (i >= 3) {
           i = 0;
       }
       img = list.get(i);
       i++;
   }
   /*
    * 鸟的上抛移动
    * */
   public void moveUP() {
       v0 = 10;
   }
   /*
    * 鸟的落体运动*/
   public void move() {
       s= (int) (v0*t);
       y -= s;
       double v2 = v0 - g * t;
       v0 = v2;
   }
}

十、小鸟添加到画布

和柱子一样,不做赘述

效果如下:

Java实战之飞翔的小鸟小游戏

十一、实现键盘监听

注意:实现加键盘监听需要画布获得焦点

方法1:main 方法中设置


gameJPenal.requestFocus();

方法2:直接在GamePanel 类设置


setFocusable(true);

主要代码:


this.addKeyListener(new KeyAdapter() {
           @Override
           public void keyPressed(KeyEvent e) {
               if (gameOver) {
                   if (e.getKeyCode() == KeyEvent.VK_UP) {
                       gameOver = false;
                       gameStart = false;
                       try {
                           ground = new Ground();
                           c0 = new Column(0);
                           c1 = new Column(1);
                           bird = new Bird();

} catch (IOException ioException) {
                           ioException.printStackTrace();
                       }
                   }

} else if (gameStart) {
                   if (e.getKeyCode() == KeyEvent.VK_UP) {
                       bird.moveUP();
                   }
               } else {
                   start();
               }
           }
       });

十二、判断游戏结束的方法

判断游戏结束其实比较简单,通过检测小鸟的x, y 坐标以及小鸟图片的宽度与柱子的x , y 坐标加上柱子的宽度做一个碰撞检测


/*
    * 判定游戏是否结束的方法
    * */
   public void isGameOver() {
       //先判断是否落地
       if (bird.y <= 0 || bird.y >= 512 - ground.land.getHeight() - bird.img.getHeight()) {
           gameOver = true;
       }
       //判断是否撞柱子
       int bh = bird.img.getHeight();
       int bw = bird.img.getWidth();
       //c0.img1, c0.x1, c0.y1, c0.w1, c0.h1, null
       if (c0.x1 <= bird.x + bw && c0.x1 + c0.w1 >= bird.x && c0.y1 + c0.h1 >= bird.y) {
           gameOver = true;
       }
       if (c1.x1 <= bird.x + bw && c1.x1 + c1.w1 >= bird.x && c1.y1 + c1.h1 >= bird.y) {
           gameOver = true;
       }
       if (c0.x2 <= bird.x + bw && c0.x2 + c0.w2 >= bird.x && c0.y2 <= bird.y + bh) {
           gameOver = true;
       }
       if (c1.x2 <= bird.x + bw && c1.x2 + c1.w2 >= bird.x && c1.y2 <= bird.y + bh) {
           gameOver = true;
       }
   }

if (gameStart == false) {
           g.drawImage(imageStart, 50, 200, null);
       }
       if (gameOver == true) {
           g.drawImage(imageGameOver, 50, 200, null);

十三、游戏结束后回到准备状态

这里其实就是状态重置,写在在键盘监听事件里面,逻辑就是游戏结束,只要按了Up键,游戏就重置为准备状态。同时,在游戏 paint 方法中加一个判断,不同的状态对应不同的图片


if (gameOver) {
                   if (e.getKeyCode() == KeyEvent.VK_UP) {
                       gameOver = false;
                       gameStart = false;
                       try {
                           ground = new Ground();
                           c0 = new Column(0);
                           c1 = new Column(1);
                           bird = new Bird();
                           score = 0;
                       } catch (IOException ioException) {
                           ioException.printStackTrace();
                       }
                   }
                   repaint();

十四、统计分数

分数统计原理:小鸟的x坐标和柱子的( x+w)相等,分数就+1


if (bird.x == c0.x1 + c0.w1 || bird.x == c1.x1 + c1.w1) {
                   score++;
               }

来源:https://blog.csdn.net/qq_42236679/article/details/116085773

标签:java,飞翔的小鸟,小游戏
0
投稿

猜你喜欢

  • java easyUI实现自定义网格视图实例代码

    2022-05-16 23:52:54
  • 深入了解Java接口回调机制

    2023-11-09 15:52:05
  • Java超详细分析@Autowired原理

    2023-11-25 05:37:44
  • 深入理解ThreadLocal工作原理及使用示例

    2022-02-27 19:24:14
  • java处理数据库不支持的emoji表情符问题解决

    2021-08-21 00:16:58
  • JVM(Java虚拟机)简介(动力节点Java学院整理)

    2023-11-25 06:12:12
  • springcloud配置ssh的问题及解决方法

    2023-11-03 15:48:11
  • Java 客户端操作 FastDFS 实现文件上传下载替换删除功能

    2022-06-01 15:01:38
  • 微信开发--自定义菜单查询返码乱码的解决方法

    2023-11-25 04:47:55
  • springboot 中整合mybatis多数据源不使用JPA

    2023-03-01 08:43:02
  • 如何利用Jackson序列化忽略指定类型的属性详解

    2023-11-16 09:01:27
  • springboot集成mybatisPlus+多数据源的实现示例

    2023-11-24 22:35:02
  • Java对象类型的判断详解

    2023-07-26 09:55:07
  • Spring spel表达式使用方法示例

    2023-08-25 00:43:32
  • RocketMQ源码解析broker 启动流程

    2022-12-25 10:50:54
  • C++对string进行大小写转换操作方法

    2023-11-03 04:32:59
  • Android 应用签名的两种方法

    2023-07-29 14:02:04
  • SpringBoot如何整合redis实现过期key监听事件

    2023-08-04 18:51:19
  • 详解ThreadLocal为什么会内存溢出原理

    2023-11-09 18:45:26
  • Android Camera+SurfaceView自动聚焦防止变形拉伸

    2023-06-18 06:35:54
  • asp之家 软件编程 m.aspxhome.com