JAVA 飞机大战
? ? ? ? 小的時候我們玩過很多小游戲,比如:坦克大戰(zhàn)、貪吃蛇、超級瑪麗、推箱子、飛機(jī)大戰(zhàn)等等。今天就用java寫一個小游戲,飛機(jī)大戰(zhàn)。飛機(jī)大戰(zhàn)的主要知識點就是線程,只要對線程有基本的了解就能完成飛機(jī)大戰(zhàn)的編程。
? ? ? ? 關(guān)于飛機(jī)大戰(zhàn)的編寫,接下來分幾個步驟來完成。
一、實現(xiàn)敵方飛機(jī)的移動
? ? ? ?(用一張球的圖片來替代敵方飛機(jī),之后更換圖片就好)
? ? ? ?1.首先要有一個圖形界面(定義為BallFream類)
? ? ? ? ? 繼承JPanel是為了獲取它的畫筆,這不是最簡便的方法。接下來要使用畫筆,你用其他的方法獲取也可以.
? ? ? ?2.定義一個(Ball類)類來存儲對象(現(xiàn)在用的圖片是球,定義一個球類存儲相應(yīng)的參數(shù))
? ? ? ? ? 同時將球的圖片引入進(jìn)來,之后會使用。引入球的圖片用ImageIcon,將圖片直接復(fù)制到當(dāng)前包下。通過新建一個對象new ImageIcon(this.getClass().getResource("圖片名.類型名")).getImage();
public class Ball {private int x,y,width,height;private Image image;public Ball(int x, int y, int width, int height) {super();this.x = x;this.y = y;this.width = width;this.height = height;this.image = new ImageIcon(this.getClass().getResource("ball.png")).getImage();}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public int getWidth() {return width;}public void setWidth(int width) {this.width = width;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}}? ? ? ?3.將圖片畫出來和實現(xiàn)球的移動
? ? ? ? ? 在定義的對象類(Ball類)里寫畫球方法和移動方法。球并不是真的移動,而是我們改變了球的坐標(biāo)。畫完一個球后,畫一個窗體大小的顏色與窗體背景色一樣的矩形,會把之前畫的球覆蓋掉,接下來按照新的坐標(biāo)再畫一次。因為電腦畫的非常快,在我們看來就達(dá)到了移動的效果。
//畫球的方法,把圖畫出來,給定了位置、大小 public void draw(Graphics2D g) {g.drawImage(image, x - 80/2, y - 80/2, 80, 80,null);}//移動的方法,改變坐標(biāo)y的值,讓球看起來在下落 public void move() {y+=5; }? ? ? ?4.添加監(jiān)聽和繼承線程接口(Runnable)
? ? ? ? ?我們希望在窗體某個位置點擊鼠標(biāo)的時候就生成一個球,并開始運(yùn)動。這就要添加鼠標(biāo)監(jiān)聽機(jī)制,還要有事件處理類(BallListener類)。
? ? ? ? ?繼承Runnable是為了讓移動方法不斷執(zhí)行,這樣球的縱坐標(biāo)就會不斷改變,達(dá)到移動的效果。
? ? ? ? ?添加監(jiān)聽方法并啟動線程
? ? ? ? ?在圖形界面類(BallFrame類)中添加和啟動,啟動線程要先與Runnable建立聯(lián)系,也就是實例化Thread類。同時在實例化事件處理類BallListener是將需要使用的參數(shù)傳遞過去,這里把窗體傳遞過去,可以通過窗體獲取參數(shù)。
BallListener bl = new BallListener(this); this.addMouseListener(bl); Thread td = new Thread(bl); td.start();? ? ? 5.事件處理類BallListener
? ? ? 繼承MouseAdapter類只需重寫用到的抽象方法;
? ? ? 繼承Runnable接口,重寫run()方法。
? ? ? 實現(xiàn)球的移動,需要將以前畫的球去掉,留下最新畫的球。畫一個界面大小的矩形,將以前的球覆蓋掉再畫新的球。
? ? ? 在界面上可能不止一個球在移動,而是多個球在移動,就需要把所有的球存儲起來。建立一個數(shù)組隊列用來存儲生成的球,?鼠標(biāo)點擊一次就生成一個球,在鼠標(biāo)點擊后就將球存儲起來。當(dāng)線程運(yùn)行的時候,遍歷數(shù)組隊列,將所有的球都畫一遍。
? ? ?將球和矩形畫出來就需要畫筆,而我們已經(jīng)將窗體傳過來了,如果畫筆為空,就獲取畫筆。
import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList;public class BallListener extends MouseAdapter implements Runnable{private BallFrame bf;private int x,y;private Ball ball;//創(chuàng)建數(shù)組隊列,直接通過Ball類來存儲private ArrayList<Ball> list = new ArrayList<Ball>();//你也可以用Graphics,不用Graphics2Dprivate Graphics2D g;//構(gòu)造函數(shù)public BallListener(BallFrame bf) {this.bf = bf;}//重寫鼠標(biāo)點擊方法,獲取鼠標(biāo)點擊的坐標(biāo),把球的坐標(biāo)、寬度、高度存儲起來public void mouseClicked(MouseEvent e) {x = e.getX();y = e.getY();ball = new Ball(x,y,80,80);list.add(ball);}//重寫線程的抽象方法public void run() {//while循環(huán),讓代碼不斷執(zhí)行while(true) {//判斷畫筆是否為空,如果是則獲取畫筆if(g == null) {g = (Graphics2D) bf.getGraphics();//畫筆抗鋸齒(讓圖形邊緣變得光滑)g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);}//獲取窗體的背景顏色g.setColor(bf.getBackground());//畫一個窗體大小矩形,去掉移動痕跡g.fillRect(0, 0, 500, 600);//遍歷數(shù)組隊列,將隊列里的球都畫出來for(int i = 0; i < list.size(); i++) {//判斷語句,當(dāng)球移動出窗體可見范圍后就將它從數(shù)組隊列移除,減少內(nèi)存消耗if(y < bf.getHeight()) {ball = list.get(i);//調(diào)用畫球的方法ball.draw(g);//調(diào)用移動方法ball.move();}else {//移除不可見的球list.remove(ball);}}//線程休眠try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}? ? ? 6.補(bǔ)充
? ? ? ? ?關(guān)于調(diào)用Ball類的移動方法后數(shù)組隊列里存儲的值也會改變問題。
? ? ? ? ?ball = list.get(i);只是把地址賦給了ball,并不是直接把存儲的值給了ball。當(dāng)ball調(diào)用移動方法后改變了y的值,改變的值是數(shù)組隊列中的值。
? ? ? 7.總結(jié)
? ? ? ? 以上實現(xiàn)的功能為:鼠標(biāo)點擊窗體的某一個位置時,生成一個球,點擊多次,生成多個球。之后球會向下移動。
? ? ? ? 但存在屏閃問題。有可能在畫一個球的同時,一個清除移動痕跡的矩形也在繪制,會造成相互沖突。我們希望球在同一時間畫出來,接下來就解決這個問題。
二、雙緩沖解決屏閃問題
? ? ? ? ?
? ? ? ??
? ? ? ? 注意,顯示緩沖區(qū)是和顯示器一起的,顯示器只負(fù)責(zé)從顯示緩沖區(qū)取數(shù)據(jù)顯示。我們通常所說的在顯示器上畫一條直線,其實就是往該顯示緩沖區(qū)中寫入數(shù)據(jù)。顯示器通過不斷的刷新(從顯示緩沖區(qū)取數(shù)據(jù)),從而使顯示緩沖區(qū)中數(shù)據(jù)的改變及時的反映到顯示器上。
? ? ?這也是顯示復(fù)雜圖形時造成閃爍的原因,比如你現(xiàn)在要顯示從屏幕中心向外發(fā)射的一簇射線,你開始編寫代碼用一個循環(huán)從0度開始到360度,每隔一定角度畫一條從圓心開始向外的直線。你每次畫線其實是往顯示緩沖區(qū)寫入數(shù)據(jù),如果你還沒有畫完,顯示器就從顯示緩沖區(qū)取數(shù)據(jù)顯示圖形,此時你看到的是一個不完整的圖形,然后你繼續(xù)畫線,等到顯示器再次取顯示緩沖區(qū)數(shù)據(jù)顯示時,圖形比上次完整了一些,依次下去直到顯示完整的圖形。你看到圖形不是一次性完整地顯示出來,而是每次顯示一部分,從而造成閃爍。
--------------------- 本文來自 Smith先生 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/acs713/article/details/16359551?utm_source=copy
?
? ? ? ? 所以,我們引入次畫布。將數(shù)組隊列里存儲的信息遍歷完后將圖形在次畫布上畫出來,再把次畫布和次畫布上的圖形畫到窗體上。
? ? ? ? 在BallListener類進(jìn)行操作,對run()方法進(jìn)行修改。
private BufferedImage bi;//可用來構(gòu)建次畫布public void run() {while(true) {//引入次畫布,先在次畫布上消除移動的痕跡,然后把圖形按照數(shù)組隊列逐一畫出來BufferedImage bi = new BufferedImage(bf.getWidth(), bf.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);//獲取次畫布上的畫筆ig =(Graphics2D) bi.getGraphics();ig.setColor(bf.getBackground());//用次畫布上的畫筆在次畫布上消除移動痕跡ig.fillRect(0, 0, bf.getWidth(), bf.getHeight());for(int i = 0; i < list.size(); i++) {//圖形移動出窗體可見后就移除,減少占用if(x > 0 && x < bf.getWidth() && y > 0 && y < bf.getHeight()) {ball = list.get(i);//用次畫布上的畫筆畫球ball.draw(ig);ball.move();}else {list.remove(ball);}}//如果畫筆為空,則獲取窗體上的畫筆if(g == null) {g = (Graphics2D) bf.getGraphics();g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);}//將次畫布上的圖形畫到窗體上g.drawImage(bi, null, bf);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}三、敵方飛機(jī)自動生成和發(fā)射子彈
現(xiàn)在我們在前面的基礎(chǔ)上開發(fā)飛機(jī)大戰(zhàn),為了代碼的易讀,修改了類名,現(xiàn)在再貼一遍代碼吧
界面類PlaneFrame
import javax.swing.JFrame; import javax.swing.JPanel;public class PlaneFrame extends JPanel{public static void main(String[] args) {PlaneFrame pf = new PlaneFrame();pf.showFrame();}public void showFrame() {JFrame frame = new JFrame();frame.setTitle("球");frame.setSize(700,600);frame.setLocationRelativeTo(null);frame.setDefaultCloseOperation(3);frame.setResizable(false);//將JPanel添加到JFrame,this就是JPanelframe.add(this);frame.setVisible(true);PlaneListener bl = new PlaneListener(this);//添加鼠標(biāo)監(jiān)聽方法this.addMouseListener(bl);//與線程建立聯(lián)系Thread td = new Thread(bl);//調(diào)用啟動線程的方法td.start();}}事件處理類PlaneListener
import java.awt.Graphics; import java.awt.Image; import java.awt.event.MouseAdapter; import java.awt.image.BufferedImage; import java.util.ArrayList;import javax.swing.ImageIcon;public class PlaneListener extends MouseAdapter implements Runnable{private PlaneFrame pf;private int x,y,width,height;private Plane plane;private ArrayList<Plane> list = new ArrayList<Plane>();//創(chuàng)建球類數(shù)組隊列,存儲數(shù)據(jù)private Graphics g;private BufferedImage bi;//可用來構(gòu)建次畫布private Graphics ig;private Image background;//構(gòu)造函數(shù),把窗體傳遞過來public PlaneListener(PlaneFrame pf) {this.pf = pf;}public void run() {while(true) {//引入次畫布,先在次畫布上消除移動的痕跡,然后把圖形按照數(shù)組隊列逐一畫出來BufferedImage bi = new BufferedImage(pf.getWidth(), pf.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);//獲取次畫布上的畫筆ig = bi.getGraphics();// ig.setColor(bf.getBackground());//用次畫布上的畫筆在次畫布上消除移動痕跡// ig.fillRect(0, 0, bf.getWidth(), bf.getHeight());//引入背景圖片,用背景圖片替代矩形達(dá)到察除移動痕跡的目的background = new ImageIcon(this.getClass().getResource("background.jpg")).getImage();;ig.drawImage(background, 0, 0, pf.getWidth(), pf.getHeight(),null);for(int i = 0; i < list.size(); i++) {if(x < pf.getWidth() && y < pf.getHeight()) {plane = list.get(i);//用次畫布上的畫筆畫球plane.draw(ig);plane.move();}else {list.remove(plane);}}//如果畫筆為空,則獲取窗體上的畫筆if(g == null) {g = pf.getGraphics();}//將次畫布上的圖形畫到窗體上g.drawImage(bi,0,0, null);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}定義存儲類Plane
import java.awt.Graphics; import java.awt.Image; import java.util.ArrayList; import java.util.Random;import javax.swing.ImageIcon;public class Plane {private int x,y,width,height;private Image image,img;private int size;private PlaneFrame pf;private int addx;private int speed;//構(gòu)造方法//獲取圖片小球的數(shù)據(jù)public Plane(int x, int y, int width, int height,int speed,,PlaneFrame pf,Image image) {super();this.x = x;this.y = y;this.width = width;this.height = height;this.speed = speed;this.pf = pf;this.image = image;this.img = new ImageIcon(this.getClass().getResource("bullet.png")).getImage(); }//畫圖public void draw(Graphics g) {g.drawImage(image, x-width/2, y-height/2, width, height,null); //畫圖}//移動public void move() {y+=speed; }public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public int getWidth() {return width;}public void setWidth(int width) {this.width = width;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public int getSize() {return size;}public void setSize(int size) {this.size = size;}}? ? ? ?1.之前我們是點擊鼠標(biāo)才會生成圖片,現(xiàn)在我們要讓圖片自動生成。這就需要新建一個線程,在程序開始執(zhí)行的時候通過start()方法來啟動線程的run()方法來達(dá)到我們目的 。
? ? ? ?在PlaneListener類的構(gòu)造方法里啟動新的線程PlaneThread類(未定義),并傳遞參數(shù)。
public PlaneListener(PlaneFrame pf) {this.pf = pf;//這里的10為產(chǎn)生敵機(jī)的數(shù)量,可修改PlaneThread pt = new PlaneThread(pf,list,10,width,height);//啟動新線程pt.start(); }? ? ? ?在run()方法里寫一個循環(huán),給定size的大小,也就是產(chǎn)生敵方飛機(jī)的數(shù)量。再利用休眠,每隔一段時間就產(chǎn)生一架敵機(jī),并且利用數(shù)組隊列存儲起來。
接下來是PlaneThread類
import java.awt.Image; import java.util.ArrayList; import java.util.Random;import javax.swing.ImageIcon;public class PlaneThread extends Thread{private int size;private PlaneFrame pf;private ArrayList<Plane> list;private int x,y,width,height;private Image image;//構(gòu)造方法public PlaneThread(PlaneFrame pf,ArrayList<Plane> list,int size,int width, int height) {this.pf = pf;this.list = list;this.size = size;this.width = width;this.height = height;//敵機(jī)的圖片this.image = new ImageIcon(this.getClass().getResource("fighter.png")).getImage();}public void run() {//循環(huán),利用隨機(jī)數(shù)讓敵機(jī)生成的橫坐標(biāo)無規(guī)律for(int i = 0; i < size; i++) {Random rand = new Random();x = 40 + rand.nextInt(pf.getWidth() - 200) + 1;y = 20;//利用數(shù)組隊列將敵機(jī)存儲起來//x-80/2,y-80/2為坐標(biāo),80,80為圖片的寬度和高度,2為移動速度,image為圖片Plane ball = new Plane(x-80/2,y-80/2,80,80,2,list,pf,image);list.add(ball);try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}}}? ? ? ?2.敵機(jī)自動發(fā)射子彈,我這里是讓敵機(jī)每向下移動50單位距離就產(chǎn)生一枚子彈。在Plane類的移動方法里進(jìn)行操作。當(dāng)敵機(jī)的y坐標(biāo)除以50沒有余數(shù)的時候就生成一枚子彈。同時需要將數(shù)組隊列傳過來,把生成的子彈存儲起來。傳遞數(shù)組對列這里就不再重復(fù)了,通過構(gòu)造函數(shù),還要將子彈的圖片引入,下面是Plane類里移動方法的修改。
public void move(ArrayList<Plane> list) {//用speed來指代每次移動距離y+=speed;//判斷是否應(yīng)該生成子彈if(y%50 ==0) {//將子彈存儲起來//x,y為子彈生成的坐標(biāo);20,20為子彈圖片的大小;6為子彈的移動速度;img為子彈的圖片Plane bullet = new Plane(x,y,20,20,6,list,pf,img);list.add(bullet); }? ? ? ?注意:到達(dá)這一步運(yùn)行程序后會發(fā)現(xiàn),敵機(jī)每隔50個單位距離會產(chǎn)生一顆子彈,而之前產(chǎn)生的子彈也會每隔50個單位生產(chǎn)一顆子彈。這是因為判斷生成子彈的時候只根據(jù)距離來判斷,沒有區(qū)分?jǐn)硻C(jī)還是子彈,這個問題后面會解決。
? ? ? ?
四、己方飛機(jī)和子彈
? ? ? 1.通過鼠標(biāo)控制己方飛機(jī),當(dāng)鼠標(biāo)在窗體上移動時己方飛機(jī)跟著移動,同時還需給定己方飛機(jī)的初始位置。創(chuàng)建一個myplane對象來對應(yīng)己方飛機(jī),實時更新己方飛機(jī)的坐標(biāo)值。在PlaneListener類進(jìn)行操作
//給定己方飛機(jī)的初始坐標(biāo)值 private int x = 330,y = 520,width,height; //引入己方飛機(jī)的圖片 private Image imagemyplane = new ImageIcon(this.getClass().getResource("myplane.png")).getImage();private Plane myplane;//構(gòu)造函數(shù),把窗體傳遞過來 public PlaneListener(PlaneFrame pf) {this.pf = pf;//存儲己方飛機(jī)的數(shù)據(jù) myplane = new Plane(x,y,80,80,0,list,pf,imagemyplane); list.add(myplane);//啟動生成敵機(jī)的線程 PlaneThread pt = new PlaneThread(pf,list,50,width,height);pt.start();}public void mouseMoved(MouseEvent e){//獲取鼠標(biāo)移動時的坐標(biāo)值x = e.getX();y = e.getY();//將坐標(biāo)賦給myplane對象myplane.setX(x);myplane.setY(y); }? ? ? 2.己方飛機(jī)發(fā)射不斷發(fā)射子彈,創(chuàng)建一個己方飛機(jī)的子彈線程MyBullet。己方飛機(jī)的子彈通過獲取己方飛機(jī)的坐標(biāo)來生成子彈。
import java.awt.Image; import java.util.ArrayList;import javax.swing.ImageIcon;public class MyBullet extends Thread{private PlaneFrame pf;private ArrayList<Plane> list;private int x,y,width,height;private Image imagemybullet;private Plane myplane;//構(gòu)造函數(shù)public MyBullet(PlaneFrame pf,ArrayList<Plane> list,int x,int y,int width, int height,Plane myplane) {this.pf = pf;this.list = list;this.x = x;this.y = y;this.width = width;this.height = height;this.myplane = myplane;//引入己方飛機(jī)的子彈圖片this.imagemybullet = new ImageIcon(this.getClass().getResource("mybullet.png")).getImage();}public void run() {while(true) {//獲取己方飛機(jī)的坐標(biāo)x = myplane.getX();y = myplane.getY();//存儲己方飛機(jī)的子彈//x,y為坐標(biāo),20,20為圖片的寬度和高度,-6為移動速度(負(fù)號是移動方向)Plane mybullet = new Plane(x,y,20,20,-6,list,pf,imagemybullet);list.add(mybullet);//每隔500毫秒生成一顆子彈try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}}?
五、判斷飛機(jī)大戰(zhàn)輸贏
? ? ? ?1.為解決子彈每隔50個單位距離生成一顆子彈,用字符對存儲的信息進(jìn)行標(biāo)識,確認(rèn)是敵機(jī)才會生成子彈。
在Plane類里操作
public void move(ArrayList<Plane> list) {y+=speed;for(int i = 0;i < list.size();i++) {//如果是敵機(jī)且移動50個單位距離if(type.equals("e_p") && y%50 == 0) {Plane bullet = new Plane(x,y,20,20,6,list,pf,imagebullet,"e_b");list.add(bullet); }break;}}? ? ? ?2.在Plane類里寫一個碰撞crash方法,敵機(jī)和敵機(jī)子彈碰到己方飛機(jī)子彈時會消失(從數(shù)組隊列里一起移除);敵機(jī)和敵機(jī)子彈碰到己方飛機(jī)時,游戲結(jié)束,線程停止。static關(guān)鍵字的運(yùn)用可定義靜態(tài)變量,在不同的類可通過類名.對象名調(diào)用,根據(jù)這個來控制PlaneListener類里的run()方法的
Plane類里的crash()方法
public void crash() {for(int j = 0;j < list.size();j++){Plane lt = list.get(j);if(lt.getType().equals("m_p")) {for(int i = 0;i < list.size();i++) {Plane temp = list.get(i);if(temp.getType().equals("e_p") || temp.getType().equals("e_b")) {double xx = lt.getX() - temp.getX();double yy = lt.getY() - temp.getY();double ww = lt.getWidth() + temp.getWidth();if(Math.sqrt(xx*xx + yy*yy) <= ww/2) {list.remove(lt);list.remove(temp);//給靜態(tài)屬性賦值PlaneListener.flag=false;}}}}if(lt.getType().equals("m_b")) {for(int i = 0;i < list.size();i++) {Plane temp = list.get(i);if(temp.getType().equals("e_p") || temp.getType().equals("e_b")) {double xx = lt.getX() - temp.getX();double yy = lt.getY() - temp.getY();double ww = lt.getWidth() + temp.getWidth();if(Math.sqrt(xx*xx + yy*yy) <= ww/2) {list.remove(temp);list.remove(lt);}}}}}}下面貼上完整代碼(此版本的飛機(jī)大戰(zhàn)很簡單,可以在此基礎(chǔ)上進(jìn)行開發(fā),你可以發(fā)揮自己的想象,增設(shè)關(guān)卡、血條、獎勵、Boss等等)(可能存在少許未使用代碼和注釋掉的代碼沒有刪除)
import java.awt.Color;import javax.swing.JFrame; import javax.swing.JPanel;public class PlaneFrame extends JPanel{public static void main(String[] args) {PlaneFrame pf = new PlaneFrame();pf.showFrame();}public void showFrame() {JFrame frame = new JFrame();frame.setTitle("飛機(jī)大戰(zhàn)");frame.setSize(700,600);frame.setLocationRelativeTo(null);frame.setDefaultCloseOperation(3);frame.setResizable(false);//將JPanel添加到JFrame,this就是JPanelframe.add(this);frame.setVisible(true);PlaneListener bl = new PlaneListener(this);//添加鼠標(biāo)監(jiān)聽方法// this.addMouseListener(bl);this.addMouseMotionListener(bl);//與線程建立聯(lián)系Thread td = new Thread(bl);//調(diào)用啟動線程的方法td.start();//讓線程里的代碼執(zhí)行bl.setFlag(true);}} import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.ArrayList;import javax.swing.ImageIcon;public class PlaneListener extends MouseAdapter implements Runnable{private PlaneFrame pf;private int x = 330,y = 520,width,height;private Plane plane;private ArrayList<Plane> list = new ArrayList<Plane>();//創(chuàng)建飛機(jī)類數(shù)組隊列,存儲數(shù)據(jù)private Graphics2D g;private BufferedImage bi;//可用來構(gòu)建次畫布private Graphics ig;private Image background;private Image imagemyplane = new ImageIcon(this.getClass().getResource("myplane.png")).getImage();;private Plane myplane;private String type;//設(shè)置為靜態(tài)屬性,可在別的類直接調(diào)用public static boolean flag = true;//構(gòu)造函數(shù),把窗體傳遞過來public PlaneListener(PlaneFrame pf) {this.pf = pf;//創(chuàng)建我的飛機(jī)對象,并存儲起來myplane = new Plane(x,y,80,80,0,list,pf,imagemyplane,"m_p"); list.add(myplane);//創(chuàng)建敵機(jī)線程,讓敵機(jī)自動出現(xiàn),把用到的參數(shù)傳遞過去PlaneThread pt = new PlaneThread(pf,list,50,width,height);pt.start();//創(chuàng)建我的飛機(jī)的子彈線程,讓子彈自動發(fā)射并傳遞參數(shù)MyBullet mb = new MyBullet(pf,list,x,y,width,height,type,myplane);mb.start();}public PlaneListener() {}public void mouseMoved(MouseEvent e){//獲取坐標(biāo)x = e.getX();y = e.getY();//將坐標(biāo)賦給我的飛機(jī)對象myplane.setX(x);myplane.setY(y); }public void run() {while(true) {if(flag) {//引入次畫布,先在次畫布上消除移動的痕跡,然后把圖形按照數(shù)組隊列逐一畫出來BufferedImage bi = new BufferedImage(pf.getWidth(), pf.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);//獲取次畫布上的畫筆ig = bi.getGraphics();background = new ImageIcon(this.getClass().getResource("background.jpg")).getImage();;ig.drawImage(background, 0, 0, pf.getWidth(), pf.getHeight(),null);for(int i = 0; i < list.size(); i++) {if(x < pf.getWidth() && y < pf.getHeight()) {plane = list.get(i);//用次畫布上的畫筆來畫plane.draw(ig); plane.move(list);plane.judge(pf);plane.crash();}else {list.remove(plane);}}//如果畫筆為空,則獲取窗體上的畫筆if(g == null) {g = (Graphics2D) pf.getGraphics();g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);}//將次畫布上的圖形畫到窗體上g.drawImage(bi,0,0, null);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}} import java.awt.Image; import java.util.ArrayList; import java.util.Random;import javax.swing.ImageIcon;public class PlaneThread extends Thread{private int size;private PlaneFrame pf;private ArrayList<Plane> list;private int x,y,width,height;private Image imageplane;private String type;public PlaneThread(PlaneFrame pf,ArrayList<Plane> list,int size,int width, int height) {this.pf = pf;this.list = list;this.size = size;this.width = width;this.height = height;this.imageplane = new ImageIcon(this.getClass().getResource("plane.png")).getImage();}public void run() {//自動產(chǎn)生敵機(jī)for(int i = 0; i < size; i++) {Random rand = new Random();x = 80 + rand.nextInt(pf.getWidth() - 100);y = 20;Plane plane = new Plane(x-80/2,y-80/2,80,80,2,list,pf,imageplane,"e_p");list.add(plane);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}}} import java.awt.Image; import java.util.ArrayList;import javax.swing.ImageIcon;public class MyBullet extends Thread{private PlaneFrame pf;private ArrayList<Plane> list;private int x,y,width,height;private Image imagemybullet;private String type;private Plane myplane;public MyBullet(PlaneFrame pf,ArrayList<Plane> list,int x,int y,int width, int height,String type,Plane myplane) {this.pf = pf;this.list = list;this.x = x;this.y = y;this.width = width;this.height = height;this.type = type;this.myplane = myplane;this.imagemybullet = new ImageIcon(this.getClass().getResource("mybullet.png")).getImage();}public void run() {//自動產(chǎn)生子彈while(true) {//通過我的飛機(jī)對象來獲取子彈發(fā)射的坐標(biāo)x = myplane.getX();y = myplane.getY();Plane mybullet = new Plane(x,y,20,20,-6,list,pf,imagemybullet,"m_b");list.add(mybullet);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}} import java.awt.Graphics; import java.awt.Image; import java.util.ArrayList; import java.util.Random;import javax.swing.ImageIcon;public class Plane {private int x,y,width,height;private Image imageplane,imagebullet;private int size;private ArrayList<Plane> list;private PlaneFrame pf;private int addx;private int speed;private String type;private PlaneListener pr;//構(gòu)造方法//獲取圖片小球的數(shù)據(jù)public Plane(int x, int y, int width, int height,int speed,ArrayList<Plane> list,PlaneFrame pf,Image imageplane,String type) {super();this.x = x;this.y = y;this.width = width;this.height = height;this.speed = speed;this.list = list;this.pf = pf;this.imageplane = imageplane;this.type = type;this.imagebullet = new ImageIcon(this.getClass().getResource("bullet.png")).getImage();//隨機(jī)數(shù)的運(yùn)用Random rand = new Random();addx = -10 + rand.nextInt(20);}//畫圖public void draw(Graphics g) {g.drawImage(imageplane, x-width/2, y-height/2, width, height,null); //畫圖// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,// RenderingHints.VALUE_ANTIALIAS_ON);}//移動public void move(ArrayList<Plane> list) {//x+=addx;敵機(jī)的運(yùn)動可以左右隨機(jī) // x+=addx;y+=speed;for(int i = 0;i < list.size();i++) {if(type.equals("e_p") && y%50 == 0) {Plane bullet = new Plane(x,y,20,20,6,list,pf,imagebullet,"e_b");list.add(bullet); }break;}}//判斷是否到達(dá)邊界//敵機(jī)左右運(yùn)動到達(dá)邊界后反彈public void judge(PlaneFrame pf) {if(x-40 <= 0) {addx = -addx;}else if(x+40 >= pf.getWidth()) {addx = -addx;}}//碰撞public void crash() {for(int j = 0;j < list.size();j++){Plane lt = list.get(j);if(lt.getType().equals("m_p")) {for(int i = 0;i < list.size();i++) {Plane temp = list.get(i);if(temp.getType().equals("e_p") || temp.getType().equals("e_b")) {double xx = lt.getX() - temp.getX();double yy = lt.getY() - temp.getY();double ww = lt.getWidth() + temp.getWidth();if(Math.sqrt(xx*xx + yy*yy) <= ww/2) {list.remove(lt);list.remove(temp);//給靜態(tài)屬性賦值PlaneListener.flag=false;}}}}if(lt.getType().equals("m_b")) {for(int i = 0;i < list.size();i++) {Plane temp = list.get(i);if(temp.getType().equals("e_p") || temp.getType().equals("e_b")) {double xx = lt.getX() - temp.getX();double yy = lt.getY() - temp.getY();double ww = lt.getWidth() + temp.getWidth();if(Math.sqrt(xx*xx + yy*yy) <= ww/2) {list.remove(temp);list.remove(lt);}}}}}}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public int getWidth() {return width;}public void setWidth(int width) {this.width = width;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public int getSize() {return size;}public void setSize(int size) {this.size = size;}public String getType() {return type;}public void setType(String type) {this.type = type;}}?
總結(jié)
- 上一篇: python装饰器调用顺序_聊一聊Pyt
- 下一篇: 分布式锁与实现(一)——基于Redis实