JAVA專案:Java實現飛揚的小鳥(Flappy Bird)
飛揚的小鳥(Flappy Bird)
需求描述
遊戲載入完畢點選介面即可開始遊戲。
滑鼠點選控制小鳥飛行,或者通過鍵盤控制小鳥的前後上下也可以,不要撞到管道哦!
控制好小鳥越過障礙飛得更遠,獲得更高的積分。
使用的技術點
- 變數
- 分支語句
- 迴圈語句
- 物件導向
- 異常處理
- Random隨機數
- StringBuffer字串操作
- IO操作
- 多執行緒
- swing元件
- 。。。。
需求分析
程式碼實現
1、實現介面背景
step1:首先新建一個class表示背景類BackGround。我們要在該類中,載入背景圖片。
建立一個包pics,裡面先存放背景圖:bg.png。
先定義一個常量類Constant,專門用於儲存程式中的常量。
程式碼實現:
package com.ruby.demo;
/**
* 常量類
* @author ruby
*
*/
public class Constant {
// 圖片路徑
public static String PATH_PIC = "/pics/";
// 背景圖片路徑
public static String PATH_BACKGROUND = PATH_PIC + "bg.png";
}
然後建立BackGround類:
程式碼實現:
package com.ruby.demo;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* step1:背景類:單例模式
*
* @author ruby
*
*/
public class BackGround {
public BufferedImage img = null;// 背景圖片
public int width = 0;// 背景的寬度
public int height = 0;// 背景的高度
private static BackGround instance = null;
private BackGround() {
try {
// ImageIO用於載入圖片資源
// this.getClass().getResource根據當前路徑載入圖片
img = ImageIO.read(this.getClass().getResource(Constant.PATH_BACKGROUND));
// 獲取背景圖片長度和高度
width = img.getWidth();// 獲取圖片的寬度
height = img.getHeight();// 獲取圖片的寬度
System.out.println("widthBg=" + width + ", heightBg=" + height);
} catch (IOException e) {
e.printStackTrace();
}
}
// 實現執行緒安全的懶漢式
public static BackGround getInstance() {
if (instance == null) {
instance = new BackGround();
}
return instance;
}
}
程式碼實現:
package com.ruby.demo;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* step1:背景類:單例模式
*
* @author ruby
*
*/
public class BackGround {
public BufferedImage img = null;// 背景圖片
public int width = 0;// 背景的寬度
public int height = 0;// 背景的高度
private static BackGround instance = null;
private BackGround() {
try {
// ImageIO用於載入圖片資源
// this.getClass().getResource根據當前路徑載入圖片
img = ImageIO.read(this.getClass().getResource(Constant.PATH_BACKGROUND));
// 獲取背景圖片長度和高度
width = img.getWidth();// 獲取圖片的寬度
height = img.getHeight();// 獲取圖片的寬度
System.out.println("widthBg=" + width + ", heightBg=" + height);
} catch (IOException e) {
e.printStackTrace();
}
}
// 實現執行緒安全的懶漢式
public static BackGround getInstance() {
if (instance == null) {
instance = new BackGround();
}
return instance;
}
}
說明:
- 整個專案只有這一個背景,所以可以設計為單例模式。
- 通過getResource()方法載入圖片資源。
step2:然後建立一個皮膚類,上面用於實現背景,小鳥等。GamePanel
這裡主要重寫paint()方法,將背景圖片,顯示到皮膚上。
程式碼實現:
package com.ruby.demo;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
/**
* step2:自定義JPanel類的子類
*
* @author ruby
*
*/
public class GamePanel extends JPanel {
private BackGround bg = null;// 宣告背景物件
/*
* 建構函式
*/
public GamePanel() {
// 單例模式宣告背景物件和地面物件
bg = BackGround.getInstance();
}
/**
* 當前皮膚中繪製元件(載入圖片等)
*
* paint方法會在初始化以及最小和最大化時自動呼叫該方法(即視窗發生變化時,jvm都會自動呼叫該方法用於繪製皮膚)
*/
@Override
public void paint(Graphics g) {
super.paint(g);
System.out.println("paint方法被呼叫" + getCurrentTime());
// Graphics物件繪製背景圖案
g.drawImage(bg.img, 0, 0, null);
}
// 獲取當前時間
public String getCurrentTime() {
Date day = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return df.format(day);
}
}
step3:建立一個窗體類GameFrame,裡面新增剛剛建立的GamePanel物件。
但是首先要在Constant常量類中,設定一些常量:
程式碼:
// 介面引數
public static String GAME_TITLE = "飛翔吧小鳥";
public static int WINDOW_WIDTH = 432;
public static int WINDOW_HEIGHT = 644;
程式碼實現:
package com.ruby.demo;
import javax.swing.JFrame;
/**
* step3:窗體
* @author ruby
*
*/
public class GameFrame extends JFrame {
// 初始化窗體
public void initFrame() {
// 設定視窗標題
setTitle(Constant.GAME_TITLE);
// 設定視窗大小
setSize(Constant.WINDOW_WIDTH, Constant.WINDOW_HEIGHT);
// 新增Panel
GamePanel panel = new GamePanel();
add(panel);
// 設定視窗座標
setLocationRelativeTo(null);
// 設定視窗可見
setVisible(true);
// 設定視窗大小不可調整
setResizable(false);
// 監聽視窗關閉,程式結束
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
step4:建立Main類,表示程式的入口:
程式碼實現:
package com.ruby.demo;
/**
* 程式的入口
* @author ruby
*
*/
public class Main {
public static void main(String[] args) {
GameFrame frame = new GameFrame();
frame.initFrame();
}
}
執行效果:
2、實現地面移動
思路:
首先得先在Constant常量類中,新增地面的圖片路徑,並且將地面圖片拷貝到pics資源目錄下。
// 地面圖片路徑
public static String PATH_GROUND = PATH_PIC + "ground.png";
然後建立Ground類:
然後建立Ground類:
package com.ruby.demo;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* step1:地面類
*
* @author ruby
*
*/
public class Ground {
public BufferedImage img = null;// 地面圖片
public int x, y;// 地面繪製的起始座標
public int width = 0;// 地面的寬度
public int height = 0;// 地面的高度
private static Ground instance = null;
private Ground() {
try {
// 單例模式
BackGround bg = BackGround.getInstance();
// ImageIO用於載入圖片資源
// this.getClass().getResource根據當前路徑載入圖片
img = ImageIO.read(this.getClass().getResource(Constant.PATH_GROUND));
// 獲取地面圖片的長度和高度
width = img.getWidth();// 獲取圖片的寬度
height = img.getHeight();// 獲取圖片的寬度
x = 0;
y = bg.height - height;// 背景高度與地面圖片高度的差值就是地面圖片的起始Y座標
System.out.println("widthGround=" + width + ", heightGround=" + height);
System.out.println("x=" + x + ", y=" + y);
} catch (IOException e) {
e.printStackTrace();
}
}
// 實現懶漢式
public static Ground getInstance() {
if (instance == null) {
instance = new Ground();
}
return instance;
}
}
說明:在該類中要計算出地面的座標點x和y。
x為0即可,而y的值為背景圖片的高度減去地面圖片的高度。
然後新增一個地面移動的方法:
// 地面移動
public void move(BackGround bg) {
x--;
if (x == bg.width +9 - width) {// 9為修正值,根據地面移動效果調整該數值,保證圖片移動自然流暢。是地面圖片中條紋間距的一半數值。
x = 0;
}
System.out.println("x=" + x);
}
所謂的運動地面,就是就是修改x的值,地面向左側移動,所以x--。
然後要修改GamePanel中的paint()方法,繪製地面:
@Override
public void paint(Graphics g) {
super.paint(g);
System.out.println("paint方法被呼叫" + getCurrentTime());
// Graphics物件繪製背景圖案
g.drawImage(bg.img, 0, 0, null);
// 繪製地面
g.drawImage(ground.img, ground.x, ground.y, null);
}
然後在GamePanel類中新增一個新增的方法action(),表示遊戲的動作,我們需要設定一個死迴圈,來讓地面不停的移動。
多久移動一次呢,我們可以設定一個速度,其實就表示遊戲的速度,可以初始化一個常量,每當過一關,遊戲的速度可以適當的增加。
現在定義一個常量:
public static int MOVE_SPEED = 40;// 地面及柱子移動初始速度。當積分累加,速度會遞增
在GamePanel類中新增一個變數speed,表示速度:
private int speed = 0;// 柱子和地面的移動速度
修改GamePanel()構造方法:
// 初始化速度
speed = Constant.MOVE_SPEED;
然後新增一個action()方法,
public void action() {
// 設定滑鼠監聽
// 設定鍵盤監聽
// 通過監聽滑鼠事件,監聽到state的變化,無限迴圈,不斷切換狀態
while (true) {
// 地面移動
ground.move(bg);
// 執行緒休眠(因為是無限迴圈,下一次迴圈開始需要一段休息時間,這樣才能讓程式有緩衝的執行時間)
try {
Thread.sleep(1000 / speed);// 調節遊戲速度
// 重新繪製(重新呼叫皮膚paint方法)
this.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
}
為了不讓地面移動太快,頻繁的繪製介面,我們需要讓程式睡眠一下,這個睡眠的時間,其實表示遊戲的速度,就是地面和柱子移動的速度。
然後在GameFrame的initFrame()方法中,呼叫action()方法:
// 皮膚執行
panel.action();
執行效果:
3、顯示開始和結束
思路:整個遊戲分為3個狀態:遊戲開始前,開始玩,遊戲結束。定義一個變數state,0表示未開始,1表示玩ing,2表示game over,遊戲結束。
當遊戲未開始狀態,顯示開始圖片。
點選開始遊戲後,可以玩,當小鳥撞到地面或者天空或者柱子,遊戲結束。
遊戲結束時,顯示結束圖片。
在GamePanel類中,新增一個變數state
private static int state = 0;// 遊戲狀態,0表示遊戲未開始,1表示遊戲正在進行,2表示遊戲結束
然後在構造方法中,初始化state的狀態,以及載入開始和結束的圖片:
/*
* 建構函式
*/
public GamePanel() {
// 初始化資料
// 單例模式宣告背景物件和地面物件
bg = BackGround.getInstance();
ground = Ground.getInstance();
// state = 0表示遊戲未開始
state = 0;
try {
// 載入開始和結束圖片
imgStart = ImageIO.read(this.getClass().getResource(Constant.PATH_START));
imgOver = ImageIO.read(this.getClass().getResource(Constant.PATH_GAMEOVER));
} catch (IOException e) {
e.printStackTrace();
}
}
同時將兩張圖片,新增到pics目錄下,並且在Constant類中,新增常量值:
public static String PATH_START = PATH_PIC + "start.png";
public static String PATH_GAMEOVER = PATH_PIC + "gameover.png";
修改繪圖的方法paint(),先繪製背景,然後根據狀態不同,繪製不同的圖案,最後繪製地面:
@Override
public void paint(Graphics g) {
super.paint(g);
System.out.println("paint方法被呼叫" + getCurrentTime());
// Graphics物件繪製背景圖案
g.drawImage(bg.img, 0, 0, null);
// 根據狀態,繪製不同的圖案
if (state == 0) {//遊戲未開始
// 遊戲未開始時,繪製開始圖案及小鳥
g.drawImage(imgStart, 0, 0, null);
} else if (state == 1) {// 遊戲開始後
} else if (state == 2) {// 遊戲結束
// 遊戲結束時,繪製結束圖案
g.drawImage(imgOver, 0, 0, null);
}
// 繪製地面
g.drawImage(ground.img, ground.x, ground.y, null);
}
修改action()方法,新增滑鼠事件:
public void action() {
// 設定滑鼠監聽
this.addMouseListener(new MouseAdapter() {
//點選滑鼠後
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
switch (state) {
case 0://遊戲未開始
// 切換到狀態1時的資料
state = 1;
break;
case 1://開始遊戲
state = 2;//...
break;
case 2://遊戲結束
//遊戲結束後,更改狀態為0,可以繼續下一次遊戲
state = 0;
break;
default:
break;
}
}
});
// 設定鍵盤監聽
//....
}
執行效果:
點選一下,開始遊戲,再點一下結束遊戲,效果如圖:
開始遊戲的時候,小鳥是灰色的,我們希望小鳥能一直扇動翅膀。
小鳥翅膀扇動,其實就是小鳥的8張圖迴圈輪播。我們可以通過陣列來實現。
現在Constant類中定義小鳥的圖片數量,以及小鳥的初始位置:
public static int BIRD_PIC_COUNT = 8;// 小鳥皮膚個數
public static int BIRD_POSITION_X = 190;// 小鳥初始化座標
public static int BIRD_POSITION_Y = 220;
然後建立一個小鳥類Bird:
package com.ruby.demo;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* 小鳥類
*
* @author ruby
*
*/
public class Bird {
public BufferedImage img = null;// 小鳥圖片
public BufferedImage imgs[] = new BufferedImage[Constant.BIRD_PIC_COUNT];// 陣列,儲存所有小鳥圖案
public static int index = 0;// 當前皮膚的序號
public int x, y;// 初始座標
public int width = 0;// 小鳥的寬度
public int height = 0;// 小鳥的高度
public Bird() {
try {
for (int i = 0; i < 8; i++) {
imgs[i] = ImageIO.read(getClass().getResource(Constant.PATH_PIC + i + ".png"));
}
img = imgs[0];
// 獲取小鳥的寬度和高度
width = img.getWidth();
height = img.getHeight();
// 初始化小鳥的座標位置
x = Constant.BIRD_POSITION_X;
y = Constant.BIRD_POSITION_Y;
} catch (IOException e) {
e.printStackTrace();
}
}
}
新增一個小鳥扇動翅膀的方法:
// 小鳥飛翔的圖片切換
public void fly() {
index++;
// 小鳥圖形切換的頻率,index/x,x越大,翅膀切換頻率越慢
img = imgs[index / 6 % Constant.BIRD_PIC_COUNT];
if (index == 6 * Constant.BIRD_PIC_COUNT) {
index = 0;
}
}
然後在GamePanel類中新增小鳥物件,
private Bird bird = null;// 宣告小鳥物件
並在構造方法中初始化:
// 宣告小鳥物件
bird = new Bird();
修改paint()方法,在未開始遊戲的時候,就要繪製小鳥了:
// 根據狀態,繪製不同的圖案
if (state == 0) {// 遊戲未開始
// 遊戲未開始時,繪製開始圖案及小鳥
g.drawImage(imgStart, 0, 0, null);
g.drawImage(bird.img, bird.x, bird.y, null);
} else if (state == 1) {// 遊戲開始後
} else if (state == 2) {// 遊戲結束
// 遊戲結束時,繪製結束圖案
g.drawImage(imgOver, 0, 0, null);
}
修改action()方法,在迴圈中,除了移動地面外,還要讓小鳥扇動翅膀:
// 通過監聽滑鼠事件,監聽到state的變化,無限迴圈,不斷切換狀態
while (true) {
// 地面移動
ground.move(bg);
bird.fly();
// 執行緒休眠(因為是無限迴圈,下一次迴圈開始需要一段休息時間,這樣才能讓程式有緩衝的執行時間)
try {
Thread.sleep(1000 / speed);// 調節遊戲速度
// 重新繪製(重新呼叫皮膚paint方法)
this.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
執行結果:
4、遊戲開始
遊戲開始後,小鳥就要移動了。還要新增上下兩根柱子,柱子向左側移動,通過點選滑鼠,讓小鳥上下移動,來躲避柱子。
小鳥上下飛動
我們現在實現小鳥的部分:
遊戲開始時,小鳥在距離螢幕左側120左右的位置就可以:
在Constant類中定義,遊戲開始時小鳥的位置:
public static int BIRD_FLY_POSITION_X = 120;// 小鳥開始飛翔時初始座標
首先修改action()中,case 0裡,先修改小鳥的位置
public void action() {
// 設定滑鼠監聽
this.addMouseListener(new MouseAdapter() {
// 點選滑鼠後
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
switch (state) {
case 0:// 遊戲未開始
// 切換到狀態1時的資料
state = 1;
bird.x = Constant.BIRD_FLY_POSITION_X;// 小鳥飛的初始x座標
break;
.....
}
然後在paint()方法中,state如果為1,代表遊戲開始,應該繪製小鳥和兩根柱子,我們先畫小鳥:
else if (state == 1) {// 遊戲開始後
//繪製小鳥和兩根柱子
g.drawImage(bird.img, bird.x, bird.y, null);
}
執行效果:
小鳥預設會向下掉,要考慮重力加速度。當點選滑鼠的時候,會向上移動。
先在Constant提供常量:
public static double GRAVITATIONAL_ACCELERATION = 9.8;
public static double DOWN_TIME = 0.18; // 小鳥自然下降的時長
在小鳥Bird類中,提供一些變數:
public double g = Constant.GRAVITATIONAL_ACCELERATION; // 重力加速度
public double v = 0;// 下降速度
public double t = Constant.DOWN_TIME;// 下降時間
public double h;// 下降的距離
再新增兩個方法:down()表示下降
// 小鳥自然下降
public void down() {
v = v - g * t; // 末速度Vt=Vo-gt
h = v * t - g * t * t / 2; // 位移h=Vot-gt²/2
y = y - (int) h;
}
然後修改action()方法:
while (true) {
// 地面移動
ground.move(bg);
bird.fly();
if(state == 0){
}else if(state == 1){
bird.down();//小鳥下降
}else if(state == 2){
}
// 執行緒休眠(因為是無限迴圈,下一次迴圈開始需要一段休息時間,這樣才能讓程式有緩衝的執行時間)
try {
Thread.sleep(1000 / speed);// 調節遊戲速度
// 重新繪製(重新呼叫皮膚paint方法)
this.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
執行結果:
當點選滑鼠時,小鳥會向上飛,每次向上20。先在Constant中新增一個常量:
public static double UP_SPEED = 20;// 上升的速度
在Bird中,再新增一個方法:
// 上升,點滑鼠或點鍵盤向上鍵
public void up() {
v = Constant.UP_SPEED;
}
修改action(),在滑鼠抬起的事件中,如果state為1,那麼要呼叫up()方法,讓小鳥上升:
...
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
switch (state) {
case 0:// 遊戲未開始
// 切換到狀態1時的資料
state = 1;
bird.x = Constant.BIRD_FLY_POSITION_X;// 小鳥飛的初始x座標
break;
case 1:// 開始遊戲
// 當狀態1時,小鳥點選向上移動
bird.up();
break;
...
柱子左右移動
首先在pics下放圖片資源pillar.png。然後在Constant類中,新增常量:
// 柱子引數
public static String PATH_PILLAR = PATH_PIC + "pillar.png";
public static int PILLAR_GAP = 144;// 柱子通道距離
public static int PILLAR_DISTANCE = 244;// 柱子間距
然後建立柱子類Pillar類:
package com.ruby.demo;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
/**
* 柱子類 其中構造方法中需要背景物件和地面物件
*
* @author ruby
*
*/
public class Pillar {
public BufferedImage img;// 圖片
public int x, y;// 座標
public int width = 0;// 柱子寬度
public int height = 0;// 柱子高度
Random random = new Random();// 一個生成隨機數的物件
private int max, min = 0;// 為了保證柱子通道能夠完全顯示在螢幕上,所以存在柱子在Y座標的最大值和最小值
public Pillar(BackGround bg, Ground ground) {
try {
img = ImageIO.read(getClass().getResource(Constant.PATH_PILLAR));
width = img.getWidth();
height = img.getHeight();
System.out.println("柱子width=" + width + ",height=" + height);
x = bg.width;
max = (height - Constant.PILLAR_GAP) / 2;
min = (height - Constant.PILLAR_GAP) / 2 - (bg.height - Constant.PILLAR_GAP - ground.height);
y = -(min + random.nextInt(max - min));
// System.out.println("y=" + y);
} catch (IOException e) {
e.printStackTrace();
}
}
}
小鳥闖關的柱子,每隔244間距,就要再產生一根柱子。柱子的高度通過隨機數來產生,先計算出柱子的高度:柱子圖片高度-柱子通道距離114,然後除以2。柱子的最小高度,就是柱子的高度減去背景圖高度-地面高度-通道距離。
柱子的y座標,就因該是柱子的高度和柱子最小高度之間的隨機數。
再新增一個移動柱子的方法:
// 柱子移動,遊戲一旦開始則柱子移動
public void move(BackGround bg) {
x--;
if (x == -width) {
x = bg.width;
y = -(min + random.nextInt(max - min));
// System.out.println("y=" + y);
}
}
然後修改GamePanel類,建立2個柱子物件,因為遊戲介面中,最多出現2根柱子。然後再構造方法中,例項化兩個柱子物件,並設定x座標,柱子是從遊戲介面右側,移入到遊戲介面上,所以第一個柱子的x值為遊戲介面的寬度,第二個柱子要再加柱子間距。
// 宣告兩個柱子,並分別設定柱子的起始X座標
p1 = new Pillar(bg, ground);
p1.x = bg.width;
p2 = new Pillar(bg, ground);
p2.x = bg.width + Constant.PILLAR_DISTANCE;
修改paint()方法,遊戲開始後,繪製柱子:
else if (state == 1) {// 遊戲開始後
// 繪製小鳥和兩根柱子
g.drawImage(bird.img, bird.x, bird.y, null);
g.drawImage(p1.img, p1.x, p1.y, null);
g.drawImage(p2.img, p2.x, p2.y, null);
}
然後再action()的迴圈裡,呼叫兩個柱子的移動方法:
while (true) {
// 地面移動
ground.move(bg);
bird.fly();
if (state == 0) {
} else if (state == 1) {
// 遊戲開始。地面移動、柱子移動、小鳥飛並自然下降
bird.down();
p1.move(bg);
p2.move(bg);
} else if (state == 2) {
}
// 執行緒休眠(因為是無限迴圈,下一次迴圈開始需要一段休息時間,這樣才能讓程式有緩衝的執行時間)
try {
Thread.sleep(1000 / speed);// 調節遊戲速度
// 重新繪製(重新呼叫皮膚paint方法)
this.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
執行結果:
5、計算得分
然後在Constant類中,新增和得分相關的常量:
// 得分資訊的字型大小及座標
public static int FONT_SIZE = 20;
public static int SCORE_X = 20;
public static int SCORE_Y = 40;
在GamePanel中定義score,表示分數,然後在paint()方法中,繪製分數:
// 繪製文字
Font font = new Font(Font.SERIF, Font.ITALIC, Constant.FONT_SIZE);//字型,傾斜,大小
g.setFont(font);
g.setColor(Color.white);// 這裡font和color導包都導java.awt
g.drawString("得分:" + score, Constant.SCORE_X, Constant.SCORE_Y);
要想統計分數,得先計算小鳥的各種碰撞,首先在小鳥的類中,新增一個是否碰撞地面的方法,其實就是檢測小鳥y的值:
// 碰撞檢測
// 掉落到地面時
public boolean hitGround(BackGround bg, Ground ground) {
if (y + height >= (bg.height - ground.height)) {
return true;
}
return false;
}
再新增一個檢測是否碰撞天空的方法,就是遊戲介面的頂部:
// 碰撞到舞臺頂部時
public boolean hitSky() {
if (y <= 0) {
return true;
}
return false;
}
再新增一個檢測碰撞柱子的方法:
// 碰到柱子時的檢測
public boolean hitPillar(Pillar p) {
// x方向小鳥和柱子碰撞的條件
if ((x + width) >= p.x && x <= p.x + p.width) {
if (y <= p.y + (p.height - Constant.PILLAR_GAP) / 2
|| y >= p.y + (p.height + Constant.PILLAR_GAP) / 2 - height) {
return true;
}
}
return false;
}
要判斷小鳥的橫向上,碰撞柱子。
在小鳥類裡新增一個方法:
// 碰到柱子時的檢測
public boolean hitPillar(Pillar p) {
// x方向小鳥和柱子碰撞的條件
if ((x + width) >= p.x && x <= p.x + p.width) {
if (y <= p.y + (p.height - Constant.PILLAR_GAP) / 2
|| y >= p.y + (p.height + Constant.PILLAR_GAP) / 2 - height) {
return true;
}
}
return false;
}
再新增一個積分的方法:
// 增加積分,通過柱子通道後呼叫該方法
public boolean addScore(Pillar p) {
// System.out.println("x=" + x + ",p.x=" + p.x);
if (x == p.x + p.width) {
return true;
}
return false;
}
然後在GamePanel類中修改action()方法:
while (true) {
if (state == 0) {
// 遊戲未開始。地面移動,小鳥展翅
ground.move(bg);
bird.fly();
} else if (state == 1) {
// 遊戲開始。地面移動、柱子移動、小鳥飛並自然下降
ground.move(bg);
p1.move(bg);
p2.move(bg);
bird.fly();
bird.down();
// 碰到地面、天空、柱子都顯示遊戲結束。
if (bird.hitGround(bg, ground) || bird.hitSky() || bird.hitPillar(p1) || bird.hitPillar(p2)) {
state = 2;
} else {
// 小鳥每通過一個竹子通道,累計積分,並提高柱子和地面移動速度。
if (bird.addScore(p1) || bird.addScore(p2)) {
score++;
// 每通過一個柱子,速度會遞增
speed += 2;
// System.out.println("speed=" + speed);
}
}
}
然後修改滑鼠事件的監聽:
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
super.mousePressed(e);
switch (state) {
case 0:
// 切換到狀態1時的資料
state = 1;
bird.x = Constant.BIRD_FLY_POSITION_X;// 小鳥飛的初始x座標
musicThread = new MusicThread();
musicThread.start();
break;
case 1:
// 當狀態1時,小鳥點選向上移動
bird.up();
break;
case 2:
// 切換到狀態0時的資料
musicThread.stopBGM();
state = 0;
score = 0;
// 重置小鳥的位置
bird.x = Constant.BIRD_POSITION_X;
bird.y = Constant.BIRD_POSITION_Y;
bird.v = 0;
// 重置柱子的座標
p1.x = bg.width;
p2.x = bg.width + Constant.PILLAR_DISTANCE;
// System.out.println("p1==" + p1 + ", p2==" + p2);
break;
default:
break;
}
}
當遊戲結束的時候,要初始化小鳥和柱子的資料。
6、新增小鳥的鍵盤事件
在Constant中新增常量:
public static int DISTANCE_PER_PRESS = 10;// 每點一次滑鼠或鍵盤,移動的位置
然後在小鳥類中,新增鍵盤的上下左右事件方法:
// 後退,點鍵盤向左鍵
public void backward() {
x -= Constant.DISTANCE_PER_PRESS;
}
// 前進,點鍵盤向右鍵
public void foward() {
x += Constant.DISTANCE_PER_PRESS;
}
// 點選鍵盤下降,點鍵盤向下鍵
public void pressdown() {
y += Constant.DISTANCE_PER_PRESS;
}
然後在action()中,新增滑鼠事件監聽:
// 設定鍵盤監聽事件
this.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
bird.up();
break;
case KeyEvent.VK_RIGHT:
bird.foward();
break;
case KeyEvent.VK_LEFT:
bird.backward();
break;
case KeyEvent.VK_DOWN:
bird.pressdown();
break;
}
}
});
注意,最後要在GameFrame中,可以響應鍵盤事件:
// 讓該Frame中的panel聚焦,可以響應鍵盤事件
panel.requestFocus();
7、新增背景音樂
先準備一首背景音樂,然後在src上建立一個音樂的資源目錄music,並將音樂檔案拷貝進去:
然後倒入音訊播放的jar包:
在Constant中新增常量:
//音樂路徑
public static String PATH_MUSIC = "/music/";
public static String PATH_BGM = PATH_MUSIC + "Ari_Pulkkinen-Funky_Theme.mp3";//music/Ari Pulkkinen-Funky Theme.mp3
然後建立一個執行緒類,播放音樂,再提供一個停止播放的方法:
package com.ruby.demo;
import java.io.InputStream;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;
// 播放音樂
class MusicThread extends Thread {
Player player = null;
@Override
public void run() {
// 繼承執行緒類後,重寫run方法,播放音樂的程式碼
// 1.載入音訊檔案得到輸入流
InputStream inputStream = this.getClass().getResourceAsStream(Constant.PATH_BGM);
try {
// 2.建立Player物件,播放音樂
player = new Player(inputStream);
player.play();
} catch (JavaLayerException e) {
e.printStackTrace();
}
}
public void stopBGM(){
if(player != null){
player.close();
}
}
}
然後在GamePanel裡建立MusicThread物件,用於播放和停止音樂,在action()中修改程式碼:
this.addMouseListener(new MouseAdapter() {
// 點選滑鼠後
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
switch (state) {
case 0:// 遊戲未開始
// 切換到狀態1時的資料
state = 1;
bird.x = Constant.BIRD_FLY_POSITION_X;// 小鳥飛的初始x座標
musicThread = new MusicThread();
musicThread.start();
break;
case 1:// 開始遊戲
// 當狀態1時,小鳥點選向上移動
bird.up();
break;
case 2:// 遊戲結束
// 遊戲結束後,更改狀態為0,可以繼續下一次遊戲
musicThread.stopBGM();
state = 0;
score = 0;
// 重置小鳥的位置
bird.x = Constant.BIRD_POSITION_X;
bird.y = Constant.BIRD_POSITION_Y;
bird.v = 0;
// 重置柱子的座標
p1.x = bg.width;
p2.x = bg.width + Constant.PILLAR_DISTANCE;
break;
default:
break;
}
}
});
在state為0時,開始播放音樂,當時遊戲結束state為2時,停止音樂。
相關文章
- PaddlePaddle版Flappy-Bird—使用DQN演算法實現遊戲智慧APP演算法遊戲
- Flappy Bird 惡意程式詳細分析APP
- P1941 NOIP2014 提高組 飛揚的小鳥 題解
- Scratch3之AI整合 - flappy bird AI版本AIAPP
- java實現分散式專案搭建Java分散式
- 飛機小專案
- 使用EventStoreDB實現事件溯源的Java開源專案事件Java
- 極客大學小馬哥的 Java 專案實戰營Java
- Java實現飛機大戰遊戲Java遊戲
- java web專案 使用elfinder 實現檔案管理器JavaWeb
- Java小遊戲——飛機大戰Java遊戲
- DQN(Deep Q-learning)入門教程(四)之Q-learning Play Flappy BirdAPP
- DQN(Deep Q-learning)入門教程(六)之DQN Play Flappy-bird ,MountainCarAPPAI
- 34套Java專案教程+原始碼包含Java swing專案 Java web專案 Java控制檯專案(視訊教程+原始碼)Java原始碼Web
- Java實現檔案切割拼接Java
- 使用java的MultipartFile實現layui官網檔案上傳實現全部示例,java檔案上傳JavaUI
- 最適合Java基礎練手的Java小專案「圖書管理系統」Java
- 2018-10-16 java學習小專案合Java
- 關於一個java專案呼叫另一個java專案的心得Java
- 太讚了!用Java實現的線上聊天小專案,適合鞏固基礎(附原始碼)Java原始碼
- Rovio怎樣讓“憤怒的小鳥”重新起飛?
- Java實現檔案下載功能Java
- mvn 建立java專案 web專案JavaWeb
- java實現檔案的下載的方法概述Java
- 【Java基礎教程】用Java實現猜數字小遊戲Java遊戲
- 「小程式JAVA實戰」 小程式wxss樣式檔案的使用(七)Java
- java菜鳥入門Java
- java實現一個簡單的爬蟲小程式Java爬蟲
- Java大作業:AI千戀萬花(呼叫api實現)附專案)JavaAIAPI
- 以Java專案為例,實現Jenkins對接CCE Autopilot叢集JavaJenkins
- Java 專案中使用 Resilience4j 框架實現故障隔離Java框架
- Docker部署Java專案DockerJava
- Java專案問題Java
- Java程式設計師從笨鳥到菜鳥(五十二) 配置檔案實現將返回 POJO 類直接轉換成 json 物件Java程式設計師POJOJSON物件
- Java專案:線上嘿嘿網盤系統設計和實現(java+Springboot+ssm+mysql+maven)JavaSpring BootSSMMySqlMaven
- java怎麼匯入專案?java已有專案如何匯入eclipse?JavaEclipse
- 利用 Webpack 實現小程式多專案管理Web專案管理
- Java Spring Cloud 實戰之路 - 1 建立專案JavaSpringCloud