Java實(shí)現(xiàn)經(jīng)典游戲之大魚(yú)吃小魚(yú)
一、項(xiàng)目演示
二、項(xiàng)目實(shí)現(xiàn)
1.創(chuàng)建游戲窗口
創(chuàng)建一個(gè)游戲窗口類 GameWin,創(chuàng)建一個(gè) launch() 啟動(dòng)方法,在其中設(shè)置窗口相關(guān)屬性:
import javax.swing.*; public class GameWin extends JFrame { int width = 1440; int height = 900; //創(chuàng)建一個(gè)啟動(dòng)方法,設(shè)置窗口信息 public void launch() { this.setVisible(true); //設(shè)置窗口可見(jiàn) this.setSize(width, height); //設(shè)置窗口大小 this.setLocationRelativeTo(null); //設(shè)置窗口居中 this.setResizable(false); //設(shè)置窗口大小不可改變 this.setTitle("大魚(yú)吃小魚(yú)"); //設(shè)置窗口標(biāo)題 this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕 } }
創(chuàng)建一個(gè)游戲窗口測(cè)試類 GameWinDemo,進(jìn)行窗口對(duì)象的創(chuàng)建和啟動(dòng)方法的調(diào)用:
public class GameWinDemo { public static void main(String[] args) { //創(chuàng)建一個(gè)游戲窗口對(duì)象 GameWin gameWin = new GameWin(); //啟動(dòng)窗口 gameWin.launch(); } }
2.添加背景圖片
將下載的背景圖片的文件夾復(fù)制到項(xiàng)目文件夾中:
創(chuàng)建一個(gè)工具類 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) { //用畫(huà)筆繪制背景圖片 g.drawImage(GameUtils.bgimg, 0, 0, null); }
如果發(fā)現(xiàn)背景圖片加載不出來(lái),先檢查一下路徑有沒(méi)有寫(xiě)錯(cuò),相對(duì)路徑不行的話就試試絕對(duì)路徑;有時(shí)候加載背景圖片會(huì)較慢,可以試著將運(yùn)行出來(lái)的窗口最小化后再恢復(fù)
3.制作封面
定義默認(rèn)游戲狀態(tài),同時(shí)在 paint 方法中對(duì)游戲狀態(tài)進(jìn)行判斷進(jìn)而顯示對(duì)應(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)建一個(gè)啟動(dòng)方法,設(shè)置窗口信息 public void launch() { this.setVisible(true); //設(shè)置窗口可見(jiàn) this.setSize(width, height); //設(shè)置窗口大小 this.setLocationRelativeTo(null); //設(shè)置窗口居中 this.setResizable(false); //設(shè)置窗口大小不可改變 this.setTitle("大魚(yú)吃小魚(yú)"); //設(shè)置窗口標(biāo)題 this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕 } @Override public void paint(Graphics g) { //用畫(huà)筆繪制背景圖片 g.drawImage(GameUtils.bgimg, 0, 0, null); //游戲狀態(tài):0未開(kāi)始,1游戲中,2游戲失敗,3游戲勝利,4游戲暫停,5重新開(kāi)始 switch (state) { case 0: g.drawImage(GameUtils.bgimg, 0, 0, null); //為啟動(dòng)頁(yè)面添加文字 g.setColor(Color.pink); g.setFont(new Font("仿宋", Font.BOLD, 60)); g.drawString("請(qǐng)點(diǎn)擊開(kāi)始游戲", 600, 500); break; case 1: break; case 2: break; case 3: break; case 4: break; default: break; } } }
4.啟動(dòng)頁(yè)面的點(diǎn)擊事件
在啟動(dòng)方法中添加鼠標(biāo)監(jiān)聽(tīng)器,當(dāng)點(diǎn)擊啟動(dòng)頁(yè)面時(shí)游戲開(kāi)始
//創(chuàng)建一個(gè)啟動(dòng)方法,設(shè)置窗口信息 public void launch() { this.setVisible(true); //設(shè)置窗口可見(jiàn) this.setSize(width, height); //設(shè)置窗口大小 this.setLocationRelativeTo(null); //設(shè)置窗口居中 this.setResizable(false); //設(shè)置窗口大小不可改變 this.setTitle("大魚(yú)吃小魚(yú)"); //設(shè)置窗口標(biāo)題 this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕 //添加鼠標(biāo)監(jiān)聽(tīng)事件 this.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == 1 && state == 0) { state = 1; repaint(); } } }); }
5.游戲開(kāi)始時(shí)的背景添加
新建一個(gè)背景圖的實(shí)體類 Bg,在 GameWin 類里創(chuàng)建其對(duì)象,并在 case 1 中調(diào)用繪制背景圖的方法
import java.awt.*; public class Bg { void paintSelf(Graphics g) { g.drawImage(GameUtils.bgimg, 0, 0, null); } }
由于游戲中需要不斷繪制背景圖片,所以我們?cè)趩?dòng)方法中添加一個(gè) 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; //獲取背景圖類的對(duì)象 Bg bg = new Bg(); //創(chuàng)建一個(gè)啟動(dòng)窗口,設(shè)置窗口信息 public void launch() throws InterruptedException { this.setVisible(true); //設(shè)置窗口可見(jiàn) this.setSize(width, height); //設(shè)置窗口大小 this.setLocationRelativeTo(null); //設(shè)置窗口居中 this.setResizable(false); //設(shè)置窗口大小不可改變 this.setTitle("大魚(yú)吃小魚(yú)"); //設(shè)置窗口標(biāo)題 this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕 //添加鼠標(biāo)監(jiān)聽(tīng)事件 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) { //用畫(huà)筆繪制背景圖片 g.drawImage(GameUtils.bgimg, 0, 0, null); switch (state) { case 0: g.drawImage(GameUtils.bgimg, 0, 0, null); //為啟動(dòng)頁(yè)面添加文字 g.setColor(Color.pink); g.setFont(new Font("仿宋", Font.BOLD, 60)); g.drawString("請(qǐng)點(diǎn)擊開(kāi)始游戲", 500, 500); break; case 1: bg.paintSelf(g); break; case 2: break; case 3: break; case 4: break; default: break; } } }
6.雙緩存解決閃屏問(wèn)題
如果此時(shí)你的屏幕出現(xiàn)閃爍的情況,可以用下面的方法解決
整體思路為:
重新創(chuàng)建一個(gè)空的圖片,將所有的組件先繪制到空的圖片上,然后把繪制好的圖片一次性地繪制到主窗口上
創(chuàng)建一個(gè)空?qǐng)D片對(duì)象,在 paint 方法中將所有組件繪制到空?qǐng)D片上,再一次性繪制到主窗口上
public class GameWin extends JFrame { int width = 1440; int height = 900; //定義游戲默認(rèn)狀態(tài) static int state = 0; //獲取背景圖類的對(duì)象 Bg bg = new Bg(); //創(chuàng)建一個(gè)空?qǐng)D片對(duì)象 Image offScreenImage; //創(chuàng)建一個(gè)啟動(dòng)窗口,設(shè)置窗口信息 public void launch() throws InterruptedException { this.setVisible(true); //設(shè)置窗口可見(jiàn) this.setSize(width, height); //設(shè)置窗口大小 this.setLocationRelativeTo(null); //設(shè)置窗口居中 this.setResizable(false); //設(shè)置窗口大小不可改變 this.setTitle("大魚(yú)吃小魚(yú)"); //設(shè)置窗口標(biāo)題 this.setDefaultCloseOperation(EXIT_ON_CLOSE); //設(shè)置窗口按鈕 //添加鼠標(biāo)監(jiān)聽(tīng)事件 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) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 switch (state) { case 0: //把組件重新繪制到主窗口中 gImage.drawImage(GameUtils.bgimg, 0, 0, null); //為啟動(dòng)頁(yè)面添加文字 gImage.setColor(Color.pink); gImage.setFont(new Font("仿宋", Font.BOLD, 60)); gImage.drawString("請(qǐng)點(diǎn)擊開(kāi)始游戲", 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.敵方第一條小雨的添加
新建敵方魚(yú)的父類 Enamy,編寫(xiě)左敵方魚(yú)類 Enamy_1_L 繼承父類
import java.awt.*; public class Enamy { //定義圖片 Image img; //定義物體坐標(biāo) int x; int y; int width; int height; //移動(dòng)速度 int speed; //方向 int dir = 1; //敵方魚(yú)的類型、分值 int type; int count; //繪制自身方法 public void paintSelf(Graphics g) { g.drawImage(img, x, y, width, height, null); } //獲取自身矩形用于碰撞檢測(cè) public Rectangle getRec() { return new Rectangle(x, y, width, height); } } //左敵方魚(yú)類 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 類中添加左敵方魚(yú)類的圖片
public class GameUtils { //背景圖 public static Image bgimg = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\sea.jpg"); //敵方魚(yú)類 public static Image enamy_l_img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\fish1_r.gif"); }
在 GameWin 類中創(chuàng)建左敵方魚(yú)類對(duì)象并在 case 1 的情況下繪制左敵方魚(yú)類
public class GameWin extends JFrame { //...... //敵方魚(yú)類 Enamy enamy = new Enamy_1_L(); //創(chuàng)建一個(gè)啟動(dòng)窗口,設(shè)置窗口信息 public void launch() throws InterruptedException { //...... } @Override public void paint(Graphics g) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 switch (state) { case 0: //把組件重新繪制到主窗口中 gImage.drawImage(GameUtils.bgimg, 0, 0, null); //為啟動(dòng)頁(yè)面添加文字 gImage.setColor(Color.pink); gImage.setFont(new Font("仿宋", Font.BOLD, 60)); gImage.drawString("請(qǐng)點(diǎn)擊開(kāi)始游戲", 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.敵方左方小魚(yú)的批量添加
在工具類中創(chuàng)建一個(gè)所有敵方魚(yú)物體的集合
public class GameUtils { //敵方魚(yú)類集合 public static List<Enamy> EnamyList = new ArrayList<>(); //...... }
在窗口類中添加一個(gè)方法,用于批量添加敵方魚(yú)類
public class GameWin extends JFrame { //...... //敵方魚(yú)類 Enamy enamy; //計(jì)數(shù)器,用來(lái)記錄游戲的重繪次數(shù),也是魚(yú)生成的數(shù)量 int time = 0; //創(chuàng)建一個(gè)啟動(dò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) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 switch (state) { case 0: //...... case 1: bg.paintSelf(gImage); logic(); //不斷添加敵方魚(yú)類 //遍歷敵方魚(yú)類集合,繪制敵方魚(yú)類 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); } //批量添加魚(yú)類 void logic() { //每重繪10次生成一條敵方魚(yú)類 if (time % 10 == 0) { enamy = new Enamy_1_L(); GameUtils.EnamyList.add(enamy); } //移動(dòng)方向 for (Enamy enamy : GameUtils.EnamyList) { enamy.x = enamy.x + enamy.dir * enamy.speed; } } }
9.我方魚(yú)的生成
在工具類中添加我方魚(yú)類的圖片
public class GameUtils { //...... //我方魚(yú)類 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)建我方魚(yú)類
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; //移動(dòng)速度 int speed = 20; //等級(jí) 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); } }
在窗口類中獲取我方魚(yú)的對(duì)象
//我方魚(yú)類 MyFish myFish = new MyFish();
在工具類中添加方向判定,用來(lái)控制我方魚(yú)的方向
public class GameUtils { //方向 static boolean UP = false; static boolean DOWN = false; static boolean LEFT = false; static boolean RIGHT = false; //...... }
在我方魚(yú)類添加一個(gè)方法,實(shí)現(xiàn)對(duì)鍵盤(pá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 中,添加我方魚(yú)類所創(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 { //...... //我方魚(yú)類 MyFish myFish = new MyFish(); //創(chuàng)建一個(gè)啟動(dòng)窗口,設(shè)置窗口信息 public void launch() throws InterruptedException { //...... }); //鍵盤(pán)移動(dòng) this.addKeyListener(new KeyAdapter() { @Override//按壓 public void keyPressed(KeyEvent e) { //上下左右四個(gè)鍵的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) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 switch (state) { case 0: //...... break; case 1: bg.paintSelf(gImage); myFish.paintSelf(gImage); logic(); //不斷添加敵方魚(yú)類 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); } //批量添加魚(yú)類 void logic() { //...... } }
10.我方魚(yú)與敵方小魚(yú)的碰撞測(cè)試
在游戲中所有物體被視為矩形,兩個(gè)物體是否碰撞,即檢測(cè)兩個(gè)物體的所在矩形是否有重疊部分
具體檢測(cè),就是讓我方魚(yú)與敵方魚(yú)的矩形進(jìn)行一一檢測(cè),在窗口類的 logic() 方法中實(shí)現(xiàn)
//批量添加魚(yú)類 void logic() { //每重繪10次生成一條敵方魚(yú)類 if (time % 10 == 0) { enamy = new Enamy_1_L(); GameUtils.EnamyList.add(enamy); } //移動(dòng)方向 for (Enamy enamy : GameUtils.EnamyList) { enamy.x = enamy.x + enamy.dir * enamy.speed; //我方魚(yú)與敵方魚(yú)的碰撞檢測(cè) if (myFish.getRec().intersects(enamy.getRec())) { enamy.x = -200; enamy.y = -200; } } }
11.游戲積分的實(shí)現(xiàn)
在工具類中定義分?jǐn)?shù)
//分?jǐn)?shù) static int count = 0;
在窗口類中實(shí)現(xiàn)我方魚(yú)吃掉敵方魚(yú)增加分?jǐn)?shù),同時(shí)在頁(yè)面上打印分?jǐn)?shù)
public class GameWin extends JFrame { //...... @Override public void paint(Graphics g) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 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(); //不斷添加敵方魚(yú)類 for (Enamy enamy : GameUtils.EnamyList) { enamy.paintSelf(gImage); } break; case 2: break; case 3: break; case 4: break; default: break; } //...... //批量添加魚(yú)類 void logic() { //...... //移動(dòng)方向 for (Enamy enamy : GameUtils.EnamyList) { enamy.x = enamy.x + enamy.dir * enamy.speed; //我方魚(yú)與敵方魚(yú)的碰撞檢測(cè) if (myFish.getRec().intersects(enamy.getRec())) { enamy.x = -200; enamy.y = -200; //********得分******** GameUtils.count += enamy.count; } } } }
在我方魚(yú)類中實(shí)現(xiàn)吃掉敵方魚(yú)后,我方魚(yú)體積增加
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ù)積分來(lái)實(shí)現(xiàn)關(guān)卡的設(shè)置,如果達(dá)到目標(biāo)積分則過(guò)關(guān),并增加我方魚(yú)的等級(jí)
在工具類中定義關(guān)卡等級(jí)
//關(guān)卡等級(jí) static int level = 0;
在窗口類的 logic() 方法中設(shè)置關(guān)卡
public class GameWin extends JFrame { //...... @Override public void paint(Graphics g) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 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); } //批量添加魚(yú)類 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)化
實(shí)現(xiàn)游戲界面積分、難度、關(guān)卡的可視化編寫(xiě)
在工具類中定義繪制文字的方法
//繪制文字的方法 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); }
在背景類中讓積分、難度、我方魚(yú)等級(jí)可視化
public class Bg { void paintSelf(Graphics g, int fishLevel) { g.drawImage(GameUtils.bgimg, 0, 0, null); switch (GameWin.state) { case 0: GameUtils.drawWord(g, "請(qǐng)點(diǎn)擊開(kāi)始游戲", 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, "等級(jí):" + 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, "等級(jí):" + fishLevel, Color.ORANGE, 50, 1000, 120); GameUtils.drawWord(g, "勝利", Color.RED, 80, 700, 500); break; default: break; } } }
對(duì)窗口類的 paint 方法進(jìn)行優(yōu)化
@Override public void paint(Graphics g) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 bg.paintSelf(gImage, myFish.level); //*********************** //游戲狀態(tài):0未開(kāi)始,1游戲中,2游戲失敗,3游戲勝利,4游戲暫停,5重新開(kāi)始 switch (state) { case 0: break; case 1: myFish.paintSelf(gImage); logic(); //不斷添加敵方魚(yú)類 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è)敵方魚(yú)和多種敵方魚(yú)的生成
首先實(shí)現(xiàn)右側(cè)敵方小魚(yú)的生成,在工具類中添加右側(cè)敵方小魚(yú)的圖
//敵方魚(yú)類 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");
在敵方魚(yú)類中創(chuàng)建敵方小魚(yú)右類繼承敵方小魚(yú)左類
//右敵方魚(yú)類 class Enamy_1_R extends Enamy_1_L { Enamy_1_R() { this.x = 1400; dir = -1; this.img = GameUtils.enamy_r_img; } }
在窗口類中定義隨機(jī)數(shù),使左右敵方小魚(yú)隨機(jī)出現(xiàn)
public class GameWin extends JFrame { //...... //定義一個(gè)隨機(jī)數(shù),以此讓左右魚(yú)的數(shù)量隨機(jī) double random; //...... random = Math.random(); //每重繪10次生成一條敵方魚(yú)類 if (time % 10 == 0) { if (random > 0.5) { enamy = new Enamy_1_L(); } else { enamy = new Enamy_1_R(); } GameUtils.EnamyList.add(enamy); } //...... }
接下來(lái)是其他敵方魚(yú)類的生成,其原理與敵方小魚(yú)的生成原理一致
在窗口類添加敵方魚(yú)圖
//敵方魚(yú)類 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ú)類并寫(xiě)入?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種魚(yú)的體積過(guò)大,我們需要將其修改一下 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; } }
在窗口類種添加敵方魚(yú)的生成,添加我方魚(yú)和敵方魚(yú)的等級(jí)比較來(lái)判斷游戲是否失敗,同時(shí)游戲失敗界面只剩下敵方魚(yú)類
public class GameWin extends JFrame { //...... @Override public void paint(Graphics g) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 bg.paintSelf(gImage, myFish.level); //游戲狀態(tài):0未開(kāi)始,1游戲中,2游戲失敗,3游戲勝利,4游戲暫停,5重新開(kāi)始 switch (state) { case 0: break; case 1: myFish.paintSelf(gImage); logic(); //不斷添加敵方魚(yú)類 for (Enamy enamy : GameUtils.EnamyList) { enamy.paintSelf(gImage); } break; case 2: //*********游戲失敗界面********* logic(); //不斷添加敵方魚(yú)類 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); } //批量添加魚(yú)類 void logic() { //...... random = Math.random(); //********根據(jù)游戲等級(jí)生成魚(yú)類******** 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次生成一條敵方魚(yú)類 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; } //移動(dòng)方向 for (Enamy enamy : GameUtils.EnamyList) { enamy.x = enamy.x + enamy.dir * enamy.speed; //我方魚(yú)與敵方魚(yú)的碰撞檢測(cè) if (myFish.getRec().intersects(enamy.getRec())) { //********如果我方魚(yú)的等級(jí)大于等于敵方魚(yú)******* 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, "請(qǐng)點(diǎn)擊開(kāi)始游戲", 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, "等級(jí):" + 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, "等級(jí):" + 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, "等級(jí):" + fishLevel, Color.ORANGE, 50, 1000, 120); GameUtils.drawWord(g, "勝利", Color.RED, 80, 700, 500); } default -> { } } } }
15.boss魚(yú)的添加
在工具類中添加敵方 boss 魚(yú)
public static Image boss_img = Toolkit.getDefaultToolkit().createImage("D:\\IDEA\\idea_Demo\\FishGame\\images\\enemyFish\\boss.gif");
創(chuàng)建敵方 boss 魚(yú)類繼承父類
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 魚(yú)類,在游戲等級(jí)為 4 的代碼塊中,添加 boss 生成的條件,同時(shí)對(duì) boss 和我方魚(yú)類及其他魚(yú)類碰撞的情況做出處理
public class GameWin extends JFrame { //...... //敵方boss類 Enamy boss; //是否生成boss boolean isboss = false; //創(chuàng)建一個(gè)啟動(dòng)窗口,設(shè)置窗口信息 public void launch() throws InterruptedException { //...... } @Override public void paint(Graphics g) { //懶加載模式初始化對(duì)象 offScreenImage = createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //獲取圖片對(duì)應(yīng)的畫(huà)筆對(duì)象 bg.paintSelf(gImage, myFish.level); //游戲狀態(tài):0未開(kāi)始,1游戲中,2游戲失敗,3游戲勝利,4游戲暫停,5重新開(kāi)始 switch (state) { case 0: break; case 1: myFish.paintSelf(gImage); logic(); //不斷添加敵方魚(yú)類 for (Enamy enamy : GameUtils.EnamyList) { enamy.paintSelf(gImage); } //********boss魚(yú)的生成******** 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(); //不斷添加敵方魚(yú)類 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); } //批量添加魚(yú)類 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; } //移動(dòng)方向 for (Enamy enamy : GameUtils.EnamyList) { enamy.x = enamy.x + enamy.dir * enamy.speed; //********boss魚(yú)的碰撞處理******** if (isboss) { if (boss.getRec().intersects(enamy.getRec())) { enamy.x = -200; enamy.y = -200; } if (boss.getRec().intersects(myFish.getRec())) { state = 2; } } //...... } } }
16.游戲暫停功能和重新開(kāi)始功能的實(shí)現(xiàn)
首先實(shí)現(xiàn)暫停功能,如果游戲狀態(tài)為 4,則游戲暫停,我們使用空格鍵來(lái)實(shí)現(xiàn)游戲暫停功能
在 launch 方法中添加鍵盤(pán)的監(jiān)聽(tīng)事件
public class GameWin extends JFrame { //...... //創(chuàng)建一個(gè)啟動(dòng)窗口,設(shè)置窗口信息 public void launch() throws InterruptedException { //...... //鍵盤(pán)移動(dòng) this.addKeyListener(new KeyAdapter() { @Override//按壓 public void keyPressed(KeyEvent e) { //上下左右四個(gè)鍵的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)為運(yùn)行中,則暫停,反之 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); } //批量添加魚(yú)類 ...... }
接下來(lái)是重新開(kāi)始功能,在窗口類中創(chuàng)建一個(gè)新的方法,將游戲狀態(tài)恢復(fù)到初始,然后定義在游戲狀態(tài)為結(jié)束或勝利的情況下,點(diǎn)擊鼠標(biāo)左鍵進(jìn)入重新開(kāi)始界面
//重新開(kāi)始 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)建一個(gè)啟動(dòng)窗口,設(shè)置窗口信息 public void launch() throws InterruptedException { //...... //添加鼠標(biāo)監(jiān)聽(tīng)事件 this.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == 1 && state == 0) { state = 1; repaint(); } //重新開(kāi)始 if (e.getButton() == 1 && (state == 2 || state == 3)) { reGame(); state = 1; } } }); //鍵盤(pán)移動(dòng) //...... }
以上就是Java實(shí)現(xiàn)經(jīng)典游戲之大魚(yú)吃小魚(yú)的詳細(xì)內(nèi)容,更多關(guān)于Java大魚(yú)吃小魚(yú)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
idea的easyCode的 MybatisPlus模板的配置詳解
這篇文章主要介紹了idea的easyCode的 MybatisPlus模板的配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09java加載properties文件的六種方法總結(jié)
這篇文章主要介紹了java加載properties文件的六種方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-05-05Java中一個(gè)線程執(zhí)行死循環(huán)有什么后果
這篇文章主要為大家詳細(xì)介紹了Java中一個(gè)線程執(zhí)行死循環(huán)有什么后果,當(dāng)一個(gè)線程在執(zhí)行死循環(huán)時(shí)會(huì)影響另外一個(gè)線程嗎,下面為大家揭曉2016-05-05Springboot中登錄后關(guān)于cookie和session攔截問(wèn)題的案例分析
這篇文章主要介紹了Springboot中登錄后關(guān)于cookie和session攔截案例,本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08