Java实现飞机大战
飛機(jī)大戰(zhàn)詳細(xì)文檔
文末有源代碼,以及本游戲使用的所有素材,將plane2文件復(fù)制在src文件下可以直接運(yùn)行。
實(shí)現(xiàn)效果:
結(jié)構(gòu)設(shè)計(jì)
角色設(shè)計(jì)
- 飛行對(duì)象類 FlyObject
- 戰(zhàn)機(jī)類
- 我的飛機(jī) MyPlane
- 敵方飛機(jī) EnemyPlane
- 子彈類
- 我的子彈 MyBullet
- 敵方子彈 EnemyBullet
- 道具類 Prop
- 加分,加血,升級(jí)
- 戰(zhàn)機(jī)類
- 地圖背景類 Background
- 玩家類 Player
- HP,得分
- 線程類
- 繪制線程 DrawThread
- 移動(dòng)線程 MoveThread
- 生成敵方飛機(jī)線程 EnemyPlaneThread
- 敵方飛機(jī)生成子彈線程 EnemyButtleThread
- 檢測(cè)碰撞線程 TestCrashThread
- 界面類
- 主界面 GameUI
- 選擇地圖界面 SelectMapUI
- 監(jiān)聽器類 KListener
- 通過按壓鍵盤改變我方飛機(jī)的速度
- 數(shù)據(jù)結(jié)構(gòu)
- 我方戰(zhàn)機(jī)(只有一個(gè))
- 我方飛機(jī)子彈集合
- 敵方飛機(jī)集合
- 敵方子彈集合
- 道具集合
詳細(xì)分析
###Main界面類
- 使用邊框布局,給面板分三個(gè)區(qū),如圖所示
- 關(guān)鍵代碼:
繪制背景地圖
###飛行道具類
- UML圖
- 判斷FlyObject對(duì)象是否碰撞
繪制線程: 如何讓我們的游戲動(dòng)起來
視頻原理:我們?cè)谄聊簧峡匆姷膭?dòng)態(tài)圖像圖像實(shí)際上由若干個(gè)靜止圖像構(gòu)成,由于人眼有暫留特性,剛顯示的圖像在大腦中停留一段時(shí)間,若靜態(tài)圖像每
秒鐘變化25幅,那么人的感覺屏幕上的圖像是動(dòng)的。
- 繪制時(shí)要把所有的飛行物都繪制一遍,所以我們需要在每一個(gè)飛行物被創(chuàng)建時(shí),添加到相關(guān)的飛行物集合中。(為了方便傳值,我們將集合設(shè)為靜態(tài)變量)
- 我們的繪制線程,選擇每30ms繪制一次,注意先畫背景,然后再遍歷飛行物集合畫飛行物。
背景的繪制
要想繪制動(dòng)態(tài)的背景,首先我們要先畫一張靜態(tài)的背景圖,那么如何繪制一張靜態(tài)的背景圖呢?
獲取包中的圖片:
String fileName_0 = "src\\plane2\\z_img\\img_bg_0.jpg"; //相對(duì)地址(和絕對(duì)地址區(qū)分開)BufferedImage bufferedImage; bufferedImage = ImageIO.read(new File(fileName_0)); //將文件讀出記錄在bufferedImage中,記得拋出異常g.drawImage(bufferedImage,0,0,null); // 將bufferedImage中的內(nèi)容畫在畫筆g對(duì)應(yīng)的地方我們的地圖是一張可以從上往下無縫滾動(dòng)的圖片,就像是這樣的圖
接下來,如何讓畫出連續(xù)的圖片呢?
在繪制函數(shù)中,有一個(gè)函數(shù)可以完美實(shí)現(xiàn)我們的需求
img – the specified image to be drawn. This method does nothing if img is null.dx1 – the x coordinate of the first corner of the destination rectangle. dy1 – the y coordinate of the first corner of the destination rectangle.dx2 – the x coordinate of the second corner of the destination rectangle.dy2 – the y coordinate of the second corner of the destination rectangle.sx1 – the x coordinate of the first corner of the source rectangle.sy1 – the y coordinate of the first corner of the source rectangle.sx2 – the x coordinate of the second corner of the source rectangle.sy2 – the y coordinate of the second corner of the source rectangle.observer – object to be notified as more of the image is scaled and converted.public abstract boolean drawImage(Image img,int dx1, int dy1, int dx2, int dy2,int sx1, int sy1, int sx2, int sy2,ImageObserver observer);比如說,我們的圖片高度為712個(gè)像素點(diǎn),我們?cè)谙乱粫r(shí)刻,圖片向下移動(dòng)了m個(gè)像素點(diǎn),那么我們就將這張圖片的0 ~ 712-m 部分,繪制到游戲界面的m ~ 712部分,
再將712-m ~ 712 部分繪制到游戲界面的0 ~ m 部分;
接下來,我們就要確定 m 的值,這個(gè)就很簡(jiǎn)單了,在繪制線程中,定義一個(gè)整數(shù)變量m ,每次繪制完 m++ 就可以了。(個(gè)人建議m+=2比較舒服)
/** * @author liTianLu * @Date 2022/5/21 23:33 * @purpose 繪制背景* 提醒: 這里我寫了四種地圖的繪制,后面在選擇地圖時(shí)會(huì)用到。 */ public class BackGround {Graphics g;BufferedImage bufferedImage_1;BufferedImage bufferedImage_2;BufferedImage bufferedImage_3;BufferedImage bufferedImage_4;int w;int h;String fileName_1 = "src\\plane2\\z_img\\img_bg_1.jpg"; //地圖1String fileName_2 = "src\\plane2\\z_img\\img_bg_2.jpg"; //地圖2String fileName_3 = "src\\plane2\\z_img\\img_bg_3.jpg"; //地圖3String fileName_4 = "src\\plane2\\z_img\\img_bg_4.jpg"; //地圖4public BackGround(Graphics g) throws IOException {this.g = g;bufferedImage_1 = ImageIO.read(new File(fileName_1));bufferedImage_2 = ImageIO.read(new File(fileName_2));bufferedImage_3 = ImageIO.read(new File(fileName_3));bufferedImage_4 = ImageIO.read(new File(fileName_4));w = bufferedImage_1.getWidth();h = bufferedImage_1.getHeight();}/*** i : 向下移動(dòng)了i個(gè)像素* num : 用來控制繪制哪一個(gè)地圖*/public void draw(int i , int num){ switch(num){case 1 :g.drawImage(bufferedImage_1,0,i,w,i+h,0,0,w,h,null);g.drawImage(bufferedImage_1,0,0,w,i,0,h-i,w,h,null);break;case 2 :g.drawImage(bufferedImage_2,0,i,w,i+h,0,0,w,h,null);g.drawImage(bufferedImage_2,0,0,w,i,0,h-i,w,h,null);break;case 3 :g.drawImage(bufferedImage_3,0,i,w,i+h,0,0,w,h,null);g.drawImage(bufferedImage_3,0,0,w,i,0,h-i,w,h,null);break;case 4 :g.drawImage(bufferedImage_4,0,i,w,i+h,0,0,w,h,null);g.drawImage(bufferedImage_4,0,0,w,i,0,h-i,w,h,null);break;}}public int getH() {return h;} }- 繪制線程:
我的飛機(jī)的繪制
使用的飛機(jī)素材圖片:
飛機(jī)扇動(dòng)翅膀的原理與視頻的原理相同,不停更換圖片,形成視覺暫留效果
//這里僅使用了三張圖片來回切換,更多的圖片會(huì)有更好的效果 public void draw(int i){ //此處的i是用來控制顯示哪一張圖片的int j = i%30; // 150ms換一張 if (j<10){g.drawImage(plane_img,x,y,x+sizeX,y+sizeY,0,0,sizeX,sizeY,null);}else if(j<20) {g.drawImage(plane_img,x,y,x+sizeX,y+sizeY,0,sizeY,sizeX,2*sizeY,null);}else if(j<30){g.drawImage(plane_img,x,y,x+sizeX,y+sizeY,288,0,424,112,null);}}敵方飛機(jī),敵方子彈等飛行物的繪制原理與MyPlane相同,后面不在贅述。(為了簡(jiǎn)化開發(fā)流程,飛行物可以不”扇動(dòng)翅膀“)
移動(dòng)線程
- 我們已經(jīng)給每個(gè)飛行對(duì)象設(shè)置了X軸移動(dòng)速度和Y軸移動(dòng)速度,所以每次移動(dòng)的時(shí)候,我們只需要遍歷所有的飛行對(duì)象,
然后逐個(gè)移動(dòng)一個(gè)speedX 和 speedY 單位即可。 - 多久移動(dòng)一次呢?和繪制線程的間隔時(shí)間相同就好了,我們都設(shè)為30ms.
- 當(dāng)飛行物飛出屏幕時(shí),將飛行物移出集合,減少計(jì)算機(jī)資源的消耗。
###如何控制我的飛機(jī)移動(dòng)?
- 當(dāng)然是通過鍵盤的 ↑ ↓ ← → 來控制了,我們需要設(shè)置一個(gè)鍵盤監(jiān)聽器給game界面,
- 注意要先使用 game.requestFocus(); 獲取焦點(diǎn),鍵盤監(jiān)聽器才可以使用。
###敵方飛機(jī)線程 : 如何生成敵方飛機(jī)呢?
每隔一段時(shí)間,在游戲面板的頂部,產(chǎn)生一個(gè)敵方飛機(jī)
敵方子彈線程 : 使每一個(gè)敵方飛機(jī)開火
我們?yōu)槊恳粋€(gè)敵方飛機(jī)創(chuàng)建一個(gè)生成子彈的線程,要確定子彈產(chǎn)生的具體位置,就要知道敵方飛機(jī)的位置,所以我們要傳入一個(gè)敵方飛機(jī)對(duì)象給該線程。
public EnemyBulletThread(EnemyPlane enemyPlane){this.enemyPlane = enemyPlane;}@Overridepublic void run() {try {sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}while(enemyPlane.isAlive() ){EnemyBullet enemyBullet = null;int enemyBullet_x = enemyPlane.getX()+25;int enemyBullet_y = enemyPlane.getY()+66;try {enemyBullet = new EnemyBullet(enemyBullet_x,enemyBullet_y);} catch (IOException e) {e.printStackTrace();}enemyBullets.add(enemyBullet);try {sleep(2000+ random.nextInt(2000));} catch (InterruptedException e) {e.printStackTrace();}}}###檢測(cè)碰撞線程 : 在子彈與敵機(jī)碰撞時(shí),移除敵機(jī)
-
此時(shí)我們會(huì)遇到一個(gè)問題,就是在遍歷時(shí),move移動(dòng)線程有可能將其中的一個(gè)飛行物移出集合,會(huì)出現(xiàn)IndexOutOfBoundsException異常
,我們只需要在兩個(gè)線程使用飛行物集合時(shí),加上synchronized關(guān)鍵字,即可解決。 -
MoveThread 遍歷我的子彈集合
- TestCrashThread 檢測(cè)我的子彈與敵方飛機(jī)碰撞
其他功能:顯示玩家hp,掉落道具,得分,升級(jí),更換地圖
顯示hp:每次檢測(cè)到我的飛機(jī)與敵方飛機(jī),敵方子彈碰撞,就減分。減到<=0時(shí),游戲結(jié)束。
- 得分:子彈打到敵方飛機(jī)時(shí),加分,并將當(dāng)前分?jǐn)?shù)通過繪制線程繪制在屏幕上。
- 掉落道具:敵機(jī)消失的時(shí)候,隨機(jī)掉落一個(gè)道具,我的飛機(jī)碰到道具時(shí),回血/加分/升級(jí)
- 升級(jí):我的飛機(jī)初始為1級(jí),最高為3級(jí),等級(jí)改變時(shí),使用switch 根據(jù)等級(jí)改變我的飛機(jī)的子彈發(fā)射方式。
- 更換地圖: 使用一個(gè)新的窗體,設(shè)置幾個(gè)單選按鈕,選擇時(shí)通過監(jiān)聽器,改變地圖的控制變量,從而改變地圖的繪制。
源代碼:鏈接:https://pan.baidu.com/s/1DXIASEHg5JUdqEptoMNImw
提取碼:ltlt
總結(jié)
以上是生活随笔為你收集整理的Java实现飞机大战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 打开CAD图纸转换成dwf格式的文件
- 下一篇: java socket编程聊天室_Jav