總結:由於這幾天快過年比較忙然后沒怎么寫,寫代碼途中一些經驗總結現在給忘記了。這次的小項目感覺比上次寫的思路清楚了點。沒有之前第一次寫那么邏輯混亂,結構也搞的比之前的要好,添加功能比較容易。學習了之前的經驗,操作對象的方法由對象本身提供。不過這次小項目還有不足和不完善之處,有些可以做的功能沒有實現,比如游戲存檔,這里應該可以用下對象的序列化,還有游戲難度的設置也可以寫個文件弄出來。要過年了,到處走親戚沒什么心思寫了,這里只能留個尾巴了。
前言:之前的及時通信項目完成后感覺線程方面和對java的運用還不是很熟悉,在另外一個學習視頻中看到一個做坦克游戲的項目視頻,便想自己試着做做看,先看視頻把游戲規則什么的都搞清楚。然后開始一步一步實現。
主要功能步驟如下
* 1、畫出坦克
* 2、我的坦克可以上下移動
* 3、可以發射子彈,子彈連發(或者最多5發)
* 4、當我的坦克擊中敵人坦克時,敵人坦克消失(或者爆炸效果)
* 5、我被擊中也顯示爆炸效果。
* 6、游戲開始和游戲介紹選項
這次游戲界面沒有進行設計,直接在一個frame上放個panel。
游戲設計仿mvc 這里不做詳細介紹了直接上代碼
model包

package com.gh.model; /** * 爆炸類 * 考慮到同時爆炸定義個類 * @author ganhang * */ public class Bomb { private int x; private int y;//坐標 public boolean islive=true; private int time=9;//炸彈生命 public Bomb() { super(); } public Bomb(int x, int y) { super(); this.x = x; this.y = y; } 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 getTime() { return time; } //生命遞減 public void livedown(){ if(time>0){ time--; }else{ islive=false; } } }

1 package com.gh.model; 2 /** 3 * 子彈類, 4 * 因為多個子彈同時運動所以需要個內部類做線程 5 * @author ganhang 6 * 7 */ 8 public class Bullet { 9 private int x; 10 private int y; 11 private int speed; 12 private int drect; 13 public boolean islive=true; 14 public Bullet(int x, int y, int speed, int drect) { 15 super(); 16 this.x = x; 17 this.y = y; 18 this.speed = speed; 19 this.drect = drect; 20 new Thread(new BulletThread()).start(); 21 } 22 public Bullet() { 23 super(); 24 } 25 26 public int getX() { 27 return x; 28 } 29 30 public void setX(int x) { 31 this.x = x; 32 } 33 34 public int getY() { 35 return y; 36 } 37 38 public void setY(int y) { 39 this.y = y; 40 } 41 42 class BulletThread implements Runnable { 43 @Override 44 public void run() { 45 while (true) { 46 try { 47 Thread.sleep(50); 48 } catch (Exception e) { 49 e.printStackTrace(); 50 } 51 switch (drect) {//判斷方向坐標移動 52 case 0: 53 y-=speed; 54 break; 55 case 1: 56 x+=speed; 57 break; 58 case 2: 59 y+=speed; 60 break; 61 case 3: 62 x-=speed; 63 break; 64 } 65 if (x < 0 || y < 0 || x > 500 || y > 500||!islive) { 66 islive=false; 67 break; 68 } 69 } 70 } 71 } 72 }

package com.gh.model; /** * 地圖坐標標記 * 防止敵方坦克重疊 * @author ganhang * */ public class Map { public int[][] location=new int[500][500]; public Map() { for (int i = 0; i < 500; i++) { for (int j = 0; j <500; j++) { location[i][j]=0; } } } }
坦克類,剛才上傳發現點問題暫時沒改

1 package com.gh.model; 2 3 import java.util.Vector; 4 /** 5 * 坦克類 6 * 每個坦克就是一個線程, 7 * 這里自己坦克並沒有啟動線程 8 * @author ganhang 9 * 10 */ 11 public class Tank implements Runnable { 12 private int x = 0; 13 private int y = 0;// 坐標 14 private int drect = 0;// 方向 0向上,1向右,2向下,3向左; 15 private int type = 0;// 坦克類型 0表示自己 16 private int speed = 3;// 速度 17 public Vector<Bullet> mybs = new Vector<Bullet>();// 子彈集 18 private Bullet myBullet;// 子彈 19 public boolean islive = true; 20 private Map map; 21 public boolean start = true; 22 public Map getMap() { 23 return map; 24 } 25 26 public void setMap(Map map) { 27 this.map = map; 28 } 29 30 public Tank(int x, int y, int drect, int type) { 31 super(); 32 this.x = x; 33 this.y = y; 34 this.drect = drect; 35 this.type = type; 36 } 37 38 public Tank() { 39 super(); 40 } 41 42 public Bullet getMyBullet() { 43 return myBullet; 44 } 45 46 public int getSpeed() { 47 return speed; 48 } 49 50 public void setSpeed(int speed) { 51 this.speed = speed; 52 } 53 54 public int getX() { 55 return x; 56 } 57 58 public int getDrect() { 59 return drect; 60 } 61 62 public void setDrect(int drect) { 63 this.drect = drect; 64 } 65 66 public int getType() { 67 return type; 68 } 69 70 public void setType(int type) { 71 this.type = type; 72 } 73 74 public void setX(int x) { 75 this.x = x; 76 } 77 78 public int getY() { 79 return y; 80 } 81 82 public void setY(int y) { 83 this.y = y; 84 } 85 86 public void moveUp() { 87 if (y - speed < 0) 88 y = 0; 89 else { 90 y -= speed; 91 map.location[x][y]=1;//標記此坦克坐標在地圖上防止其他坦克過來占用導致重疊 92 // 這里只標記了坦克坐標那一個點,會有bug,部分坦克還是有重疊現象, 93 // 這里可以遍歷整個坦克坐標(x到x+20,y到y+30)設置標記。 94 // for(int i=x;i<x+20;i++){ 95 // for (int j = y; j < y+30; j++) { 96 // map.location[x][y]=1; 97 // } 98 // } 99 } 100 } 101 102 public void moveDown() { 103 if (y + speed > 470) 104 y = 470; 105 else { 106 y += speed; 107 map.location[x][y]=1; 108 } 109 } 110 111 public void moveRight() { 112 if (x + speed > 470) 113 x = 470; 114 else { 115 x += speed; 116 map.location[x][y]=1; 117 } 118 } 119 120 public void moveLeft() { 121 if (x - speed < 0) 122 x = 0; 123 else { 124 x -= speed; 125 map.location[x][y]=1; 126 } 127 } 128 129 public void shot() { 130 switch (drect) { 131 case 0: 132 myBullet = new Bullet(x + 10, y, 5, 0); 133 mybs.add(myBullet); 134 break; 135 case 1: 136 myBullet = new Bullet(x + 30, y + 10, 5, 1); 137 mybs.add(myBullet); 138 break; 139 case 2: 140 myBullet = new Bullet(x + 10, y + 30, 5, 2); 141 mybs.add(myBullet); 142 break; 143 case 3: 144 myBullet = new Bullet(x, y + 10, 5, 3); 145 mybs.add(myBullet); 146 break; 147 } 148 } 149 150 @Override 151 public void run() { 152 while (islive) { 153 if (start) { 154 int step; 155 int s; 156 try { 157 switch (drect) { 158 case 0: 159 step = (int) (Math.random() * 30); 160 for (int i = 0; i < step; i++) { 161 moveUp(); 162 if (y <= 0) 163 break;// 撞牆跳出循環 164 if (y >= 30)// 仿數組越界 165 if (map.location[x][y - 30] == 1 || map.location[x][y - 20] == 1) { 166 map.location[x][y - 30] = 0;//這里沒分開判斷 167 map.location[x][y - 20] = 0; 168 break; 169 } 170 Thread.sleep(80); 171 } 172 break; 173 case 1: 174 step = (int) (Math.random() * 30); 175 for (int i = 0; i < step; i++) { 176 moveRight(); 177 if (x >= 500) 178 break; 179 if (x < 470) 180 if (map.location[x + 20][y] == 1 || map.location[x + 30][y] == 1) { 181 map.location[x + 20][y] = 0; 182 map.location[x + 30][y] = 0; 183 break; 184 } 185 Thread.sleep(80); 186 } 187 break; 188 case 2: 189 step = (int) (Math.random() * 30); 190 for (int i = 0; i < step; i++) { 191 moveDown(); 192 if (y >= 500) 193 break; 194 if (y < 470) 195 if (map.location[x][y + 30] == 1 || map.location[x][y + 20] == 1) { 196 map.location[x][y + 30] = 0; 197 map.location[x][y + 20] = 0; 198 break; 199 } 200 Thread.sleep(80); 201 } 202 break; 203 case 3: 204 step = (int) (Math.random() * 30); 205 for (int i = 0; i < step; i++) { 206 moveLeft(); 207 if (x <= 0) 208 break; 209 if (x >= 30) 210 if (map.location[x - 20][y] == 1 || map.location[x - 30][y] == 1) { 211 map.location[x - 20][y] = 0; 212 map.location[x - 30][y] = 0; 213 break; 214 } 215 Thread.sleep(80); 216 } 217 break; 218 } 219 } catch (InterruptedException e) { 220 e.printStackTrace(); 221 } 222 drect = (int) (Math.random() * 4);// 隨機方向 223 s = (int) (Math.random() * 10); 224 if (s > 8) { 225 shot(); 226 } 227 } 228 } 229 } 230 }
view包

1 package com.gh.view; 2 3 import java.awt.Color; 4 import java.awt.Graphics; 5 import java.awt.Image; 6 import java.awt.Toolkit; 7 import java.util.Vector; 8 9 import javax.swing.JOptionPane; 10 import javax.swing.JPanel; 11 12 import com.gh.model.Bomb; 13 import com.gh.model.Bullet; 14 import com.gh.model.Map; 15 import com.gh.model.Tank; 16 17 /** 18 * 游戲顯示面板 19 * 20 * @author ganhang 21 * 22 */ 23 24 public class Mypanel extends JPanel implements Runnable { 25 public Tank mytank = null;// 我的坦克 26 Tank ek = null; 27 Image img; 28 Vector<Tank> eks = new Vector<Tank>();//地方坦克集 29 Vector<Bomb> bs = new Vector<Bomb>();//爆炸集合 30 Map map = new Map(); 31 32 public Mypanel() { 33 mytank = new Tank(200, 200, 0, 0); 34 mytank.setMap(map); 35 // 創建敵人坦克 36 for (int i = 0; i < 17; i++) { 37 ek = new Tank(i * 30, 10, 2, 1); 38 eks.add(ek); 39 ek.setMap(map); 40 new Thread(ek).start(); 41 } 42 img = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/1.png")); 43 } 44 45 @Override 46 public void paint(Graphics g) { 47 super.paint(g); 48 // 畫背景 49 g.fillRect(0, 0, 500, 500); 50 // 畫自己的坦克 51 if (mytank.islive) 52 drawTank(mytank.getX(), mytank.getY(), g, mytank.getDrect(), mytank.getType()); 53 // 畫自己的子彈 54 for (int i = 0; i < mytank.mybs.size(); i++) {// 循環時刪除集合時,不要用foreach,用for 55 Bullet b = new Bullet(); 56 b = mytank.mybs.get(i); 57 if (b.islive) { 58 g.setColor(Color.white); 59 g.fill3DRect(b.getX(), b.getY(), 2, 2, false); 60 } else 61 mytank.mybs.remove(b); 62 } 63 // 畫敵人坦克 64 for (int i = 0; i < eks.size(); i++) { 65 Tank ek = new Tank(); 66 ek = eks.get(i); 67 if (ek.islive) 68 drawEnemyTank(ek.getX(), ek.getY(), ek.getDrect(), g); 69 // 畫敵人子彈 70 for (int j = 0; j < ek.mybs.size(); j++) { 71 Bullet eb = new Bullet(); 72 eb = ek.mybs.get(j); 73 if (eb.islive) { 74 g.setColor(Color.green); 75 g.fill3DRect(eb.getX(), eb.getY(), 2, 2, false); 76 } else 77 ek.mybs.remove(eb); 78 } 79 } 80 // 畫爆炸,這里有個bug第一次爆炸沒有爆炸效果圖出來,檢查原因是只一閃而過 81 // 添加休眠好了點,不過影響后面爆炸效果,不明白為什么第一次畫得快些 82 for (int i = 0; i < bs.size(); i++) { 83 // System.out.println(bs.size()); 84 Bomb bb = bs.get(i); 85 if (bb.islive) { 86 if (bb.getTime() > 6) { 87 try { 88 Thread.sleep(50); 89 } catch (Exception e) { 90 e.printStackTrace(); 91 } 92 g.drawImage(img, bb.getX(), bb.getY(), 30, 30, this); 93 } else if (bb.getTime() > 3) { 94 g.drawImage(img, bb.getX(), bb.getY(), 15, 15, this); 95 } else if (bb.getTime() > 0) { 96 g.drawImage(img, bb.getX(), bb.getY(), 1, 1, this); 97 } 98 } 99 bb.livedown(); 100 if (bb.getTime() == 0) 101 bs.remove(bb); 102 } 103 } 104 105 public boolean isHitEnemy(Bullet b, Tank ek) { 106 if (ek.getDrect() == 0 || ek.getDrect() == 2) { 107 // 坦克豎着時寬20,高30 108 if (b.getX() >= ek.getX() && b.getX() <= ek.getX() + 20 && b.getY() >= ek.getY() 109 && b.getY() <= ek.getY() + 30) { 110 b.islive = false; 111 ek.islive = false; 112 Bomb bb = new Bomb(ek.getX(), ek.getY()); 113 bs.add(bb); 114 return true; 115 } 116 return false; 117 } else {// 橫着寬30,高20; 118 if (b.getX() >= ek.getX() && b.getX() <= ek.getX() + 30 && b.getY() >= ek.getY() 119 && b.getY() <= ek.getY() + 20) { 120 ek.islive = false; 121 b.islive = false; 122 Bomb bb = new Bomb(ek.getX(), ek.getY()); 123 bs.add(bb); 124 return true; 125 } 126 return false; 127 } 128 } 129 130 public void drawEnemyTank(int x, int y, int drect, Graphics g) { 131 drawTank(x, y, g, drect, 1); 132 } 133 134 public void drawTank(int x, int y, Graphics g, int drect, int type) { 135 switch (type) { 136 case 0: 137 g.setColor(Color.cyan); 138 break; 139 case 1: 140 g.setColor(Color.GREEN); 141 default: 142 break; 143 } 144 switch (drect) { 145 case 0: 146 // 坦克向上時寬20,高30 147 g.fill3DRect(x, y, 5, 30, false); 148 g.fill3DRect(x + 15, y, 5, 30, false); 149 g.fill3DRect(x + 5, y + 9, 10, 15, false); 150 g.drawLine(x + 10, y + 14, x + 10, y); 151 break; 152 case 1: 153 // 坦克向右時寬30,高20 154 g.fill3DRect(x, y, 30, 5, false); 155 g.fill3DRect(x, y + 15, 30, 5, false); 156 g.fill3DRect(x + 7, y + 5, 15, 10, false); 157 g.drawLine(x + 13, y + 10, x + 30, y + 10); 158 break; 159 case 2: 160 g.fill3DRect(x, y, 5, 30, false); 161 g.fill3DRect(x + 15, y, 5, 30, false); 162 g.fill3DRect(x + 5, y + 7, 10, 15, false); 163 g.drawLine(x + 10, y + 12, x + 10, y + 30); 164 break; 165 case 3: 166 g.fill3DRect(x, y, 30, 5, false); 167 g.fill3DRect(x, y + 15, 30, 5, false); 168 g.fill3DRect(x + 8, y + 5, 15, 10, false); 169 g.drawLine(x, y + 10, x + 13, y + 10); 170 break; 171 } 172 } 173 174 @Override 175 public void run() { 176 while (true) { 177 try { 178 Thread.sleep(50);// 畫板刷新頻率 179 } catch (InterruptedException e) { 180 e.printStackTrace(); 181 } 182 // 判斷自己坦克的子彈是否擊中敵人坦克 183 for (int i = 0; i < mytank.mybs.size(); i++) { 184 Bullet mb = new Bullet(); 185 mb = mytank.mybs.get(i); 186 if (mb.islive) { 187 for (int j = 0; j < eks.size(); j++) { 188 Tank ek = new Tank(); 189 ek = eks.get(j); 190 if (ek.islive) { 191 isHitEnemy(mb, ek); 192 } 193 } 194 } 195 } 196 // 判斷敵方坦克 的子彈是否擊中我方坦克 197 for (int i = 0; i < eks.size(); i++) { 198 Tank et = new Tank(); 199 et = eks.get(i); 200 for (int j = 0; j < et.mybs.size(); j++) {// 這里寫錯ek查到死。。。 201 Bullet etb = new Bullet(); 202 etb = et.mybs.get(j); 203 if (etb.islive) { 204 isHitEnemy(etb, mytank); 205 } 206 } 207 } 208 this.repaint();// 刷新 209 if (!mytank.islive) { 210 JOptionPane.showMessageDialog(this, "你被GG"); 211 mytank.islive = true; 212 } 213 } 214 } 215 }
開始游戲類

1 package com.gh.view; 2 3 import java.awt.EventQueue; 4 /** 5 * 1、畫出坦克 6 * 2、我的坦克可以上下移動 7 * 3、可以發射子彈,子彈連發(或者最多5發) 8 * 4、當我的坦克擊中敵人坦克時,敵人坦克消失(或者爆炸效果) 9 * 5、我被擊中也顯示爆炸效果。 10 * 6、游戲開始選項 11 * @author ganhang 12 * 13 */ 14 public class TankGame { 15 private JFrame frame; 16 /** 17 * Launch the application. 18 */ 19 public static void main(String[] args) { 20 EventQueue.invokeLater(new Runnable() { 21 public void run() { 22 try { 23 TankGame window = new TankGame(); 24 window.frame.setVisible(true); 25 } catch (Exception e) { 26 e.printStackTrace(); 27 } 28 } 29 }); 30 } 31 32 /** 33 * Create the application. 34 */ 35 public TankGame() { 36 initialize(); 37 } 38 39 /** 40 * Initialize the contents of the frame. 41 */ 42 private void initialize() { 43 frame = new JFrame(); 44 frame.setTitle("\u5766\u514B\u5927\u6218"); 45 frame.setBounds(450, 70, 600, 600); 46 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 47 48 JMenuBar menuBar = new JMenuBar(); 49 frame.setJMenuBar(menuBar); 50 51 JMenu mnNewMenu = new JMenu("\u9009\u9879"); 52 mnNewMenu.setFont(new Font("微軟雅黑", Font.PLAIN, 13)); 53 menuBar.add(mnNewMenu); 54 55 JMenuItem mntmNewMenuItem = new JMenuItem("開始游戲"); 56 mntmNewMenuItem.addActionListener(new ActionListener() { 57 public void actionPerformed(ActionEvent e) { 58 Mypanel mp = new Mypanel(); 59 Thread t=new Thread(mp); 60 t.start(); 61 frame.getContentPane().add(mp, BorderLayout.CENTER); 62 frame.addKeyListener(new KeyListen(mp)); 63 frame.setVisible(true); 64 } 65 }); 66 67 mntmNewMenuItem.setFont(new Font("微軟雅黑", Font.PLAIN, 12)); 68 mnNewMenu.add(mntmNewMenuItem); 69 70 JMenu mnNewMenu_1 = new JMenu("\u6E38\u620F\u8BF4\u660E"); 71 mnNewMenu_1.setFont(new Font("微軟雅黑", Font.PLAIN, 13)); 72 menuBar.add(mnNewMenu_1); 73 74 JMenuItem mntmNewMenuItem_1 = new JMenuItem("\u5173\u4E8E\u6E38\u620F"); 75 mntmNewMenuItem_1.addActionListener(new ActionListener() { 76 public void actionPerformed(ActionEvent e) { 77 JOptionPane.showMessageDialog(frame, "上:W, 下:A ,左:S, 右:D ,射擊:空格\n Made by Ganhang");; 78 } 79 }); 80 mntmNewMenuItem_1.setFont(new Font("微軟雅黑", Font.PLAIN, 12)); 81 mnNewMenu_1.add(mntmNewMenuItem_1); 82 83 } 84 85 }
control包 這里放的事件監聽主要業務邏輯在畫板類和模型類里寫了

1 package com.gh.control; 2 3 import java.awt.event.KeyEvent; 4 import java.awt.event.KeyListener; 5 6 import com.gh.view.Mypanel; 7 8 /** 9 * 事件監聽 10 * 這里有個控制最大發射子彈數 11 * @author ganhang 12 */ 13 public class KeyListen implements KeyListener{ 14 private Mypanel mp=null; 15 16 public KeyListen(Mypanel mp) { 17 super(); 18 this.mp = mp; 19 } 20 @Override 21 public void keyTyped(KeyEvent e) { 22 23 } 24 25 @Override 26 public void keyPressed(KeyEvent e) { 27 //方向鍵監聽 28 if(e.getKeyCode()==KeyEvent.VK_W){ 29 mp.mytank.setDrect(0); 30 mp.mytank.moveUp(); 31 }else if(e.getKeyCode()==KeyEvent.VK_S){ 32 mp.mytank.setDrect(2); 33 mp.mytank.moveDown(); 34 }else if(e.getKeyCode()==KeyEvent.VK_D){ 35 mp.mytank.setDrect(1); 36 mp.mytank.moveRight(); 37 }else if(e.getKeyCode()==KeyEvent.VK_A){ 38 mp.mytank.setDrect(3); 39 mp.mytank.moveLeft(); 40 } 41 //發射子彈監聽 42 if(e.getKeyCode()==KeyEvent.VK_SPACE){ 43 if(mp.mytank.mybs.size()<5) 44 mp.mytank.shot(); 45 } 46 mp.repaint(); 47 } 48 @Override 49 public void keyReleased(KeyEvent e) { 50 // TODO Auto-generated method stub 51 52 } 53 }
github:https://github.com/ganhang/My_TankGame
想到什么再更吧。。