關於這個坦克大戰的項目是在學習Java基礎的時候,拿來練習的最近看到這些代碼,感覺很親切,就把他們都復制下來,編輯成博客。回首看去,Java基礎的學習確實應該建立在找項目練習上,這樣才能將學到的基礎知識用到實際當中,不然你知道什么是面向對象編程,什么是線程,什么是死鎖,概念都了解了一大堆,等到實際應用的時候,還是力不從心。初學者千萬不要灰心,真心是敲着敲着就有感覺了。下面還是循序漸進的介紹這個項目的幾個版本,注釋我寫的很詳細,新功能添加后部分代碼有改動,如果感興趣,可以看前幾篇博客。
坦克大戰(1.4版本)
1.MyTankGame類:
/* * 刪掉很多之前的注釋 * 功能:線程(坦克打子彈)進過分析:子彈是個類 */ package com.fanghua3; import java.awt.*; import java.awt.event.KeyEvent; import java.util.Vector; import javax.swing.*; public class MyTankGame1_4 extends JFrame { Mypanel1_2 mp = null; public static void main(String[] args) { new MyTankGame1_4(); } // 構造函數 public MyTankGame1_4() { mp = new Mypanel1_2(); // 啟動mp線程 Thread t = new Thread(mp); t.start(); this.add(mp); // 注冊監聽 this.addKeyListener(mp); this.setSize(400, 300); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } } // 我的面板,拓寬思路:Panel本身就是一個刷新體 class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener, Runnable { // 定義我的坦克 Hero1_2 hero = null; // 定義敵人的坦克(不止一輛,線程安全,集合) Vector<EnemyTank> ets = new Vector<EnemyTank>(); int enSize = 3;// 敵人坦克保持三個 // 構造函數 public Mypanel1_2() { hero = new Hero1_2(10, 10); for (int i = 0; i < enSize; i++) { // 創建一輛敵人的坦克 EnemyTank et = new EnemyTank((i + 1) * 50, 0); et.setColor(0); // 坦克默認反向是0(向上),這里改一下 et.setDirect(2); // 加入 ets.add(et); } } // 重寫paint函數 public void paint(Graphics g) { // 一定要調用 super.paint(g); g.fillRect(0, 0, 400, 300); // 畫出自己的坦克(將方向填進去) this.drawTank(hero.getX(), hero.getY(), g, this.hero.direct, 1); // 畫出子彈(后添加 hero.s.isLive==true,節省資源) if (hero.s != null && hero.s.isLive == true) { g.draw3DRect(hero.s.x, hero.s.y, 1, 1, false); } // 畫出敵人的坦克 for (int i = 0; i < enSize; i++) { this.drawTank(ets.get(i).getX(), ets.get(i).getY(), g, ets.get(i) .getDirect(), 0); } } // 畫出坦克的函數 public void drawTank(int x, int y, Graphics g, int direct, int type) { // 坦克類型 switch (type) { case 0: g.setColor(Color.green); break; case 1: g.setColor(Color.yellow); break; } // 方向設置 switch (direct) { // 向上 case 0: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y); break; // 向右 case 1: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x + 30, y + 10); break; // 向下 case 2: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y + 30); break; // 向左 case 3: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x, y + 10); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub // 已更正為順時針 if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) { this.hero.moveUp(); this.hero.setDirect(0); } else if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D) { this.hero.setDirect(1); this.hero.moveRight(); } else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S) { this.hero.moveDown(); this.hero.setDirect(2); } else if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A) { this.hero.moveLeft(); this.hero.setDirect(3); } else if (e.getKeyCode() == KeyEvent.VK_J) { // 將J鍵設置為發出子彈 this.hero.shotEnemy(); this.repaint(); } } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void run() { // TODO Auto-generated method stub // 每隔100毫秒去重繪 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 重繪 this.repaint(); } } }
2.Menbers類:
package com.fanghua3; //子彈類 class Shot implements Runnable{ int x; int y; int direct; //設置子彈的消亡(默認活着的) boolean isLive=true; //speed要給個初始值1,之前給0,按J鍵,子彈沒有動 int speed=1; public Shot(int x, int y,int direct) { super(); this.x = x; this.y= y; this.direct=direct; } @Override public void run() { // TODO Auto-generated method stub while(true){ //設置子彈休息50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } switch(direct){ case 0: y-=speed; break; case 1: x+=speed; break; case 2: y+=speed; break; case 3: x-=speed; break; } System.out.println("子彈坐標x="+x+"y="+y); //子彈什么時候死亡 //判斷該子彈是否碰到邊緣 if(x<0||x>400||y<0||y>300){ this.isLive=false; break; } } } } //坦克類 class Tank1_2 { int x = 0; int y = 0; // 坦克方向:0表示上,1表示右,2表示下,3表示左 int direct = 0; int speed = 1; //坦克的顏色 int color; public int getColor() { return color; } public void setColor(int color) { this.color = color; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } 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 Tank1_2(int x, int y) { this.x = x; this.y = y; } } //敵人的坦克 class EnemyTank extends Tank1_2{ public EnemyTank(int x, int y) { super(x, y); // TODO Auto-generated constructor stub } } //我的坦克 class Hero1_2 extends Tank1_2 { //子彈 Shot s=null; public Hero1_2(int x, int y) { super(x, y); } //坦克開火 public void shotEnemy(){ switch(this.direct){ case 0: s=new Shot(x+10,y,0); break; case 1: s=new Shot(x+30,y+10,1); break; case 2: s=new Shot(x+10,y+30,2); break; case 3: s=new Shot(x,y+10,3); break; } //啟動子彈線程 Thread t=new Thread(s); t.start(); } public void moveUp() { y -= speed; } public void moveRight() { x += speed; } public void moveDown() { y += speed; } public void moveLeft() { x -= speed; } }
坦克大戰(1.5版本)
1.MyTankGame類:
/* * 刪掉很多之前的注釋 * 功能:子彈連發,(線程)最多5顆子彈 * 敵人坦克消失 */ package com.fanghua4; import java.awt.*; import java.awt.event.KeyEvent; import java.util.Vector; import javax.swing.*; public class MyTankGame1_5 extends JFrame { Mypanel1_2 mp = null; public static void main(String[] args) { new MyTankGame1_5(); } // 構造函數 public MyTankGame1_5() { mp = new Mypanel1_2(); // 啟動mp線程 Thread t = new Thread(mp); t.start(); this.add(mp); // 注冊監聽 this.addKeyListener(mp); this.setSize(400, 300); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } } // 我的面板,拓寬思路:Panel本身就是一個刷新體 class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener, Runnable { // 定義我的坦克 Hero1_2 hero = null; // 定義敵人的坦克(不止一輛,線程安全,集合) Vector<EnemyTank> ets = new Vector<EnemyTank>(); int enSize = 3;// 敵人坦克保持三個 // 構造函數 public Mypanel1_2() { hero = new Hero1_2(10, 10); for (int i = 0; i < enSize; i++) { // 創建一輛敵人的坦克 EnemyTank et = new EnemyTank((i + 1) * 50, 0); et.setColor(0); // 坦克默認反向是0(向上),這里改一下 et.setDirect(2); // 加入 ets.add(et); } } // 重寫paint函數 public void paint(Graphics g) { // 一定要調用 super.paint(g); g.fillRect(0, 0, 400, 300); // 畫出自己的坦克(將方向填進去) this.drawTank(hero.getX(), hero.getY(), g, this.hero.direct, 1); // 從ss中取出每一顆子彈,並畫出 for (int i = 0; i < hero.ss.size(); i++) { Shot myShot = hero.ss.get(i); if (myShot != null && myShot.isLive == true) { g.draw3DRect(myShot.x, myShot.y, 1, 1, false); /* * 畫出一顆子彈(后添加 hero.s.isLive==true,節省資源) if (hero.s != null * &&hero.s.isLive == true) { g.draw3DRect(hero.s.x, hero.s.y, * 1, 1,false); } */ } if (myShot.isLive == false) { // 從ss(向量)中刪除該子彈 // hero.ss.remove(i);會報異常。 hero.ss.remove(myShot); } } // 畫出敵人的坦克 for (int i = 0; i < enSize; i++) { EnemyTank et = ets.get(i); if (et.isLive) { this.drawTank(et.getX(), et.getY(), g, et.getDirect(), 0); } } } // 寫一個函數 專門判斷是否擊中敵人坦克 public void hitTank(Shot s, EnemyTank et) { // 判斷該坦克的方向 switch (et.direct) { // 敵人坦克的方向是0或2,一致 case 0: case 2: if (s.x > et.x && s.x < et.x + 20 && s.y > et.y && s.y < et.y + 30) { // 擊中(子彈死亡,敵人四萬) s.isLive = false; et.isLive = false; }
break; case 1: case 3: if (s.x > et.x && s.x < et.x + 30 && s.y > et.y && s.y < et.y + 20) { // 擊中(子彈死亡,敵人四萬) s.isLive = false; et.isLive = false; }
break; } } // 畫出坦克的函數 public void drawTank(int x, int y, Graphics g, int direct, int type) { // 坦克類型 switch (type) { case 0: g.setColor(Color.green); break; case 1: g.setColor(Color.yellow); break; } // 方向設置 switch (direct) { // 向上 case 0: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y); break; // 向右 case 1: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x + 30, y + 10); break; // 向下 case 2: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y + 30); break; // 向左 case 3: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x, y + 10); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub // 已更正為順時針 if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) { this.hero.moveUp(); this.hero.setDirect(0); } else if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D) { this.hero.setDirect(1); this.hero.moveRight(); } else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S) { this.hero.moveDown(); this.hero.setDirect(2); } else if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A) { this.hero.moveLeft(); this.hero.setDirect(3); } else if (e.getKeyCode() == KeyEvent.VK_J) { // 將J鍵設置為發出子彈 // 子彈連發,J被按幾下,發幾顆:this.hero.shotEnemy(); // this.repaint();在run函數里,不應該設計在這里 if (this.hero.ss.size() <= 4) { this.hero.shotEnemy(); } } } @Override public void run() { // TODO Auto-generated method stub // 每隔100毫秒去重繪 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 判斷是否擊中(寫在這里,雖然在每次重繪的時候都要調用,但是沒辦法) // 每一顆子彈都要和每個坦克進行匹配 for (int i = 0; i < hero.ss.size(); i++) { // 取出子彈 Shot myShot = hero.ss.get(i); // 判斷子彈是否有效 if (myShot.isLive) { // 取出每個坦克,與它判斷 for (int j = 0; j < ets.size(); j++) { // 取出坦克 EnemyTank et = ets.get(j); if (et.isLive) { this.hitTank(myShot, et); } } } } // 重繪 this.repaint(); } } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } }
2.Menbers類:
package com.fanghua4; import java.util.Vector; //子彈類 class Shot implements Runnable { int x; int y; int direct; // 設置子彈的消亡(默認活着的) boolean isLive = true; // speed要給個初始值1,之前給0,按J鍵,子彈沒有動 int speed = 1; public Shot(int x, int y, int direct) { super(); this.x = x; this.y = y; this.direct = direct; } @Override public void run() { // TODO Auto-generated method stub while (true) { // 設置子彈休息50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } switch (direct) { case 0: y -= speed; break; case 1: x += speed; break; case 2: y += speed; break; case 3: x -= speed; break; } System.out.println("子彈坐標x=" + x + "y=" + y); // 子彈什么時候死亡 // 判斷該子彈是否碰到邊緣 if (x < 0 || x > 400 || y < 0 || y > 300) { this.isLive = false; break; } } } } // 坦克類 class Tank1_2 { int x = 0; int y = 0; // 坦克方向:0表示上,1表示右,2表示下,3表示左 int direct = 0; int speed = 1; // 坦克的顏色 int color; public int getColor() { return color; } public void setColor(int color) { this.color = color; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } 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 Tank1_2(int x, int y) { this.x = x; this.y = y; } } // 敵人的坦克 class EnemyTank extends Tank1_2 { boolean isLive = true; public EnemyTank(int x, int y) { super(x, y); // TODO Auto-generated constructor stub } } // 我的坦克 class Hero1_2 extends Tank1_2 { // 多個子彈,用向量創建 Vector<Shot> ss = new Vector<Shot>(); // 子彈 Shot s = null; public Hero1_2(int x, int y) { super(x, y); } // 坦克開火 public void shotEnemy() { switch (this.direct) { case 0: s = new Shot(x + 10, y, 0); // 把子彈加入向量 ss.add(s); break; case 1: s = new Shot(x + 30, y + 10, 1); // 把子彈加入向量 ss.add(s); break; case 2: s = new Shot(x + 10, y + 30, 2); // 把子彈加入向量 ss.add(s); break; case 3: s = new Shot(x, y + 10, 3); // 把子彈加入向量 ss.add(s); break; } // 啟動子彈線程 Thread t = new Thread(s); t.start(); } public void moveUp() { y -= speed; } public void moveRight() { x += speed; } public void moveDown() { y += speed; } public void moveLeft() { x -= speed; } }
坦克大戰(1.6版本)
1.MyTankGame類
/* * 功能: * 1.實現爆炸效果 * 2.敵人坦克可移動,可以連發子彈 * 3.敵人擊中我的坦克,我爆炸 * 4.擊中第一個坦克爆炸的效果不明顯 */ package com.fanghua5; import java.awt.*; import java.awt.event.KeyEvent; import java.util.Vector; import javax.swing.*; public class MyTankGame1_6 extends JFrame { Mypanel1_2 mp = null; public static void main(String[] args) { new MyTankGame1_6(); } // 構造函數 public MyTankGame1_6() { mp = new Mypanel1_2(); // 啟動mp線程 Thread t = new Thread(mp); t.start(); this.add(mp); // 注冊監聽 this.addKeyListener(mp); this.setSize(400, 300); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } } // 我的面板,拓寬思路:Panel本身就是一個刷新體 class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener, Runnable { // 定義我的坦克 Hero1_2 hero = null; // 定義敵人的坦克(不止一輛,線程安全,集合) Vector<EnemyTank> ets = new Vector<EnemyTank>(); // 定義炸彈集合 Vector<Bomb> bombs = new Vector<Bomb>(); int enSize = 4;// 敵人坦克保持四個 // 定義三張圖片(三張圖片才能組成一顆炸彈) Image image1 = null; Image image2 = null; Image image3 = null; // 構造函數 public Mypanel1_2() { hero = new Hero1_2(10, 10); for (int i = 0; i < enSize; i++) { // 創建一輛敵人的坦克 EnemyTank et = new EnemyTank((i + 1) * 50, 0); et.setColor(0); // 坦克默認反向是0(向上),這里改一下 et.setDirect(2); // 加入 ets.add(et); // 啟動敵人的坦克 Thread t = new Thread(et); t.start(); // 給敵人坦克添加一顆子彈 Shot s = new Shot(et.x + 10, et.y + 30, 2); // 加入給敵人的坦克 et.ss.add(s); Thread t2 = new Thread(s); t2.start(); ets.add(et); } // 初始話圖片,這樣做擊中第一個坦克,爆炸的效果不明顯。下面優化 image1 = Toolkit.getDefaultToolkit().getImage( Panel.class.getResource("/bomb_1.gif")); image2 = Toolkit.getDefaultToolkit().getImage( Panel.class.getResource("/bomb_2.gif")); image3 = Toolkit.getDefaultToolkit().getImage( Panel.class.getResource("/bomb_3.gif")); // 引包:import javax.imageio.ImagesssIO; // try { // image1=ImageIO.read(new File("/bomsb_1.gif")); // image2=ImageIO.read(new File("/bomb_2.gif")); // image3=ImageIO.read(new File("/bomb_3.gif")); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } // 重寫paint函數 public void paint(Graphics g) { // 一定要調用 super.paint(g); g.fillRect(0, 0, 400, 350); // 畫出自己的坦克(將方向填進去) if (hero.isLive == true) { this.drawTank(hero.getX(), hero.getY(), g, this.hero.direct, 1); } // 從ss中取出每一顆子彈,並畫出 for (int i = 0; i < hero.ss.size(); i++) { Shot myShot = hero.ss.get(i); if (myShot != null && myShot.isLive == true) { g.draw3DRect(myShot.x, myShot.y, 1, 1, false); /* * 畫出一顆子彈(后添加 hero.s.isLive==true,節省資源) if (hero.s != null * &&hero.s.isLive == true) { g.draw3DRect(hero.s.x, hero.s.y, * 1, 1,false); } */ } if (myShot.isLive == false) { // 從ss(向量)中刪除該子彈 // hero.ss.remove(i);會報異常。 hero.ss.remove(myShot); } } // 畫出炸彈 for (int i = 0; i < bombs.size(); i++) { // 取出炸彈 Bomb b = bombs.get(i); if (b.life > 6) { g.drawImage(image1, b.x, b.y, 30, 30, this); } else if (b.life > 4) { g.drawImage(image2, b.x, b.y, 30, 30, this); } else { g.drawImage(image3, b.x, b.y, 30, 30, this); } // 讓b的生命值減小 b.lifeDown(); // 如果炸彈生命值為零,就把該炸彈從bombs向量中去掉 if (b.life == 0) { bombs.remove(b); } } // 畫出敵人的坦克 for (int i = 0; i < ets.size(); i++) { EnemyTank et = ets.get(i); if (et.isLive) { this.drawTank(et.getX(), et.getY(), g, et.getDirect(), 0); // 畫出敵人的子彈 for (int j = 0; j < et.ss.size(); j++) { // 取出子彈 Shot enemyShot = et.ss.get(j); if (enemyShot != null && enemyShot.isLive == true) { g.draw3DRect(enemyShot.x, enemyShot.y, 1, 1, false); } if (enemyShot.isLive == false) { // 如果敵人的坦克死亡了,就從Vector中刪除 et.ss.remove(enemyShot); } } } } } // 判斷敵人的子彈是否擊中我 public void hitMe() { // 取出每一個敵人的坦克 for (int i = 0; i < this.ets.size(); i++) { // 取出坦克 EnemyTank et = ets.get(i); // 取出每一顆子彈 for (int j = 0; j < et.ss.size(); j++) { // 取出子彈 Shot enemyShot = et.ss.get(j); this.hitTank(enemyShot, hero); } } } // 判斷我是否擊中敵人的坦克 public void hitEnemyTank() { // 判斷是否被擊中敵人的坦克 for (int i = 0; i < hero.ss.size(); i++) { // 取出我的子彈與敵人坦克匹配 Shot myShot = hero.ss.get(i); // 判斷子彈是否有效 if (myShot.isLive) { // 取出每個坦克,與它判斷 for (int j = 0; j < ets.size(); j++) { // 取出坦克 EnemyTank et = ets.get(j); if (et.isLive) { this.hitTank(myShot, et); } } } } } // 寫一個函數 專門判斷是否擊中敵人坦克(原來第二參數: EnemyTank et) public void hitTank(Shot s, Tank1_2 et) { // 判斷該坦克的方向 switch (et.direct) { // 敵人坦克的方向是0或2,一致 case 0: case 2: if (s.x >= et.x && s.x <= et.x + 20 && s.y >= et.y && s.y <= et.y + 30) { // 擊中(子彈死亡,敵人死亡) s.isLive = false; et.isLive = false; // 創建一顆炸彈,放入Vector中 Bomb b = new Bomb(et.x, et.y); // 放入 bombs.add(b); }
break; case 1: case 3: if (s.x >= et.x && s.x <= et.x + 30 && s.y >= et.y && s.y <= et.y + 20) { // 擊中(子彈死亡,敵人死亡) s.isLive = false; et.isLive = false; // 創建一顆炸彈,放入Vector中 Bomb b = new Bomb(et.x, et.y); // 放入 bombs.add(b); }
break; } } // 畫出坦克的函數 public void drawTank(int x, int y, Graphics g, int direct, int type) { // 坦克類型 switch (type) { case 0: g.setColor(Color.green); break; case 1: g.setColor(Color.yellow); break; } // 方向設置 switch (direct) { // 向上 case 0: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y); break; // 向右 case 1: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x + 30, y + 10); break; // 向下 case 2: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y + 30); break; // 向左 case 3: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x, y + 10); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub // 已更正為順時針 if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) { this.hero.moveUp(); this.hero.setDirect(0); } else if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D) { this.hero.setDirect(1); this.hero.moveRight(); } else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S) { this.hero.moveDown(); this.hero.setDirect(2); } else if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A) { this.hero.moveLeft(); this.hero.setDirect(3); } else if (e.getKeyCode() == KeyEvent.VK_J) { // 將J鍵設置為發出子彈 // 子彈連發,J被按幾下,發幾顆:this.hero.shotEnemy(); // this.repaint();在run函數里,不應該設計在這里 if (this.hero.ss.size() <= 4) { this.hero.shotEnemy(); } } } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void run() { // TODO Auto-generated method stub // 每隔100毫秒去重繪 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 判斷是否擊中(寫在這里,雖然在每次重繪的時候都要調用,但是沒辦法) // 每一顆子彈都要和每個坦克進行匹配 for (int i = 0; i < hero.ss.size(); i++) { // 取出子彈 Shot myShot = hero.ss.get(i); // 判斷子彈是否有效 if (myShot.isLive) { // 取出每個坦克,與它判斷 for (int j = 0; j < ets.size(); j++) { // 取出坦克 EnemyTank et = ets.get(j); if (et.isLive) { this.hitTank(myShot, et); } } } } this.hitEnemyTank(); this.hitMe(); // 重繪 this.repaint(); } } }
2.Menbers類:
package com.fanghua5; import java.util.Vector; //炸彈類(沒必要定義為線程,因為它不會移動,沒有坐標改變) class Bomb { // 定義炸彈的坐標 int x, y; int life = 9;// 炸彈的生命(三張圖片) // 可以看出isLive很有用,它可以決定類(或者對象)要不要展現在面板上 boolean isLive = true; public Bomb(int x, int y) { this.x = x; this.y = y; } // 炸彈減少生命值 public void lifeDown() { if (life > 0) { life--; } else { this.isLive = false; } } } // 子彈類 class Shot implements Runnable { int x; int y; int direct; // 設置子彈的消亡(默認活着的) boolean isLive = true; // speed要給個初始值1,之前給0,按J鍵,子彈沒有動 int speed = 1; public Shot(int x, int y, int direct) { super(); this.x = x; this.y = y; this.direct = direct; } @Override public void run() { // TODO Auto-generated method stub while (true) { // 設置子彈休息50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } switch (direct) { case 0: y -= speed; break; case 1: x += speed; break; case 2: y += speed; break; case 3: x -= speed; break; } // 測試用:System.out.println("子彈坐標x=" + x + "y=" + y); // 子彈什么時候死亡 // 判斷該子彈是否碰到邊緣 if (x < 0 || x > 400 || y < 0 || y > 300) { this.isLive = false; break; } } } } // 坦克類 class Tank1_2 { int x = 0; int y = 0; boolean isLive=true; // 坦克方向:0表示上,1表示右,2表示下,3表示左 int direct = 0; int speed = 1; // 坦克的顏色 int color; public int getColor() { return color; } public void setColor(int color) { this.color = color; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } 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 Tank1_2(int x, int y) { this.x = x; this.y = y; } } // 敵人的坦克(做成線程,會移動) class EnemyTank extends Tank1_2 implements Runnable { //從父類繼承了,去掉:boolean isLive = true; int times = 0;// 讓time累積 // 定義向量,可以存放敵人的子彈 Vector<Shot> ss = new Vector<Shot>(); // 敵人添加子彈,應該剛剛創建坦克和敵人子彈死亡后 public EnemyTank(int x, int y) { super(x, y); } @Override public void run() { // TODO Auto-generated method stub while (true) { try { // 設置坦克休息一會 Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } switch (this.direct) { case 0: // 說明坦克正在向上運動(繼續往上走,符合實際) // y -= speed;設置坦克平滑移動的效果 for (int i = 0; i < 30; i++) { if (y > 0) { y -= speed; } try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; case 1: for (int i = 0; i < 30; i++) { // 這里注意坐標起點問題不是(400x300) if (x < 350) { x += speed; } try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; case 2: for (int i = 0; i < 30; i++) { if (y < 250) { y += speed; } try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; case 3: for (int i = 0; i < 30; i++) { if (x > 0) { x -= speed; } try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; } this.times++; // 設置3秒發一顆子彈 if (times % 2 == 0) { if (isLive) { if (ss.size() < 5) { Shot s = null; // 沒有子彈,添加 switch (direct) { case 0: s = new Shot(x + 10, y, 0); ss.add(s); break; case 1: s = new Shot(x + 30, y + 10, 1); ss.add(s); break; case 2: s = new Shot(x + 10, y + 30, 2); ss.add(s); break; case 3: s = new Shot(x, y + 10, 3); ss.add(s); break; } // 啟動子彈線程 Thread t = new Thread(s); t.start(); } } } // 讓坦克隨機產生一個新的方向 this.direct = (int) (Math.random() * 4); // 判斷敵人坦克是否死亡了(雙等號) if (this.isLive == false) { // 讓坦克死亡,后退出線程 return; } } } } // 我的坦克 class Hero1_2 extends Tank1_2 { // 多個子彈,用向量創建 Vector<Shot> ss = new Vector<Shot>(); // 子彈 Shot s = null; public Hero1_2(int x, int y) { super(x, y); } // 坦克開火 public void shotEnemy() { switch (this.direct) { case 0: s = new Shot(x + 10, y, 0); // 把子彈加入向量 ss.add(s); break; case 1: s = new Shot(x + 30, y + 10, 1); // 把子彈加入向量 ss.add(s); break; case 2: s = new Shot(x + 10, y + 30, 2); // 把子彈加入向量 ss.add(s); break; case 3: s = new Shot(x, y + 10, 3); // 把子彈加入向量 ss.add(s); break; } // 啟動子彈線程(創建線程,趕緊傳參,我在這里吃了大虧!) Thread t = new Thread(s); t.start(); } public void moveUp() { y -= speed; } public void moveRight() { x += speed; } public void moveDown() { y += speed; } public void moveLeft() { x -= speed; } }
1.6版本(圖:基本功能都已經實現)
之后,我還會繼續寫一篇博客,來記錄該坦克大戰的最終版本(2.0),這個坦克大戰的項目利用Java圖形界面來實現坦克的繪制,畫質比較粗糙,部分的坐標沒有那么精確。