Java坦克大战(四)
这是我的坦克游戏大战的最后一版,里面添加很多新的功能。这个坦克大战的有很多不足之处,但是对于初学者来说依然是一个很好的练习项目,从中我们可以学习Java基础知识,将知识与项目结合,学习面向对象编程思想,最主要的是编程的逻辑练习,代码往往不像是写文章从上到下一气呵成完成,中间很可能为增加一个功能来添加一个类一个方法等,中间有很多细节需要我们考虑。文章最后会附加该坦克大战涉及的相关素材。
功能:
1.防止坦克重叠
2.可以分关(闪烁效果)
3.记录成绩(小型游戏都是写在文件中)
4.存盘退出,可以记录但是敌人坦克的坐标
5.可以恢复上局,继续玩
6.坦克的声音
坦克大战最终版本(2.1)
MyTanKGame类:
/* * 功能: * 1.防止坦克重叠 * 2.可以分关(闪烁效果) * 3.记录成绩(小型游戏都是写在文件中) * 4.存盘退出,可以记录但是敌人坦克的坐标 * 5.可以恢复上局,继续玩 * 6.坦克的声音 */ package com.fanghua6; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.Vector; import javax.swing.*; public class MyTankGame1_7 extends JFrame implements ActionListener { Mypanel1_2 mp = null; // 定义开始面板 MyStartPanel msp = null; // 做出菜单 JMenuBar jmb = null; // 开始游戏 JMenu jm1 = null; JMenuItem jmi1 = null; // 退出游戏 JMenuItem jmi2 = null; // 存盘退出 JMenuItem jmi3 = null; JMenuItem jmi4 = null; public static void main(String[] args) { new MyTankGame1_7(); } // 构造函数 public MyTankGame1_7() { // mp = new Mypanel1_2(); // 启动mp线程 // Thread t = new Thread(mp); // t.start(); // this.add(mp); // 注册监听 // this.addKeyListener(mp); // 创建菜单及菜单选项 jmb = new JMenuBar(); jm1 = new JMenu("游戏(G)"); jm1.setMnemonic('G'); jmi1 = new JMenuItem("开始新游戏(N)"); jmi1.setMnemonic('N'); jmi2 = new JMenuItem("退出游戏(E)"); jmi2.setMnemonic('E'); jmi3 = new JMenuItem("存盘退出(C)"); jmi3.setMnemonic('C'); jmi4 = new JMenuItem("继续上局(S)"); jmi4.setMnemonic('S'); // jmi1相应 jmi1.addActionListener(this); jmi1.setActionCommand("newgame"); jmi2.addActionListener(this); jmi2.setActionCommand("exit"); jmi3.addActionListener(this); jmi3.setActionCommand("saveExit"); jmi4.addActionListener(this); jmi4.setActionCommand("conGame"); jm1.add(jmi1); jm1.add(jmi2); jm1.add(jmi3); jm1.add(jmi4); jmb.add(jm1); // 加了开始面板(上面的全注释掉) msp = new MyStartPanel(); this.add(msp); // 启动msp面板 Thread t = new Thread(msp); t.start(); this.setJMenuBar(jmb); this.setSize(600, 500); this.setTitle("我的坦克大战"); ImageIcon icon = new ImageIcon("images\\tanke.png"); this.setIconImage(icon.getImage()); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub // 对用户不同的点击做出不同的处理 if (e.getActionCommand().equals("newgame")) { // 创建战场面板 mp = new Mypanel1_2("newGame"); // 启动mp线程 Thread t = new Thread(mp); t.start(); // 先把msp删掉,再加mp this.remove(msp); this.add(mp); // 注册监听 this.addKeyListener(mp); // 显示,刷新JFrame(这个很重要) this.setVisible(true); } else if (e.getActionCommand().equals("exit")) { // 用户退出系统的菜单(保存击毁敌人数量) Recorder.keepRecording(); System.exit(0); }// 对存盘退出的处理 else if (e.getActionCommand().equals("saveExit")) { /* * 当时这里这样处理的,大错特错。new两次 工作 new Recorder().setEts(mp.ets);new * 保存击毁敌人的数量和敌人的坐标 Recorder().keepRecAndEnemyTank(); */ // 工作 Recorder rd = new Recorder(); rd.setEts(mp.ets); // 保存击毁敌人的数量和敌人的坐标 rd.keepRecAndEnemyTank(); // 退出 System.exit(0); } else if (e.getActionCommand().equals("conGame")) { // 继续游戏 // 创建战场面板 mp = new Mypanel1_2("con"); // 不在这:mp.nodes = new Recorder().getNodesAndEnNums(); // 启动mp线程 Thread t = new Thread(mp); t.start(); // 先把msp删掉,再加mp this.remove(msp); this.add(mp); // 注册监听 this.addKeyListener(mp); // 显示,刷新JFrame(这个很重要) this.setVisible(true); } } } // 提示面板(用线程来实现闪烁效果) class MyStartPanel extends JPanel implements Runnable { // 做一个开关 int times = 0; public void paint(Graphics g) { super.paint(g); g.fillRect(0, 0, 400, 300); // 提示信息 if (times % 2 == 0) { g.setColor(Color.yellow); // 开关信息的字体 Font myFont = new Font("华文新魏", Font.BOLD, 30); g.setFont(myFont);// 别忘了 g.drawString("stage: 1", 140, 200); } } @Override public void run() { // TODO Auto-generated method stub while (true) { // 休眠 try { Thread.sleep(500); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } times++; // 重画 this.repaint(); } } } // 我的面板,拓宽思路:Panel本身就是一个刷新体 class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener, Runnable { // 定义我的坦克 Hero1_2 hero = null; // 判断是续上局,还是新游戏 // 定义敌人的坦克(不止一辆,线程安全,集合) Vector<EnemyTank> ets = new Vector<EnemyTank>(); Vector<Node> nodes = new Vector<Node>(); // 定义炸弹集合 Vector<Bomb> bombs = new Vector<Bomb>(); int enSize = 4;// 敌人坦克保持4个 // 定义三张图片(三张图片才能组成一颗炸弹) Image image1 = null; Image image2 = null; Image image3 = null; // 构造函数(续局,变成含参的构造函数) public Mypanel1_2(String flag) { // 恢复记录(写在这里) Recorder.getRecoring(); hero = new Hero1_2(70, 100); if (flag.equals("newGame")) { // 初始化敌人的坦克 for (int i = 0; i < enSize; i++) { // 创建一辆敌人的坦克 EnemyTank et = new EnemyTank((i + 1) * 50, 0); et.setColor(0); // 坦克默认反向是0(向上),这里改一下 et.setDirect(2); // 将MyPanel的敌人坦克向量交给该敌人坦克 et.setEts(ets); // 启动敌人的坦克 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); } } else { nodes = new Recorder().getNodesAndEnNums(); for (int i = 0; i < nodes.size(); i++) { Node node = nodes.get(i); // 创建一辆敌人的坦克 EnemyTank et = new EnemyTank(node.x, node.y); et.setColor(0); et.setDirect(node.direct); // 将MyPanel的敌人坦克向量交给该敌人坦克 et.setEts(ets); // 启动敌人的坦克 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(); // } // 在Mypanel1_2初始化里,放置开战音效,加载即播放 // 以后再添加爆炸效果的声音也是这样做的思路 AePlayWave apw = new AePlayWave("E:\\111.wav"); apw.start(); } // 提示信息(只需要画笔即可) public void showInfo(Graphics g) { // 画出提示信息坦克 this.drawTank(70, 310, g, 0, 0); g.setColor(Color.black); g.drawString(Recorder.getEnNum() + "", 100, 330); this.drawTank(70, 360, g, 0, 1); g.setColor(Color.black); g.drawString(Recorder.getMyLife() + "", 100, 380); // 画出玩家的总成绩 g.setColor(Color.black); Font f = new Font("宋体", Font.BOLD, 20); g.setFont(f); g.drawString("你的总成绩", 410, 30); this.drawTank(410, 60, g, 0, 0); g.setColor(Color.black); g.drawString(Recorder.getAllEnNum() + "", 440, 80); } // 重写paint函数 public void paint(Graphics g) { // 一定要调用 super.paint(g); g.fillRect(0, 0, 400, 300); // 画出提示信息 this.showInfo(g); // 画出自己的坦克(将方向填进去) 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); if (hero.isLive) { if (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) { if (this.hitTank(myShot, et)) { // 减少敌人数量 Recorder.reduceEnNum(); // 增加我的记录 Recorder.addEnNumRec(); } } } } } } // 写一个函数 专门判断是否击中敌人坦克(原来第二参数: EnemyTank et) // void改成boolean 判断击中的目标是谁 public boolean hitTank(Shot s, Tank1_2 et) { boolean b2 = false; // 判断该坦克的方向 switch (et.direct) { // 如果敌人坦克的方向是上或者是下 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; b2 = true; // 创建一颗炸弹,放入Vector Bomb b = new Bomb(et.x, et.y); // 放入Vector 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; b2 = true; // 创建一颗炸弹,放入Vector Bomb b = new Bomb(et.x, et.y); // 放入Vector bombs.add(b); } break; } return b2; } // 画出坦克的函数 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 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(); } } // 必须重新绘制Panel this.repaint(); } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyTyped(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(); } // 谨记这里:由于这里多写了这些,线程每次都被重新调用 // showInfo的信息不会动态改变 调了好久好久。苦笑脸 // 判断是否击中(写在这里,虽然在每次重绘的时候都要调用,但是没办法) // 每一颗子弹都要和每个坦克进行匹配 // 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(); } } }
Menbers类:
package com.fanghua6; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Vector; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; //播放声音的类 class AePlayWave extends Thread { private String filename; public AePlayWave(String wavfile) { filename = wavfile; } public void run() { File soundFile = new File(filename); AudioInputStream audioInputStream = null; try { audioInputStream = AudioSystem.getAudioInputStream(soundFile); } catch (Exception e1) { e1.printStackTrace(); return; } AudioFormat format = audioInputStream.getFormat(); SourceDataLine auline = null; DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); try { auline = (SourceDataLine) AudioSystem.getLine(info); auline.open(format); } catch (Exception e) { e.printStackTrace(); return; } auline.start(); int nBytesRead = 0; // 这是缓冲 byte[] abData = new byte[512]; try { while (nBytesRead != -1) { nBytesRead = audioInputStream.read(abData, 0, abData.length); if (nBytesRead >= 0) auline.write(abData, 0, nBytesRead); } } catch (IOException e) { e.printStackTrace(); return; } finally { auline.drain(); auline.close(); } } } // 继续上一局的记记录点的类 class Node { int x; int y; int direct; public Node(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } } // 记录类,同时也可以保存玩家的设置(一般都是事先写在缓存里面的) class Recorder { // 记录每关有多少敌人 private static int enNum = 20; // 设置我有多少可以用的坦克 private static int myLife = 3; // 记录一共消灭多少敌人 private static int allEnNum = 0; // 从文件中恢复记录点 static Vector<Node> nodes = new Vector<Node>(); private static FileWriter fw = null; private static BufferedWriter bw = null; private static FileReader fr = null; private static BufferedReader br = null; private Vector<EnemyTank> ets = new Vector<EnemyTank>(); // 完成读取的函数(记录点和敌人的数量) public Vector<Node> getNodesAndEnNums() { try { fr = new FileReader("E:/Tanke.txt"); br = new BufferedReader(fr); String n = ""; n = br.readLine();// 设置先读第一行 allEnNum = Integer.parseInt(n); // 接着读 while ((n = br.readLine()) != null) { String[] xyz = n.split(" "); // 在知道只有三条数据的情况下,用了这个方法。否则要用for循环 Node node = new Node(Integer.parseInt(xyz[0]), Integer.parseInt(xyz[1]), Integer.parseInt(xyz[2])); nodes.add(node); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { br.close(); fr.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } return nodes; } public Vector<EnemyTank> getEts() { return ets; } public void setEts(Vector<EnemyTank> ets) { this.ets = ets; } // 保存击毁敌人的数量和击毁敌人坦克的坐标、方向 public void keepRecAndEnemyTank() { try { fw = new FileWriter("E:/Tanke.txt"); bw = new BufferedWriter(fw); bw.write(allEnNum + "\r\n"); // 保存当前的敌人坦克的坐标和方向 for (int i = 0; i < ets.size(); i++) { // 取出第一个坦克 EnemyTank et = ets.get(i); if (et.isLive) { // 活的就保存 String recode = et.x + " " + et.y + " " + et.direct; // 写入 bw.write(recode + "\r\n"); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { bw.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { fw.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // 从文件中读取,记录 public static void getRecoring() { try { fr = new FileReader("E:/Tanke.txt"); br = new BufferedReader(fr); String n = br.readLine(); allEnNum = Integer.parseInt(n); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { br.close(); fr.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // 把玩家击毁敌人坦克数量保存到文件中 public static void keepRecording() { try { fw = new FileWriter("E:/Tanke.txt"); bw = new BufferedWriter(fw); bw.write(allEnNum + "\r\n"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { bw.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { fw.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static int getAllEnNum() { return allEnNum; } public static void setAllEnNum(int allEnNum) { Recorder.allEnNum = allEnNum; } public static int getEnNum() { return enNum; } public static void setEnNum(int enNum) { Recorder.enNum = enNum; } public static int getMyLife() { return myLife; } public void setMyLife(int myLife) { Recorder.myLife = myLife; } // 减少敌人数 public static void reduceEnNum() { enNum--; } // 消灭敌人 public static void addEnNumRec() { allEnNum++; } } // 炸弹类(没必要定义为线程,因为它不会移动,没有坐标改变) 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 { int times = 0; // 定义一个向量,可以访问MyPanel上所有敌人的坦克 Vector<EnemyTank> ets = new Vector<EnemyTank>(); // 定义向量,可以存放敌人的子弹 Vector<Shot> ss = new Vector<Shot>(); // 敌人添加子弹,应该刚刚创建坦克和敌人子弹死亡后 public EnemyTank(int x, int y) { super(x, y); } // 得到MyPaneld的敌人坦克向量 public void setEts(Vector<EnemyTank> vv) { // 此时拥有一种能力,可以拿到yPan el上所有敌人的坦克 this.ets = vv; } // 判断是否碰到别的敌人的坦克 public boolean isTouchOtherEnemy() { boolean b = false; switch (this.direct) { case 0: // 我的坦克向上 for (int i = 0; i < ets.size(); i++) { // 取出第一个坦克(很有可能是自己哦) EnemyTank et = ets.get(i); // 如果不是自己 if (et != this) { // 如果敌人的方向是向下或者向下 if (et.direct == 0 || et.direct == 2) { if (this.x >= et.x && this.x <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } if (this.x + 20 >= et.x && this.x + 20 <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } } if (et.direct == 1 || et.direct == 3) { if (this.x >= et.x && this.x <= et.x + 30 && this.y >= et.y && this.y <= et.y + 20) { return true; } if (this.x + 20 >= et.x && this.x + 20 <= et.x + 30 && this.y >= et.y && this.y <= et.y + 20) { return true; } } } } break; case 1: for (int i = 0; i < ets.size(); i++) { EnemyTank et = ets.get(i); if (et != this) { if (et.direct == 0 || et.direct == 2) { if (this.x + 30 >= et.x && this.x + 30 <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } if (this.x + 30 >= et.x && this.x + 30 <= et.x + 20 && this.y + 20 >= et.y && this.y + 20 <= et.y + 30) { return true; } } if (et.direct == 1 || et.direct == 3) { if (this.x + 30 >= et.x && this.x + 30 <= et.x + 30 && this.y >= et.y && this.y <= et.y + 20) { return true; } if (this.x + 30 >= et.x && this.x + 30 <= et.x + 30 && this.y + 20 >= et.y && this.y + 20 <= et.y + 20) { return true; } } } } break; case 2: for (int i = 0; i < ets.size(); i++) { EnemyTank et = ets.get(i); if (et != this) { if (et.direct == 0 || et.direct == 2) { if (this.x >= et.x && this.x <= et.x + 20 && this.y + 30 >= et.y && this.y + 30 <= et.y + 30) { return true; } if (this.x + 20 >= et.x && this.x + 20 <= et.x + 20 && this.y + 30 >= et.y && this.y + 30 <= et.y + 30) { return true; } } if (et.direct == 1 || et.direct == 3) { if (this.x >= et.x && this.x <= et.x + 30 && this.y + 30 >= et.y && this.y + 30 <= et.y + 20) { return true; } if (this.x + 20 >= et.x && this.x + 20 <= et.x + 30 && this.y + 30 >= et.y && this.y + 30 <= et.y + 20) { return true; } } } } break; case 3: for (int i = 0; i < ets.size(); i++) { EnemyTank et = ets.get(i); if (et != this) { if (et.direct == 0 || et.direct == 2) { if (this.x >= et.x && this.x <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } if (this.x >= et.x && this.x <= et.x + 20 && this.y + 20 >= et.y && this.y + 20 <= et.y + 30) { return true; } } if (et.direct == 1 || et.direct == 3) { if (this.x >= et.x && this.x <= et.x + 30 && this.y >= et.y && this.y <= et.y + 20) { return true; } if (this.x >= et.x && this.x <= et.x + 30 && this.y + 20 >= et.y && this.y + 20 <= et.y + 20) { return true; } } } } break; } return b; } @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 && !this.isTouchOtherEnemy()) { 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 < 400 && !this.isTouchOtherEnemy()) { 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 < 300 && !this.isTouchOtherEnemy()) { 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 && !this.isTouchOtherEnemy()) { 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.爆炸效果的图片(3张)
2.游戏声音素材
这个自己下载一个好啦(我的是上面命名为111.wav的文件,这里不支持上传)