Java实现飞机大战
飛機大戰詳細文檔
文末有源代碼,以及本游戲使用的所有素材,將plane2文件復制在src文件下可以直接運行。
實現效果:
結構設計
角色設計
- 飛行對象類 FlyObject
- 戰機類
- 我的飛機 MyPlane
- 敵方飛機 EnemyPlane
- 子彈類
- 我的子彈 MyBullet
- 敵方子彈 EnemyBullet
- 道具類 Prop
- 加分,加血,升級
- 戰機類
- 地圖背景類 Background
- 玩家類 Player
- HP,得分
- 線程類
- 繪制線程 DrawThread
- 移動線程 MoveThread
- 生成敵方飛機線程 EnemyPlaneThread
- 敵方飛機生成子彈線程 EnemyButtleThread
- 檢測碰撞線程 TestCrashThread
- 界面類
- 主界面 GameUI
- 選擇地圖界面 SelectMapUI
- 監聽器類 KListener
- 通過按壓鍵盤改變我方飛機的速度
- 數據結構
- 我方戰機(只有一個)
- 我方飛機子彈集合
- 敵方飛機集合
- 敵方子彈集合
- 道具集合
詳細分析
###Main界面類
- 使用邊框布局,給面板分三個區,如圖所示
- 關鍵代碼:
繪制背景地圖
###飛行道具類
- UML圖
- 判斷FlyObject對象是否碰撞
繪制線程: 如何讓我們的游戲動起來
視頻原理:我們在屏幕上看見的動態圖像圖像實際上由若干個靜止圖像構成,由于人眼有暫留特性,剛顯示的圖像在大腦中停留一段時間,若靜態圖像每
秒鐘變化25幅,那么人的感覺屏幕上的圖像是動的。
- 繪制時要把所有的飛行物都繪制一遍,所以我們需要在每一個飛行物被創建時,添加到相關的飛行物集合中。(為了方便傳值,我們將集合設為靜態變量)
- 我們的繪制線程,選擇每30ms繪制一次,注意先畫背景,然后再遍歷飛行物集合畫飛行物。
背景的繪制
要想繪制動態的背景,首先我們要先畫一張靜態的背景圖,那么如何繪制一張靜態的背景圖呢?
獲取包中的圖片:
String fileName_0 = "src\\plane2\\z_img\\img_bg_0.jpg"; //相對地址(和絕對地址區分開)BufferedImage bufferedImage; bufferedImage = ImageIO.read(new File(fileName_0)); //將文件讀出記錄在bufferedImage中,記得拋出異常g.drawImage(bufferedImage,0,0,null); // 將bufferedImage中的內容畫在畫筆g對應的地方我們的地圖是一張可以從上往下無縫滾動的圖片,就像是這樣的圖
接下來,如何讓畫出連續的圖片呢?
在繪制函數中,有一個函數可以完美實現我們的需求
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個像素點,我們在下一時刻,圖片向下移動了m個像素點,那么我們就將這張圖片的0 ~ 712-m 部分,繪制到游戲界面的m ~ 712部分,
再將712-m ~ 712 部分繪制到游戲界面的0 ~ m 部分;
接下來,我們就要確定 m 的值,這個就很簡單了,在繪制線程中,定義一個整數變量m ,每次繪制完 m++ 就可以了。(個人建議m+=2比較舒服)
/** * @author liTianLu * @Date 2022/5/21 23:33 * @purpose 繪制背景* 提醒: 這里我寫了四種地圖的繪制,后面在選擇地圖時會用到。 */ 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 : 向下移動了i個像素* num : 用來控制繪制哪一個地圖*/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;} }- 繪制線程:
我的飛機的繪制
使用的飛機素材圖片:
飛機扇動翅膀的原理與視頻的原理相同,不停更換圖片,形成視覺暫留效果
//這里僅使用了三張圖片來回切換,更多的圖片會有更好的效果 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);}}敵方飛機,敵方子彈等飛行物的繪制原理與MyPlane相同,后面不在贅述。(為了簡化開發流程,飛行物可以不”扇動翅膀“)
移動線程
- 我們已經給每個飛行對象設置了X軸移動速度和Y軸移動速度,所以每次移動的時候,我們只需要遍歷所有的飛行對象,
然后逐個移動一個speedX 和 speedY 單位即可。 - 多久移動一次呢?和繪制線程的間隔時間相同就好了,我們都設為30ms.
- 當飛行物飛出屏幕時,將飛行物移出集合,減少計算機資源的消耗。
###如何控制我的飛機移動?
- 當然是通過鍵盤的 ↑ ↓ ← → 來控制了,我們需要設置一個鍵盤監聽器給game界面,
- 注意要先使用 game.requestFocus(); 獲取焦點,鍵盤監聽器才可以使用。
###敵方飛機線程 : 如何生成敵方飛機呢?
每隔一段時間,在游戲面板的頂部,產生一個敵方飛機
敵方子彈線程 : 使每一個敵方飛機開火
我們為每一個敵方飛機創建一個生成子彈的線程,要確定子彈產生的具體位置,就要知道敵方飛機的位置,所以我們要傳入一個敵方飛機對象給該線程。
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();}}}###檢測碰撞線程 : 在子彈與敵機碰撞時,移除敵機
-
此時我們會遇到一個問題,就是在遍歷時,move移動線程有可能將其中的一個飛行物移出集合,會出現IndexOutOfBoundsException異常
,我們只需要在兩個線程使用飛行物集合時,加上synchronized關鍵字,即可解決。 -
MoveThread 遍歷我的子彈集合
- TestCrashThread 檢測我的子彈與敵方飛機碰撞
其他功能:顯示玩家hp,掉落道具,得分,升級,更換地圖
顯示hp:每次檢測到我的飛機與敵方飛機,敵方子彈碰撞,就減分。減到<=0時,游戲結束。
- 得分:子彈打到敵方飛機時,加分,并將當前分數通過繪制線程繪制在屏幕上。
- 掉落道具:敵機消失的時候,隨機掉落一個道具,我的飛機碰到道具時,回血/加分/升級
- 升級:我的飛機初始為1級,最高為3級,等級改變時,使用switch 根據等級改變我的飛機的子彈發射方式。
- 更換地圖: 使用一個新的窗體,設置幾個單選按鈕,選擇時通過監聽器,改變地圖的控制變量,從而改變地圖的繪制。
源代碼:鏈接:https://pan.baidu.com/s/1DXIASEHg5JUdqEptoMNImw
提取碼:ltlt
總結
以上是生活随笔為你收集整理的Java实现飞机大战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 打开CAD图纸转换成dwf格式的文件
- 下一篇: java socket编程聊天室_Jav