Java實現(xiàn)經(jīng)典游戲之大魚吃小魚
一、項目演示
二、項目實現(xiàn)
1.創(chuàng)建游戲窗口
創(chuàng)建一個游戲窗口類 GameWin,創(chuàng)建一個 launch() 啟動方法,在其中設(shè)置窗口相關(guān)屬性:
import javax.swing.*;
public class GameWin extends JFrame {
int width = 1440;
int height = 900;
//創(chuàng)建一個啟動方法,設(shè)置窗口信息
public void launch() {
this.setVisible(true); //設(shè)置窗口可見
this.setSize(width, height); //設(shè)置窗口大小
this.setLocationRelativeTo(null); //設(shè)置窗口居中
this.setResizable(false); //設(shè)置窗口大小不可改變
this.setTitle("大魚吃小魚"); //設(shè)置窗口標(biāo)題
this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕
}
}
創(chuàng)建一個游戲窗口測試類 GameWinDemo,進行窗口對象的創(chuàng)建和啟動方法的調(diào)用:
public class GameWinDemo {
public static void main(String[] args) {
//創(chuàng)建一個游戲窗口對象
GameWin gameWin = new GameWin();
//啟動窗口
gameWin.launch();
}
}

2.添加背景圖片
將下載的背景圖片的文件夾復(fù)制到項目文件夾中:

創(chuàng)建一個工具類 GameUtils,在其中添加背景圖片:
import java.awt.*;
public class GameUtils {
public static Image bgimg = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\sea.jpg");
}
在 GameWin 類中添加 paint 方法,在其中繪制背景圖片:
@Override
public void paint(Graphics g) {
//用畫筆繪制背景圖片
g.drawImage(GameUtils.bgimg, 0, 0, null);
}
如果發(fā)現(xiàn)背景圖片加載不出來,先檢查一下路徑有沒有寫錯,相對路徑不行的話就試試絕對路徑;有時候加載背景圖片會較慢,可以試著將運行出來的窗口最小化后再恢復(fù)

3.制作封面
定義默認(rèn)游戲狀態(tài),同時在 paint 方法中對游戲狀態(tài)進行判斷進而顯示對應(yīng)的游戲界面:
import javax.swing.*;
import java.awt.*;
public class GameWin extends JFrame {
int width = 1440;
int height = 900;
//定義游戲默認(rèn)狀態(tài)
static int state = 0;
//創(chuàng)建一個啟動方法,設(shè)置窗口信息
public void launch() {
this.setVisible(true); //設(shè)置窗口可見
this.setSize(width, height); //設(shè)置窗口大小
this.setLocationRelativeTo(null); //設(shè)置窗口居中
this.setResizable(false); //設(shè)置窗口大小不可改變
this.setTitle("大魚吃小魚"); //設(shè)置窗口標(biāo)題
this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕
}
@Override
public void paint(Graphics g) {
//用畫筆繪制背景圖片
g.drawImage(GameUtils.bgimg, 0, 0, null);
//游戲狀態(tài):0未開始,1游戲中,2游戲失敗,3游戲勝利,4游戲暫停,5重新開始
switch (state) {
case 0:
g.drawImage(GameUtils.bgimg, 0, 0, null);
//為啟動頁面添加文字
g.setColor(Color.pink);
g.setFont(new Font("仿宋", Font.BOLD, 60));
g.drawString("請點擊開始游戲", 600, 500);
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
}
}

4.啟動頁面的點擊事件
在啟動方法中添加鼠標(biāo)監(jiān)聽器,當(dāng)點擊啟動頁面時游戲開始
//創(chuàng)建一個啟動方法,設(shè)置窗口信息
public void launch() {
this.setVisible(true); //設(shè)置窗口可見
this.setSize(width, height); //設(shè)置窗口大小
this.setLocationRelativeTo(null); //設(shè)置窗口居中
this.setResizable(false); //設(shè)置窗口大小不可改變
this.setTitle("大魚吃小魚"); //設(shè)置窗口標(biāo)題
this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕
//添加鼠標(biāo)監(jiān)聽事件
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == 1 && state == 0) {
state = 1;
repaint();
}
}
});
}
5.游戲開始時的背景添加
新建一個背景圖的實體類 Bg,在 GameWin 類里創(chuàng)建其對象,并在 case 1 中調(diào)用繪制背景圖的方法
import java.awt.*;
public class Bg {
void paintSelf(Graphics g) {
g.drawImage(GameUtils.bgimg, 0, 0, null);
}
}
由于游戲中需要不斷繪制背景圖片,所以我們在啟動方法中添加一個 while 循環(huán),在其中每隔 40 毫秒重繪背景圖
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GameWin extends JFrame {
int width = 1440;
int height = 900;
//定義游戲默認(rèn)狀態(tài)
static int state = 0;
//獲取背景圖類的對象
Bg bg = new Bg();
//創(chuàng)建一個啟動窗口,設(shè)置窗口信息
public void launch() throws InterruptedException {
this.setVisible(true); //設(shè)置窗口可見
this.setSize(width, height); //設(shè)置窗口大小
this.setLocationRelativeTo(null); //設(shè)置窗口居中
this.setResizable(false); //設(shè)置窗口大小不可改變
this.setTitle("大魚吃小魚"); //設(shè)置窗口標(biāo)題
this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕
//添加鼠標(biāo)監(jiān)聽事件
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == 1 && state == 0) {
state = 1;
repaint();
}
}
});
//背景圖片重復(fù)使用,需要重復(fù)調(diào)用repaint方法
while(true) {
repaint();
Thread.sleep(40);
}
}
@Override
public void paint(Graphics g) {
//用畫筆繪制背景圖片
g.drawImage(GameUtils.bgimg, 0, 0, null);
switch (state) {
case 0:
g.drawImage(GameUtils.bgimg, 0, 0, null);
//為啟動頁面添加文字
g.setColor(Color.pink);
g.setFont(new Font("仿宋", Font.BOLD, 60));
g.drawString("請點擊開始游戲", 500, 500);
break;
case 1:
bg.paintSelf(g);
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
}
}
6.雙緩存解決閃屏問題
如果此時你的屏幕出現(xiàn)閃爍的情況,可以用下面的方法解決
整體思路為:
重新創(chuàng)建一個空的圖片,將所有的組件先繪制到空的圖片上,然后把繪制好的圖片一次性地繪制到主窗口上
創(chuàng)建一個空圖片對象,在 paint 方法中將所有組件繪制到空圖片上,再一次性繪制到主窗口上
public class GameWin extends JFrame {
int width = 1440;
int height = 900;
//定義游戲默認(rèn)狀態(tài)
static int state = 0;
//獲取背景圖類的對象
Bg bg = new Bg();
//創(chuàng)建一個空圖片對象
Image offScreenImage;
//創(chuàng)建一個啟動窗口,設(shè)置窗口信息
public void launch() throws InterruptedException {
this.setVisible(true); //設(shè)置窗口可見
this.setSize(width, height); //設(shè)置窗口大小
this.setLocationRelativeTo(null); //設(shè)置窗口居中
this.setResizable(false); //設(shè)置窗口大小不可改變
this.setTitle("大魚吃小魚"); //設(shè)置窗口標(biāo)題
this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕
//添加鼠標(biāo)監(jiān)聽事件
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == 1 && state == 0) {
state = 1;
repaint();
}
}
});
//背景圖片重復(fù)使用,需要重復(fù)調(diào)用repaint方法
while (true) {
repaint();
Thread.sleep(40);
}
}
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
switch (state) {
case 0:
//把組件重新繪制到主窗口中
gImage.drawImage(GameUtils.bgimg, 0, 0, null);
//為啟動頁面添加文字
gImage.setColor(Color.pink);
gImage.setFont(new Font("仿宋", Font.BOLD, 60));
gImage.drawString("請點擊開始游戲", 500, 500);
break;
case 1:
bg.paintSelf(gImage);
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
}
7.敵方第一條小雨的添加
新建敵方魚的父類 Enamy,編寫左敵方魚類 Enamy_1_L 繼承父類
import java.awt.*;
public class Enamy {
//定義圖片
Image img;
//定義物體坐標(biāo)
int x;
int y;
int width;
int height;
//移動速度
int speed;
//方向
int dir = 1;
//敵方魚的類型、分值
int type;
int count;
//繪制自身方法
public void paintSelf(Graphics g) {
g.drawImage(img, x, y, width, height, null);
}
//獲取自身矩形用于碰撞檢測
public Rectangle getRec() {
return new Rectangle(x, y, width, height);
}
}
//左敵方魚類
class Enamy_1_L extends Enamy {
Enamy_1_L() {
this.x = -45;
this.y = (int) (Math.random() * 700 + 100);
this.width = 45;
this.height = 69;
this.speed = 10;
this.count = 1;
this.img = GameUtils.enamy_l_img;
}
}
在 GameUtils 類中添加左敵方魚類的圖片
public class GameUtils {
//背景圖
public static Image bgimg = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\sea.jpg");
//敵方魚類
public static Image enamy_l_img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish1_r.gif");
}
在 GameWin 類中創(chuàng)建左敵方魚類對象并在 case 1 的情況下繪制左敵方魚類
public class GameWin extends JFrame {
//......
//敵方魚類
Enamy enamy = new Enamy_1_L();
//創(chuàng)建一個啟動窗口,設(shè)置窗口信息
public void launch() throws InterruptedException {
//......
}
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
switch (state) {
case 0:
//把組件重新繪制到主窗口中
gImage.drawImage(GameUtils.bgimg, 0, 0, null);
//為啟動頁面添加文字
gImage.setColor(Color.pink);
gImage.setFont(new Font("仿宋", Font.BOLD, 60));
gImage.drawString("請點擊開始游戲", 500, 500);
break;
case 1:
bg.paintSelf(gImage);
enamy.paintSelf(gImage);
enamy.x += enamy.speed;
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
}

8.敵方左方小魚的批量添加
在工具類中創(chuàng)建一個所有敵方魚物體的集合
public class GameUtils {
//敵方魚類集合
public static List<Enamy> EnamyList = new ArrayList<>();
//......
}
在窗口類中添加一個方法,用于批量添加敵方魚類
public class GameWin extends JFrame {
//......
//敵方魚類
Enamy enamy;
//計數(shù)器,用來記錄游戲的重繪次數(shù),也是魚生成的數(shù)量
int time = 0;
//創(chuàng)建一個啟動窗口,設(shè)置窗口信息
public void launch() throws InterruptedException {
//......
});
//背景圖片重復(fù)使用,需要重復(fù)調(diào)用repaint方法
while (true) {
repaint();
time++;
Thread.sleep(40);
}
}
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
switch (state) {
case 0:
//......
case 1:
bg.paintSelf(gImage);
logic(); //不斷添加敵方魚類
//遍歷敵方魚類集合,繪制敵方魚類
for (Enamy enamy : GameUtils.EnamyList) {
enamy.paintSelf(gImage);
}
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
//批量添加魚類
void logic() {
//每重繪10次生成一條敵方魚類
if (time % 10 == 0) {
enamy = new Enamy_1_L();
GameUtils.EnamyList.add(enamy);
}
//移動方向
for (Enamy enamy : GameUtils.EnamyList) {
enamy.x = enamy.x + enamy.dir * enamy.speed;
}
}
}

9.我方魚的生成
在工具類中添加我方魚類的圖片
public class GameUtils {
//......
//我方魚類
public static Image myFishImg_L = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\myFish\\myfish_left.gif");
public static Image myFishImg_R = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\myFish\\myfish_right.gif");
}
創(chuàng)建我方魚類
import java.awt.*;
public class MyFish {
//圖片
Image img = GameUtils.myFishImg_L;
//坐標(biāo)
int x = 700;
int y = 500;
int width = 50;
int height = 50;
//移動速度
int speed = 20;
//等級
int level = 1;
//繪制自身的方法
public void paintSelf(Graphics g) {
g.drawImage(img, x, y, width, height, null);
}
//獲取自身矩形的方法
public Rectangle getRec() {
return new Rectangle(x, y, width, height);
}
}
在窗口類中獲取我方魚的對象
//我方魚類 MyFish myFish = new MyFish();
在工具類中添加方向判定,用來控制我方魚的方向
public class GameUtils {
//方向
static boolean UP = false;
static boolean DOWN = false;
static boolean LEFT = false;
static boolean RIGHT = false;
//......
}
在我方魚類添加一個方法,實現(xiàn)對鍵盤的控制
public class MyFish {
//......
//繪制自身的方法
public void paintSelf(Graphics g) {
//調(diào)用方法
logic();
g.drawImage(img, x, y, width, height, null);
}
//......
void logic() {
if (GameUtils.UP) {
y = y - speed;
}
if (GameUtils.DOWN) {
y = y + speed;
}
if (GameUtils.LEFT) {
x = x - speed;
img = GameUtils.myFishImg_L;
}
if (GameUtils.RIGHT) {
x = x + speed;
img = GameUtils.myFishImg_R;
}
}
}
在窗口類的 paint 方法的 case 1 中,添加我方魚類所創(chuàng)造的方法
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class GameWin extends JFrame {
//......
//我方魚類
MyFish myFish = new MyFish();
//創(chuàng)建一個啟動窗口,設(shè)置窗口信息
public void launch() throws InterruptedException {
//......
});
//鍵盤移動
this.addKeyListener(new KeyAdapter() {
@Override//按壓
public void keyPressed(KeyEvent e) {
//上下左右四個鍵的ASCII值為 上 ↑:38下 ↓: 40左 ←: 37右 →: 39
if (e.getKeyCode() == 38) {
GameUtils.UP = true;
}
if (e.getKeyCode() == 40) {
GameUtils.DOWN = true;
}
if (e.getKeyCode() == 37) {
GameUtils.LEFT = true;
}
if (e.getKeyCode() == 39) {
GameUtils.RIGHT = true;
}
}
@Override//抬起
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 38) {
GameUtils.UP = false;
}
if (e.getKeyCode() == 40) {
GameUtils.DOWN = false;
}
if (e.getKeyCode() == 37) {
GameUtils.LEFT = false;
}
if (e.getKeyCode() == 39) {
GameUtils.RIGHT = false;
}
}
});
//......
}
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
switch (state) {
case 0:
//......
break;
case 1:
bg.paintSelf(gImage);
myFish.paintSelf(gImage);
logic(); //不斷添加敵方魚類
for (Enamy enamy : GameUtils.EnamyList) {
enamy.paintSelf(gImage);
}
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
//批量添加魚類
void logic() {
//......
}
}
10.我方魚與敵方小魚的碰撞測試
在游戲中所有物體被視為矩形,兩個物體是否碰撞,即檢測兩個物體的所在矩形是否有重疊部分
具體檢測,就是讓我方魚與敵方魚的矩形進行一一檢測,在窗口類的 logic() 方法中實現(xiàn)
//批量添加魚類
void logic() {
//每重繪10次生成一條敵方魚類
if (time % 10 == 0) {
enamy = new Enamy_1_L();
GameUtils.EnamyList.add(enamy);
}
//移動方向
for (Enamy enamy : GameUtils.EnamyList) {
enamy.x = enamy.x + enamy.dir * enamy.speed;
//我方魚與敵方魚的碰撞檢測
if (myFish.getRec().intersects(enamy.getRec())) {
enamy.x = -200;
enamy.y = -200;
}
}
}

11.游戲積分的實現(xiàn)
在工具類中定義分?jǐn)?shù)
//分?jǐn)?shù) static int count = 0;
在窗口類中實現(xiàn)我方魚吃掉敵方魚增加分?jǐn)?shù),同時在頁面上打印分?jǐn)?shù)
public class GameWin extends JFrame {
//......
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
switch (state) {
case 0:
//......
case 1:
bg.paintSelf(gImage);
//*******打印所得積分********
gImage.setColor(Color.ORANGE);
gImage.setFont(new Font("仿宋", Font.BOLD, 50));
gImage.drawString("積分:" + GameUtils.count, 200, 120);
myFish.paintSelf(gImage);
logic(); //不斷添加敵方魚類
for (Enamy enamy : GameUtils.EnamyList) {
enamy.paintSelf(gImage);
}
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
break;
}
//......
//批量添加魚類
void logic() {
//......
//移動方向
for (Enamy enamy : GameUtils.EnamyList) {
enamy.x = enamy.x + enamy.dir * enamy.speed;
//我方魚與敵方魚的碰撞檢測
if (myFish.getRec().intersects(enamy.getRec())) {
enamy.x = -200;
enamy.y = -200;
//********得分********
GameUtils.count += enamy.count;
}
}
}
}
在我方魚類中實現(xiàn)吃掉敵方魚后,我方魚體積增加
public class MyFish {
//......
//繪制自身的方法
public void paintSelf(Graphics g) {
logic();
g.drawImage(img, x, y, width + GameUtils.count, height + GameUtils.count, null);
}
//獲取自身矩形的方法
public Rectangle getRec() {
return new Rectangle(x, y, width + GameUtils.count, height + GameUtils.count);
}
//......
}

12.關(guān)卡的設(shè)置
根據(jù)積分來實現(xiàn)關(guān)卡的設(shè)置,如果達到目標(biāo)積分則過關(guān),并增加我方魚的等級
在工具類中定義關(guān)卡等級
//關(guān)卡等級 static int level = 0;
在窗口類的 logic() 方法中設(shè)置關(guān)卡
public class GameWin extends JFrame {
//......
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
switch (state) {
case 0:
//......
break;
case 1:
//......
break;
case 2:
break;
case 3: //玩家勝利
bg.paintSelf(gImage);
myFish.paintSelf(gImage);
gImage.setColor(Color.ORANGE);
gImage.setFont(new Font("仿宋", Font.BOLD, 80));
gImage.drawString("積分: " + GameUtils.count, 200, 120);
gImage.drawString("勝利", 400, 500);
break;
case 4:
break;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
//批量添加魚類
void logic() {
//關(guān)卡難度
if (GameUtils.count < 5) {
GameUtils.level = 0;
myFish.level = 1;
} else if (GameUtils.count <= 15) {
GameUtils.level = 1;
} else if (GameUtils.count <= 50) {
GameUtils.level = 2;
myFish.level = 2;
} else if (GameUtils.count <= 150) {
GameUtils.level = 3;
myFish.level = 3;
} else if (GameUtils.count <= 300) {
GameUtils.level = 4;
myFish.level = 3;
} else { //分?jǐn)?shù)大于300,玩家勝利
state = 3;
}
//......
}
}

13.界面優(yōu)化
實現(xiàn)游戲界面積分、難度、關(guān)卡的可視化編寫
在工具類中定義繪制文字的方法
//繪制文字的方法
public static void drawWord(Graphics g, String str, Color color, int size, int x, int y) {
g.setColor(color);
g.setFont(new Font("仿宋", Font.BOLD, size));
g.drawString(str, x, y);
}
在背景類中讓積分、難度、我方魚等級可視化
public class Bg {
void paintSelf(Graphics g, int fishLevel) {
g.drawImage(GameUtils.bgimg, 0, 0, null);
switch (GameWin.state) {
case 0:
GameUtils.drawWord(g, "請點擊開始游戲", Color.RED, 80, 500, 500);
break;
case 1:
GameUtils.drawWord(g, "積分:" + GameUtils.count, Color.ORANGE, 50, 200, 120);
GameUtils.drawWord(g, "難度:" + GameUtils.level, Color.ORANGE, 50, 600, 120);
GameUtils.drawWord(g, "等級:" + fishLevel, Color.ORANGE, 50, 1000, 120);
break;
case 2:
break;
case 3:
GameUtils.drawWord(g, "積分:" + GameUtils.count, Color.ORANGE, 50, 200, 120);
GameUtils.drawWord(g, "難度:" + GameUtils.level, Color.ORANGE, 50, 600, 120);
GameUtils.drawWord(g, "等級:" + fishLevel, Color.ORANGE, 50, 1000, 120);
GameUtils.drawWord(g, "勝利", Color.RED, 80, 700, 500);
break;
default:
break;
}
}
}對窗口類的 paint 方法進行優(yōu)化
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
bg.paintSelf(gImage, myFish.level); //***********************
//游戲狀態(tài):0未開始,1游戲中,2游戲失敗,3游戲勝利,4游戲暫停,5重新開始
switch (state) {
case 0:
break;
case 1:
myFish.paintSelf(gImage);
logic(); //不斷添加敵方魚類
for (Enamy enamy : GameUtils.EnamyList) {
enamy.paintSelf(gImage);
}
break;
case 2:
break;
case 3: //玩家勝利
myFish.paintSelf(gImage);
break;
case 4:
break;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
14.右側(cè)敵方魚和多種敵方魚的生成
首先實現(xiàn)右側(cè)敵方小魚的生成,在工具類中添加右側(cè)敵方小魚的圖
//敵方魚類
public static Image enamy_l_img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish1_r.gif");
public static Image enamy_r_img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish1_l.gif");
在敵方魚類中創(chuàng)建敵方小魚右類繼承敵方小魚左類
//右敵方魚類
class Enamy_1_R extends Enamy_1_L {
Enamy_1_R() {
this.x = 1400;
dir = -1;
this.img = GameUtils.enamy_r_img;
}
}
在窗口類中定義隨機數(shù),使左右敵方小魚隨機出現(xiàn)
public class GameWin extends JFrame {
//......
//定義一個隨機數(shù),以此讓左右魚的數(shù)量隨機
double random;
//......
random = Math.random();
//每重繪10次生成一條敵方魚類
if (time % 10 == 0) {
if (random > 0.5) {
enamy = new Enamy_1_L();
} else {
enamy = new Enamy_1_R();
}
GameUtils.EnamyList.add(enamy);
}
//......
}

接下來是其他敵方魚類的生成,其原理與敵方小魚的生成原理一致
在窗口類添加敵方魚圖
//敵方魚類
public static Image enamy_l_img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish1_r.gif");
public static Image enamy_r_img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish1_l.gif");
public static Image enamy_l_2img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish2_r.png");
public static Image enamy_r_2img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish2_l.png");
public static Image enamy_l_3img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish3_r.png");
public static Image enamy_r_3img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish3_l.png");
創(chuàng)建剩下的敵方魚類并寫入?yún)?shù)
class Enamy_2_L extends Enamy {
Enamy_2_L() {
this.x = -100;
this.y = (int) (Math.random() * 700 + 100);
this.width = 100;
this.height = 100;
this.speed = 5;
this.count = 2;
this.type = 2;
this.img = GameUtils.enamy_l_2img;
}
}
class Enamy_2_R extends Enamy_2_L {
Enamy_2_R() {
this.x = 1400;
dir = -1;
this.img = GameUtils.enamy_r_2img;
}
}
class Enamy_3_L extends Enamy {
Enamy_3_L() {
this.x = -300;
this.y = (int) (Math.random() * 700 + 100);
this.width = 300;
this.height = 150;
this.speed = 15;
this.count = 5;
this.type = 3;
this.img = GameUtils.enamy_l_3img;
}
//由于第3種魚的體積過大,我們需要將其修改一下
public Rectangle getRec() {
return new Rectangle(x + 40, y + 30, width - 80, height - 60);
}
}
class Enamy_3_R extends Enamy_3_L {
Enamy_3_R() {
this.x = 1400;
dir = -1;
this.img = GameUtils.enamy_r_3img;
}
}
在窗口類種添加敵方魚的生成,添加我方魚和敵方魚的等級比較來判斷游戲是否失敗,同時游戲失敗界面只剩下敵方魚類
public class GameWin extends JFrame {
//......
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
bg.paintSelf(gImage, myFish.level);
//游戲狀態(tài):0未開始,1游戲中,2游戲失敗,3游戲勝利,4游戲暫停,5重新開始
switch (state) {
case 0:
break;
case 1:
myFish.paintSelf(gImage);
logic(); //不斷添加敵方魚類
for (Enamy enamy : GameUtils.EnamyList) {
enamy.paintSelf(gImage);
}
break;
case 2:
//*********游戲失敗界面*********
logic(); //不斷添加敵方魚類
for (Enamy enamy : GameUtils.EnamyList) {
enamy.paintSelf(gImage);
}
break;
case 3: //玩家勝利
myFish.paintSelf(gImage);
break;
case 4:
break;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
//批量添加魚類
void logic() {
//......
random = Math.random();
//********根據(jù)游戲等級生成魚類********
switch (GameUtils.level) {
case 4:
case 3:
case 2:
if (time % 30 == 0) {
if (random > 0.5) {
enamy = new Enamy_3_L();
} else {
enamy = new Enamy_3_R();
}
GameUtils.EnamyList.add(enamy);
}
case 1:
if (time % 20 == 0) {
if (random > 0.5) {
enamy = new Enamy_2_L();
} else {
enamy = new Enamy_2_R();
}
GameUtils.EnamyList.add(enamy);
}
case 0:
//每重繪10次生成一條敵方魚類
if (time % 10 == 0) {
if (random > 0.5) {
enamy = new Enamy_1_L();
} else {
enamy = new Enamy_1_R();
}
GameUtils.EnamyList.add(enamy);
}
break;
default:
break;
}
//移動方向
for (Enamy enamy : GameUtils.EnamyList) {
enamy.x = enamy.x + enamy.dir * enamy.speed;
//我方魚與敵方魚的碰撞檢測
if (myFish.getRec().intersects(enamy.getRec())) {
//********如果我方魚的等級大于等于敵方魚*******
if (myFish.level >= enamy.type) {
enamy.x = -200;
enamy.y = -200;
//得分
GameUtils.count += enamy.count;
} else {
state = 2;
}
}
}
}
}
背景類優(yōu)化:
public class Bg {
void paintSelf(Graphics g, int fishLevel) {
g.drawImage(GameUtils.bgimg, 0, 0, null);
switch (GameWin.state) {
case 0 -> GameUtils.drawWord(g, "請點擊開始游戲", Color.RED, 80, 500, 500);
case 1 -> {
GameUtils.drawWord(g, "積分:" + GameUtils.count, Color.ORANGE, 50, 200, 120);
GameUtils.drawWord(g, "難度:" + GameUtils.level, Color.ORANGE, 50, 600, 120);
GameUtils.drawWord(g, "等級:" + fishLevel, Color.ORANGE, 50, 1000, 120);
}
case 2 -> {
GameUtils.drawWord(g, "積分:" + GameUtils.count, Color.ORANGE, 50, 200, 120);
GameUtils.drawWord(g, "難度:" + GameUtils.level, Color.ORANGE, 50, 600, 120);
GameUtils.drawWord(g, "等級:" + fishLevel, Color.ORANGE, 50, 1000, 120);
GameUtils.drawWord(g, "失敗", Color.RED, 80, 600, 500);
}
case 3 -> {
GameUtils.drawWord(g, "積分:" + GameUtils.count, Color.ORANGE, 50, 200, 120);
GameUtils.drawWord(g, "難度:" + GameUtils.level, Color.ORANGE, 50, 600, 120);
GameUtils.drawWord(g, "等級:" + fishLevel, Color.ORANGE, 50, 1000, 120);
GameUtils.drawWord(g, "勝利", Color.RED, 80, 700, 500);
}
default -> {
}
}
}
}
15.boss魚的添加
在工具類中添加敵方 boss 魚
public static Image boss_img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\boss.gif");
創(chuàng)建敵方 boss 魚類繼承父類
class Enamy_Boss extends Enamy {
Enamy_Boss() {
this.x = -1000;
this.y = (int) (Math.random()*700 + 100);
this.width = 340;
this.height = 340;
this.speed = 100;
this.count = 0;
this.type = 10;
this.img = GameUtils.boss_img;
}
}
在窗口類中獲取敵方 boss 魚類,在游戲等級為 4 的代碼塊中,添加 boss 生成的條件,同時對 boss 和我方魚類及其他魚類碰撞的情況做出處理
public class GameWin extends JFrame {
//......
//敵方boss類
Enamy boss;
//是否生成boss
boolean isboss = false;
//創(chuàng)建一個啟動窗口,設(shè)置窗口信息
public void launch() throws InterruptedException {
//......
}
@Override
public void paint(Graphics g) {
//懶加載模式初始化對象
offScreenImage = createImage(width, height);
Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對應(yīng)的畫筆對象
bg.paintSelf(gImage, myFish.level);
//游戲狀態(tài):0未開始,1游戲中,2游戲失敗,3游戲勝利,4游戲暫停,5重新開始
switch (state) {
case 0:
break;
case 1:
myFish.paintSelf(gImage);
logic(); //不斷添加敵方魚類
for (Enamy enamy : GameUtils.EnamyList) {
enamy.paintSelf(gImage);
}
//********boss魚的生成********
if (isboss) {
boss.x += boss.dir * boss.speed;
boss.paintSelf(gImage);
if (boss.x < 0) {
gImage.setColor(Color.RED);
gImage.fillRect(boss.x, boss.y, 2400, boss.height / 30);
}
}
break;
case 2:
logic(); //不斷添加敵方魚類
for (Enamy enamy : GameUtils.EnamyList) {
enamy.paintSelf(gImage);
}
//********添加敵方boss********
if (isboss) {
boss.paintSelf(gImage);
}
break;
case 3: //玩家勝利
myFish.paintSelf(gImage);
break;
case 4:
break;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
//批量添加魚類
void logic() {
//關(guān)卡難度
if (GameUtils.count < 5) {
GameUtils.level = 0;
myFish.level = 1;
} else if (GameUtils.count <= 15) {
GameUtils.level = 1;
} else if (GameUtils.count <= 50) {
GameUtils.level = 2;
myFish.level = 2;
} else if (GameUtils.count <= 150) {
GameUtils.level = 3;
myFish.level = 3;
} else if (GameUtils.count <= 300) {
GameUtils.level = 4;
myFish.level = 3;
} else { //分?jǐn)?shù)大于300,玩家勝利
state = 3;
}
random = Math.random();
switch (GameUtils.level) {
case 4:
//********判斷是否生成boss********
if (time % 60 == 0) {
if (random > 0) {
boss = new Enamy_Boss();
isboss = true;
}
}
case 3:
case 2:
//......
case 1:
//......
case 0:
//......
default:
break;
}
//移動方向
for (Enamy enamy : GameUtils.EnamyList) {
enamy.x = enamy.x + enamy.dir * enamy.speed;
//********boss魚的碰撞處理********
if (isboss) {
if (boss.getRec().intersects(enamy.getRec())) {
enamy.x = -200;
enamy.y = -200;
}
if (boss.getRec().intersects(myFish.getRec())) {
state = 2;
}
}
//......
}
}
}

16.游戲暫停功能和重新開始功能的實現(xiàn)
首先實現(xiàn)暫停功能,如果游戲狀態(tài)為 4,則游戲暫停,我們使用空格鍵來實現(xiàn)游戲暫停功能
在 launch 方法中添加鍵盤的監(jiān)聽事件
public class GameWin extends JFrame {
//......
//創(chuàng)建一個啟動窗口,設(shè)置窗口信息
public void launch() throws InterruptedException {
//......
//鍵盤移動
this.addKeyListener(new KeyAdapter() {
@Override//按壓
public void keyPressed(KeyEvent e) {
//上下左右四個鍵的ASCII值為 上 ↑:38下 ↓: 40左 ←: 37右 →: 39
if (e.getKeyCode() == 38) {
GameUtils.UP = true;
}
if (e.getKeyCode() == 40) {
GameUtils.DOWN = true;
}
if (e.getKeyCode() == 37) {
GameUtils.LEFT = true;
}
if (e.getKeyCode() == 39) {
GameUtils.RIGHT = true;
}
//********空格鍵********
if (e.getKeyCode() == 32) {
//如果游戲狀態(tài)為運行中,則暫停,反之
switch (state) {
case 1:
state = 4;
GameUtils.drawWord(getGraphics(), "游戲暫停!!!", Color.RED, 50, 600, 400);
break;
case 4:
state = 1;
break;
}
}
}
@Override//抬起
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 38) {
GameUtils.UP = false;
}
if (e.getKeyCode() == 40) {
GameUtils.DOWN = false;
}
if (e.getKeyCode() == 37) {
GameUtils.LEFT = false;
}
if (e.getKeyCode() == 39) {
GameUtils.RIGHT = false;
}
}
});
//背景圖片重復(fù)使用,需要重復(fù)調(diào)用repaint方法
while (true) {
repaint();
time++;
Thread.sleep(40);
}
}
@Override
public void paint(Graphics g) {
//......
case 4:
//********
return;
default:
break;
}
//將繪制好的圖片一次性繪制到主窗口中
g.drawImage(offScreenImage, 0, 0, null);
}
//批量添加魚類
......
}

接下來是重新開始功能,在窗口類中創(chuàng)建一個新的方法,將游戲狀態(tài)恢復(fù)到初始,然后定義在游戲狀態(tài)為結(jié)束或勝利的情況下,點擊鼠標(biāo)左鍵進入重新開始界面
//重新開始
void reGame() {
GameUtils.EnamyList.clear();
time =0;
myFish.level = 0;
GameUtils.count = 0;
myFish.x = 700;
myFish.y = 500;
myFish.width = 50;
myFish.height = 50;
boss = null;
isboss = false;
}
//創(chuàng)建一個啟動窗口,設(shè)置窗口信息
public void launch() throws InterruptedException {
//......
//添加鼠標(biāo)監(jiān)聽事件
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == 1 && state == 0) {
state = 1;
repaint();
}
//重新開始
if (e.getButton() == 1 && (state == 2 || state == 3)) {
reGame();
state = 1;
}
}
});
//鍵盤移動
//......
}
以上就是Java實現(xiàn)經(jīng)典游戲之大魚吃小魚的詳細(xì)內(nèi)容,更多關(guān)于Java大魚吃小魚的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
idea的easyCode的 MybatisPlus模板的配置詳解
這篇文章主要介紹了idea的easyCode的 MybatisPlus模板的配置詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
java加載properties文件的六種方法總結(jié)
這篇文章主要介紹了java加載properties文件的六種方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-05-05
Java中一個線程執(zhí)行死循環(huán)有什么后果
這篇文章主要為大家詳細(xì)介紹了Java中一個線程執(zhí)行死循環(huán)有什么后果,當(dāng)一個線程在執(zhí)行死循環(huán)時會影響另外一個線程嗎,下面為大家揭曉2016-05-05
Springboot中登錄后關(guān)于cookie和session攔截問題的案例分析
這篇文章主要介紹了Springboot中登錄后關(guān)于cookie和session攔截案例,本文通過實例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08

