标签:
总结:由于这几天快过年比较忙然后没怎么写,写代码途中一些经验总结现在给忘记了。这次的小项目感觉比上次写的思路清楚了点。没有之前第一次写那么逻辑混乱,结构也搞的比之前的要好,添加功能比较容易。学习了之前的经验,操作对象的方法由对象本身提供。不过这次小项目还有不足和不完善之处,有些可以做的功能没有实现,比如游戏存档,这里应该可以用下对象的序列化,还有游戏难度的设置也可以写个文件弄出来。要过年了,到处走亲戚没什么心思写了,这里只能留个尾巴了。
前言:之前的及时通信项目完成后感觉线程方面和对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
想到什么再更吧。。
标签:
原文地址:http://www.cnblogs.com/ganhang-acm/p/5183616.html