Java实现飞机大战(详细思路与过程,含源代码)
目錄
- 演示
- 構思
- 詳細分析
- GameFrame
- 背景動畫
- 菜單選項
- 添加(鍵盤)監聽器
- GamePanel
- 動態游戲顯示區(雙緩沖)
- Plane(myplane,enemyplane,bossplane)
- Break
- Collision
- Bullet
- Dialog
- Sound
- Main方法
- 總結:
演示
飛機大戰
源代碼下載: https://github.com/Fattybenny/javaswingproject/tree/main/java%E9%A3%9E%E6%9C%BA%E5%A4%A7%E6%88%98.
構思
首先,要把整體的游戲框架和內容構思出來(根據預先構思游戲里存在的組件內容和游戲功能抽象出指定類)。以我的小游戲為例:
1.主界面框架類:GameFrame(extends JFrame)
? ? ? ? ? ?顯示開始界面
2.彈出界面類:Dialog (extends JDialog)
????????????彈出設置界面(聲音開關)、彈出游戲成功、失敗界面
JDialog 窗體的功能是從一個窗體中彈出另一個窗體,就像是在使用 IE 瀏覽器時彈出的確定對話框,JDialog 窗體與 JFrame 窗體形式基本相同,設置窗體的特性時調用的方法名稱都基本相同,如設置窗體大小、窗體關閉狀態等
3.游戲面板類:GamePanel (extends JPanel)
? ? ? ? ? ?真正顯示飛機大戰動態游戲畫面,并且還添加了按鈕JButton用于控制游戲開始暫停。
4.玩家飛機類:MyPlane
? ? ? ? ? ?移動玩家飛機、畫玩家飛機等其他與玩家飛機相關的方法
5.敵機類:EnemyPlane
? ? ? ? ? ?移動敵機、畫敵機
6.BOSS飛機類:BossPlane
? ? ? ? ? ?移動BOSS飛機、畫BOSS飛機
7.子彈類(也可以分三個類:玩家飛機子彈、敵機子彈、BOSS子彈)
? ? ? ? ? ?移動子彈、繪制子彈
8.碰撞類:Collision
? ? ? ? ? ?檢測各種碰撞情況
9.爆炸類:Break
? ? ? ? ? ?繪制飛機爆炸圖片
10.聲音類:Sound
? ? ? ? ? ?控制聲音的播放與暫停
11.主類:Main
? ? ? ? ? ? 開啟程序
詳細分析
GameFrame
背景動畫
動態的圖像(視頻)原理:視頻由一張張靜態的圖片快速變換形成,連續的圖像變化每秒超過24幀(frame)畫面以上時,根據視覺暫留原理,人眼無法辨別單幅的靜態畫面;看上去是平滑連續的視覺效果。
在java程序中,如果通過人手動點擊一次換一次圖片,那么要想實現肉眼看見的視頻效果需要我們一秒鐘至少點擊24次,這是非常困難的。而我們希望的是通過一次點擊就可以產生動畫效果,讓其自動每隔一段極短的時間就換一次圖片。于是可以采用多線程的方法來實現。由于動畫效果是在當前的GameFrame類中實現的,可以直接定義一個內部類繼承Thread,當然也可以新建一個class文件定義。
獲取包中的圖片:
Image img; img=Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/Capture/飛機.mp4_"+i+".png")); //獲得資源的URL:this.getClass().getResource(String name) // 單斜杠 /開頭表示從根目錄開始 private class setBackground2 extends Thread {Image img;Graphics mg;@Overridepublic void run() {while(true){for(int i=0;i<200;i++)//200張圖片為一個完整動畫{ img=Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/Capture/飛機.mp4_"+i+".png"));mg.drawImage(img, 0, 0, null);try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }} } }菜單選項
設置不同的JLabel標簽添加到界面的相應位置。
一般容器都有默認布局方式,但是有時候需要精確指定各個組建的大小和位置,就需要用到空布局。
首先利用setLayout(null) 語句將容器的布局設置為null布局(空布局)
再調用組件的setBounds(int x, int y, int width,int height) 方法設置組件在容器中的大小和位置,單位均為像素。
通過JLabel組件添加一張圖片:
ImageIcon background = new ImageIcon(this.getClass().getResource("/images/mainback.png"));back = new JLabel(background);back.setBounds(0,700,1800, 300); this.getContentPane().add(back); //設置標簽label01 = new JLabel("開始游戲");label01.setFont(new Font("acefont-family", Font.BOLD, 50));label01.setForeground(Color.blue);//設置字體背景顏色label01.setBounds(820, 740, 400, 120);//起點寬高label02 = new JLabel("選擇飛機");label02.setFont(new Font("acefont-family", Font.BOLD, 50));label02.setBounds(820, 830, 400, 120);label03 = new JLabel(icon);label03.setBounds(600, 740, 250, 120);label04 = new JLabel(icon);label04.setBounds(600, 830, 250, 120);label04.setVisible(false);... ...問題與解決:
1.先添加的組件會覆蓋影響到后添加的組件。
例如這里有三個JLabel組件,其中一個是帶有背景圖片的,其他兩個是帶有文字的,一定要最后添加帶有背景圖片的,否則無法將文字顯示在圖片上。
2.注意畫筆繪制圖片的覆蓋問題:
后面經常要用到drawimage方法,要注意,畫筆后畫的內容會覆蓋先畫的內容;畫筆畫的內容會覆蓋類似JLabel這種組件,無論組件先添加還是后添加(解決辦法:在組件添加到相應的容器之后再設置坐標位置(setbounds),或者如上文所說的通過按正確順序添加JLabel組件的方法來達到設置背景圖片的功能,避免了用drawimage)。
添加(鍵盤)監聽器
public void keyadapter(){this.requestFocusInWindow();this.addKeyListener(new KeyAdapter() {public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); //監聽向上或向下按if(key == KeyEvent.VK_DOWN || key == KeyEvent.VK_UP) {label03.setVisible(!label03.isVisible());label04.setVisible(!label04.isVisible());if(label03.isVisible()) {label01.setForeground(Color.blue);label02.setForeground(Color.black);} else {label01.setForeground(Color.black);label02.setForeground(Color.blue);}}if(key == KeyEvent.VK_ENTER && label03.isVisible()) {//添加游戲面板JPaneladd(...);//并移除之前添加的JLabel組件remove(label02);remove(label03);remove(label04);remove(back);... ...}... ...}});}問題與解決
焦點的獲取
在使用鍵盤監聽器的時候,一定要讓監聽對象獲取焦點,如果焦點不在監聽對象上,那么鍵盤輸入的內容就無法被監聽到。
使用requestFocusInWindow()方法
注意在監聽器方法中的this關鍵字
在添加監聽器時有兩種不同的方法:
1.this.addKeyListener(listener);
listener是用戶定義的監聽器類,這個類implements相應接口。
2.直接在當前類定義監聽器;
GamePanel
按鈕區和分數區都分別添加相應的JButton按鈕,并添加ActionListener監聽器即可
動態游戲顯示區(雙緩沖)
還是和GameFrame中的背景動畫類似,都采用多線程方法。
private class MapPanel extends Canvas implements Runnable
問題與解決:
1.Canvas使用
Canvas是AWT組件,JPanel是Swing組件,Swing組件是以AWT組件為基礎的,從理論上來說,Canvas要比JPanel更輕量些.如果canvas能滿足需求,就用canvas.Canvas 組件表示屏幕上一個空白矩形區域,應用程序可以在該區域內繪圖,或者可以從該區域捕獲用戶的輸入事件。
不能直接使用該類,需要繼承Canvas并重寫其paint方法.
repaint paint update 三個方法的調用順序:
repaint->update->paint
paint源碼:
update源碼:
public void update(Graphics g) {g.clearRect(0, 0, width, height);//清除界面paint(g); }由此看來我們可以選擇性的重寫update或者paint方法來滿足程序繪畫需要,如果不需要清除界面就去除 g.clearRect(0, 0, width, height)方法,在本例中就需要執行這一步,因為每次重繪圖片如果都執行一遍清除界面操作就會出現閃爍現象。(如果是用戶自己定義的繪制方法,不需要用到paint方法進行重繪,則不用重寫這些方法,需要用到雙緩沖技術,如下所示)
除此以外解決圖片閃爍現象,還要用到雙緩沖技術:
先在內存中預先分配一定大小的圖片緩沖區,在將所有繪圖方法繪制到緩沖區之后,再最后將圖片緩沖區的內容繪制出來;
Plane(myplane,enemyplane,bossplane)
這三個不同的飛機類內容基本相同:
class Plane { 初始化飛機的圖片、坐標等如果是一組圖片則用Image[]數組存儲{planeimg=Toolkit.getDefaultToolkit().getImage(getClass().getResource());}繪制飛機方法:{先判斷飛機是否還存活存活:g.drawImage(planeimg,x, y, null); 死亡:調用爆炸類里的繪制爆炸圖片方法(下文所示)}移動飛機方法:{修改飛機圖片的x,y坐標} }Break
class Break { 初始化爆炸圖片 { plane_b = new Image[6]; for(int i = 0; i < plane_b.length; i++) {plane_b[i] = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/images/bomb_enemy_" + i + ".png"));} ... }繪制爆炸圖片 { g.drawImage(plane_b[i/5], x, y, EnemyPlane.ENEMY_SIZE, EnemyPlane.ENEMY_SIZE, null); g.drawImage(plane_b[i/5], x, y, MyPlane.PLANE_SIZE, MyPlane.PLANE_SIZE, null); ... } }Collision
兩張圖片(飛機和飛機,飛機和子彈)是否相碰,需要判斷的是矩形圖片是否有重疊部分。
以飛機與飛機相碰為例:
按照x,y坐標的大小不同,總共有2*2四種情況:
Bullet
class Bullet {初始化子彈圖片{bullet = Toolkit.getDefaultToolkit().getImage(getClass().getResource());}繪制子彈{g.drawImage(bullet, bullet_x, bullet_y, BULLET_WIDTH,BULLET_HEIGHT, null);}移動子彈 ... }存儲子彈:
private ArrayList<Bullet> mybulletarray;//玩家飛機子彈數組 private ArrayList<Bullet> enemybulletarray;//敵機子彈數組 private ArrayList<Bullet[]> bossbulletarray;//boss子彈數組Dialog
彈出對話框和JFrame類似,往里面添加各種組件和監聽器即可。
public class Dialog extends JDialog { public Dialog(JFrame j, int i) {super(j, true);setLayout(null);setResizable(false);if(i == 1)showFail(j);//顯示挑戰失敗else if(i == 2)showSuccess(j);//顯示挑戰成功對話elseshowSetting(j);//顯示設置對話setVisible(true);}... //游戲失敗 private void showFail(JFrame j) {setTitle("提示"); setBounds(800, 400, 500, 300);jl01 = new JLabel("挑戰失敗");jl01.setFont(new Font("acefont-family", Font.BOLD, 50));jl01.setForeground(Color.blue);jl01.setBounds(65, 40, 400, 50);add(jl01);jl02 = new JLabel("分數" + GamePanel.sum);jl02.setFont(new Font("acefont-family", Font.BOLD, 30));jl02.setForeground(Color.RED);jl02.setBounds(65, 120, 400, 50);add(jl02);}.... }Sound
public class Sound { private Clip clip;static boolean[] b = new boolean[]{true, true, true, true};//控制聲音播放//按鍵音 //打開聲音文件的方法。public Sound(String path){AudioInputStream audio;try {URL url = this.getClass().getResource(path);audio = AudioSystem.getAudioInputStream(url);clip = AudioSystem.getClip();clip.open(audio);}catch (Exception e) {e.printStackTrace();}}/*** 停止播放*/void stop() {clip.stop();//暫停音頻播放}/*** 開始播放*/void start() {clip.start();//播放音頻}/*** 回放背景音樂設置*/void loop() {clip.loop(20);//回放} }想要在某個界面或時刻實現播放聲音,直接new一個對象,并傳入文件地址就行。
如下Main方法所示:
Main方法
public class Main {static GameFrame f1;static Sound sound;public static void main(String[] args) {f1=new GameFrame();f1.setDefaultCloseOperation(3);f1.setVisible(true);f1.setResizable(false);if(sound==null){sound=new Sound("/sounds/mainback.wav");sound.start();sound.loop();}} }總結:
除了上文中所解決的問題,還有一點就是,對于多線程到底該什么時候去創建?或者說多線程該設置在什么位置?(以下是我自己的理解)
直觀的感受就是多線程是在自動持續執行時引入的,對于飛機大戰來說,動畫界面一旦開始執行(在滿足條件的情況下),會一直執行直到滿足結束條件就退出,很明顯,背景、玩家飛機、敵機、boss飛機等等這些圖片都是在自動不斷刷新,剛開始會想到讓這些自動執行的類都實現多線程(extends Thread),要完成刷新的同步,只需要統一好阻塞時間就行了,但是這樣實現會發現代碼開銷比較大,且不便于統一管理。所以可以將這些類統一用一個Thread類實現同步的控制(只需要在這個Thread類中調用各個類中的方法即可)。
在本例中就是用GamePanel(可以直接讓GamePanel extends Thread 或者創建一個內部Thread類)來控制背景、飛機等各種圖片的繪制和圖片移動。
總結
以上是生活随笔為你收集整理的Java实现飞机大战(详细思路与过程,含源代码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 语言三做一年级算题_一年级数学期末考试,
- 下一篇: java收发邮寄_JavaMail收发邮