一、简单介绍
这是一个功能相对全的JAVA版坦克大战,界面绘制是通过JAVA的图形化用户界面完成的,包括了菜单界面和游戏界面。其中菜单界面可以供玩家选择重新开始游戏、暂停、继续、是否播放背景音乐、帮助等操作;游戏界面绘制了坦克、河流、草地、鹰碉堡等经典坦克场景,玩家在游戏界面操作坦克开始对战。
本游戏使用的主要技术有Swing编程、面向对象编程、多线程编程。本想用I/O编程实现保存游戏数据,感觉单机版的没必要就没弄。
游戏实现的主要功能有:
1.坦克可以上下左右、以及左上左下右上右下八个方向移动,移动时添加音效
2.坦克可以发子弹(可以连发),发射时添加音效
3.击中对方坦克时,坦克消失,显示爆炸效果;子弹击中鹰碉堡时,游戏结束;子弹击中墙壁时,子弹消失
4.我方坦克吃到血块时,生命值加满
5.移动过程中检测碰撞,包括坦克与坦克,坦克与草地、河流、墙壁等
6.声音处理(开始音乐、背景音乐等)
7.菜单处理(开始游戏、暂停游戏、继续游戏、帮助等)
8.计分(分关、记录成绩)
二、工程目录
三、具体代码
每个文件都有详细的注释,不理解的可以联系作者,这里只贴出Tank.java和TankClient.java的代码,源代码将会在文章最后面贴出来,话不多说,先上代码
Tank.java代码
package com.xiaoli.tank; import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.util.List; import java.util.Random; /** * *Title:Tank *Description:坦克类,包括坦克移动、坦克发射炮弹等 *@author xiaoli *@date2017年1月4日 */ public class Tank { //坦克X方向速度,设置游戏难度时可拓展 public static int XSPEED = 5; //坦克Y方向速度,设置游戏难度时可拓展 public static int YSPEED = 5; //坦克宽度 public static final int WIDTH = 30; //坦克高度 public static final int HEIGHT = 30; //坦克是否活着 private boolean live = true; //坦克的生命值 private int life = 100; //持有对TankClient大管家的引用 TankClient tc; //判断是否是我方坦克,默认true private boolean good=true; //用于记录坦克原来的坐标,碰到墙、坦克时方便退一步 private int oldX,oldY; //绘制坦克的左上角坐标 private int x, y; //用于产生随机数 private static Random r = new Random(); //用于控制敌人随机发出子弹 private int step = r.nextInt(30)+10; //判断是否按下方向键 private boolean bL=false, bU=false, bR=false, bD = false; //枚举类型定义了坦克的八个方向,和静止时的方向 enum Direction {L, LU, U, RU, R, RD, D, LD, STOP}; //坦克的方向 private Direction dir = Direction.STOP; //炮筒的方向 private Direction ptDir = Direction.D; //血条 private BloodBar bar = new BloodBar(); //构造方法 public Tank(int x, int y, boolean good) { this.x = x; this.y = y; this.good = good; } //构造方法 public Tank(int x, int y, boolean good, Direction dir,TankClient tc) { this(x, y, good); this.tc = tc; this.oldX=x; this.oldY=y; this.dir=dir; } /** * *@Description:画出坦克 * @param g */ public void draw(Graphics g) { if(!live) { if(!good) { tc.tanks.remove(this); } return; } Color c = g.getColor(); if(good) g.setColor(Color.RED); else g.setColor(Color.BLUE); g.fillOval(x, y, WIDTH, HEIGHT); g.setColor(c); //血条 if(good) bar.draw(g); switch(ptDir) { case L: g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x, y + Tank.HEIGHT/2); break; case LU: g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x, y); break; case U: g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH/2, y); break; case RU: g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH, y); break; case R: g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH, y + Tank.HEIGHT/2); break; case RD: g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH, y + Tank.HEIGHT); break; case D: g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x + Tank.WIDTH/2, y + Tank.HEIGHT); break; case LD: g.drawLine(x + Tank.WIDTH/2, y + Tank.HEIGHT/2, x, y + Tank.HEIGHT); break; } move(); } /* * 坦克移动 */ void move() { this.oldX=x; this.oldY=y; switch(dir) { case L: x -= XSPEED; break; case LU: x -= XSPEED; y -= YSPEED; break; case U: y -= YSPEED; break; case RU: x += XSPEED; y -= YSPEED; break; case R: x += XSPEED; break; case RD: x += XSPEED; y += YSPEED; break; case D: y += YSPEED; break; case LD: x -= XSPEED; y += YSPEED; break; case STOP: break; } if(this.dir != Direction.STOP) { this.ptDir = this.dir; } if(!good){ Direction[] dirs = Direction.values(); if(step==0){ step=r.nextInt(30)+10; int rn = r.nextInt(9); this.dir=dirs[rn]; } step--; //敌人发子弹 if(r.nextInt(40)>36){ this.fire(); } } if(x < 0) x = 0; if(y < 55) y = 55; if(x + Tank.WIDTH > TankClient.GAME_WIDTH) x = TankClient.GAME_WIDTH - Tank.WIDTH; if(y + Tank.HEIGHT > TankClient.GAME_HEIGHT) y = TankClient.GAME_HEIGHT - Tank.HEIGHT; } /** * *@Description:用于撞到墙、坦克时返回上一步 */ private void stay(){ x=oldX; y=oldY; } /** * *@Description:按下键时监听 * @param e */ public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); switch(key) { case KeyEvent.VK_R: tc.bloods.clear(); tc.grasses.clear(); tc.rivers.clear(); tc.walls.clear(); tc.missiles.clear(); tc.tanks.clear(); tc.explodes.clear(); //关卡、分数重置 tc.score=0; tc.level=1; //草地 tc.newGrass(); //河流 tc.newRiver(); //墙 tc.newWall(); //当在区域中没有坦克时,就出来坦克 if (tc.tanks.size() == 0) { tc.newTank(); } tc.myTank = new Tank(220, 560, true, Direction.STOP, tc);//设置自己出现的位置 if(!tc.home.isLive()){ tc.home.setLive(true); } tc.dispose(); new TankClient().lauchFrame(); break; case KeyEvent.VK_LEFT : bL = true; break; case KeyEvent.VK_UP : bU = true; break; case KeyEvent.VK_RIGHT : bR = true; break; case KeyEvent.VK_DOWN : bD = true; break; } locateDirection(); } /** * *@Description:定位坦克的方向 */ void locateDirection() { if(bL && !bU && !bR && !bD) dir = Direction.L; else if(bL && bU && !bR && !bD) dir = Direction.LU; else if(!bL && bU && !bR && !bD) dir = Direction.U; else if(!bL && bU && bR && !bD) dir = Direction.RU; else if(!bL && !bU && bR && !bD) dir = Direction.R; else if(!bL && !bU && bR && bD) dir = Direction.RD; else if(!bL && !bU && !bR && bD) dir = Direction.D; else if(bL && !bU && !bR && bD) dir = Direction.LD; else if(!bL && !bU && !bR && !bD) dir = Direction.STOP; } /** * *@Description:松开键时监听 * @param e */ public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); switch(key) { case KeyEvent.VK_K: if(this.good){ if(!this.live){ this.live=true; this.life=100; //复活次数加1 tc.count++; } } break; case KeyEvent.VK_J: superFire(); break; case KeyEvent.VK_CONTROL: fire(); //发射炮弹音效 new Audio(2); break; case KeyEvent.VK_LEFT : bL = false; new Audio(1); break; case KeyEvent.VK_UP : bU = false; new Audio(1); break; case KeyEvent.VK_RIGHT : bR = false; new Audio(1); break; case KeyEvent.VK_DOWN : bD = false; new Audio(1); break; } locateDirection(); } /** * *@Description:坦克开火 * @return 炮弹对象 */ public Missile fire() { if(!live)return null; int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2; int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2; Missile m = new Missile(x, y, ptDir,this.good, this.tc); tc.missiles.add(m); return m; } /** * *@Description:坦克根据方向开火 * @return 炮弹对象 */ public Missile fire(Direction dir) { if(!live)return null; int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2; int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2; Missile m = new Missile(x, y, dir,this.good, this.tc); tc.missiles.add(m); return m; } /** * *@Description:超级炮弹,可以向八个方向开火 */ public void superFire(){ Direction[] dirs = Direction.values(); for(int i=0;i<8;i++){ fire(dirs[i]); } } /** * *@Description:判断坦克是否撞墙 * @param 墙对象 * @return 是否撞墙了 */ public boolean CollidesWithWall(Wall w){ if(this.live&&this.getRect().intersects(w.getRect())){ this.stay(); return true; } return false; } public boolean CollidesWithWalls(List walls){ for(int i=0;i tanks){ for(int i=0;i rivers){ for(int i=0;i
复制代码
TankClient.java代码
package com.xiaoli.tank; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.Menu; import java.awt.MenuBar; import java.awt.MenuItem; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.swing.JOptionPane; /** * *Title:TankClient *Description:这个类是大管家,控制着其他类,也是程序入口 *@author xiaoli *@date2017年1月4日 */ public class TankClient extends Frame implements ActionListener { //游戏宽度 public static final int GAME_WIDTH = 800; //游戏高度 public static final int GAME_HEIGHT = 600; //是否开启重绘线程 public static boolean printable=true; //用于产生随机数 Random r = new Random(); //记录复活次数 public static int count=0; //关卡 public static int level=1; //分数 public static int score=0; //菜单条 MenuBar jmb = null; //菜单 Menu jm1 = null, jm2 = null, jm3 = null, jm4 = null; //菜单项 MenuItem jmi1 = null, jmi2 = null, jmi3 = null, jmi4 = null, jmi5 = null, jmi6 = null, jmi7 = null, jmi8 = null, jmi9 = null,jmi10=null,jmi11=null; //从上到下从左到右画墙 Wall w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12; //创建我的坦克 Tank myTank = new Tank(220, 560, true,Tank.Direction.STOP, this); //创建鹰碉堡 Home home = new Home(379,556,this); //墙集合,用于存放墙 List walls = new ArrayList(); //河流集合,用于存放河流 List rivers = new ArrayList(); //定义一个血块 Blood b=null; //血块集合,用于存放血块。用集合的原因是方便删除 List bloods = new ArrayList(); //草集合,用于存放草 List grasses = new ArrayList(); //炸弹集合,用于存放炸弹 List explodes = new ArrayList(); //炮弹集合,用于存放炮弹 List missiles = new ArrayList(); //坦克集合,用于存放坦克 List tanks = new ArrayList(); //用于解决双缓冲闪烁现象 Image offScreenImage = null; /** * 绘制场景 */ public void paint(Graphics g) { //分关 if (score >= 1000 * level ) { setLevel(level + 1); // 进入下一关 new Audio(5); grasses.clear(); rivers.clear(); missiles.clear(); tanks.clear(); explodes.clear(); walls.clear(); this.dispose(); new TankClient().lauchFrame(); } //家 home.draw(g); //只能复活一次 if(myTank.getLife()<=0&&count==1){ myTank.setLive(false); home.gameOver(g); } if(r.nextInt(50)>48){ newBlood(); } //敌人死光 if(tanks.size()<=0){ for(int i=0; i<6; i++) { if(i<3){ tanks.add(new Tank(50 + 40*(i+1), 50, false,Tank.Direction.D, this)); }else{ tanks.add(new Tank(50 + 40*(i+1), 50, false,Tank.Direction.R, this)); } } } //g.drawString("血块数量:" + bloods.size(), 10, 50); //g.drawString("子弹数量:" + missiles.size(), 10, 70); //g.drawString("炸弹数量:" + explodes.size(), 10, 90); //g.drawString("坦克数量:" + tanks.size(), 10, 110); g.drawString("关卡:" + level, 10, 90); g.drawString("分数:" + score, 10, 110); g.drawString("生命值:" + myTank.getLife(), 10, 130); g.drawString("复活次数:" + count, 10, 150); //画墙 for(int i=0;i=3){ w1 = new Wall(210, 200, 100, 30, this); w2 = new Wall(210, 200, 30, 190, this); w3 = new Wall(280, 200, 30, 190, this); w4 = new Wall(210, 370, 100, 30, this); w5 = new Wall(430, 200, 90, 30, this); w6 = new Wall(430, 285, 90, 30, this); w7 = new Wall(430, 370, 90, 30, this); w8 = new Wall(500, 200, 30, 200, this); w10 = new Wall(379, 530, 43, 20, this); w11 = new Wall(280, 530, 30, 70, this); w12 = new Wall(510, 530, 30, 70, this); walls.add(w1); walls.add(w2); walls.add(w3); walls.add(w4); walls.add(w5); walls.add(w6); walls.add(w7); walls.add(w8); walls.add(w10); walls.add(w11); walls.add(w12); } } /** * *@Description:生成血块 */ public void newBlood(){ //血块 b = new Blood(); //if(b.CollidesWithWalls(walls)){//血块不能画在墙里面 //return; //} if(bloods.size()==0){ bloods.add(b); } } /** * *@Description:生成河流 */ public void newRiver(){ for(int i=0;i<6;i++){ River river = new River(18+i*30,340); rivers.add(river); river = new River(18+i*30,340+30); rivers.add(river); } for(int i=0;i<6;i++){ River river = new River(568+i*30,340); rivers.add(river); river = new River(568+i*30,340+30); rivers.add(river); } } /** * *@Description:生成草地 */ public void newGrass() { // 草地 for (int i = 0; i < 10; i++) { Grass grass = new Grass((i + 1) * 18, 200); grasses.add(grass); grass = new Grass((i + 1) * 18, 200 + 1 * 18); grasses.add(grass); grass = new Grass((i + 1) * 18, 200 + 2 * 18); grasses.add(grass); grass = new Grass((i + 1) * 18, 200 + 3 * 18); grasses.add(grass); grass = new Grass((i + 1) * 18, 200 + 4 * 18); grasses.add(grass); grass = new Grass((i + 1) * 18, 200 + 5 * 18); grasses.add(grass); grass = new Grass((i + 1) * 18, 200 + 6 * 18); grasses.add(grass); grass = new Grass((i + 1) * 18, 200 + 7 * 18); grasses.add(grass); } for (int i = 1; i <= 11; i++) { Grass grass = new Grass(250 + 18 * 7, 182 + i * 18); grasses.add(grass); grass = new Grass(250 + 18 * 8, 182 + i * 18); grasses.add(grass); grass = new Grass(250 + 18 * 5, 182 + i * 18); grasses.add(grass); grass = new Grass(250 + 18 * 6, 182 + i * 18); grasses.add(grass); } for(int i=0;i<10;i++){ Grass grass = new Grass(550+(i + 1) * 18, 200); grasses.add(grass); grass = new Grass(550+(i + 1) * 18, 200 + 1 * 18); grasses.add(grass); grass = new Grass(550+(i + 1) * 18, 200 + 2 * 18); grasses.add(grass); grass = new Grass(550+(i + 1) * 18, 200 + 3 * 18); grasses.add(grass); grass = new Grass(550+(i + 1) * 18, 200 + 4 * 18); grasses.add(grass); grass = new Grass(550+(i + 1) * 18, 200 + 5 * 18); grasses.add(grass); grass = new Grass(550+(i + 1) * 18, 200 + 6 * 18); grasses.add(grass); grass = new Grass(550+(i + 1) * 18, 200 + 7 * 18); grasses.add(grass); } } /** * *@Description:生成坦克 */ public void newTank(){ for (int i = 0; i < 10; i++) { if (i < 5) { tanks.add(new Tank(50 + 40 * (i + 1), 55, false, Tank.Direction.D, this)); } else { tanks.add(new Tank(50 + 40 * (i + 1), 55, false, Tank.Direction.R, this)); } } } /** * *@Description:主函数、程序入口 * @param args */ public static void main(String[] args) { TankClient tc = new TankClient(); tc.lauchFrame(); } /** * *Title:PaintThread *Description:重绘线程,模拟动态效果 *@author xiaoli *@date2017年1月4日 */ private class PaintThread implements Runnable { public void run() { while(printable) { repaint(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * *@Description:按键监听,对坦克、炮弹等进行人机交互 * @param e */ private class KeyMonitor extends KeyAdapter { public void keyReleased(KeyEvent e) { myTank.keyReleased(e); } public void keyPressed(KeyEvent e) { myTank.keyPressed(e); } } /** * 对菜单项操作 */ @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub if (e.getActionCommand().equals("NewGame")) { printable = false; Object[] options = { "确定", "取消" }; int response = JOptionPane.showOptionDialog(this, "您确认要重新开始!", "", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (response == 0) { this.score=0; this.setLevel(1); printable = true; this.dispose(); new TankClient().lauchFrame(); } else { printable = true; new Thread(new PaintThread()).start(); // 线程启动 } } else if (e.getActionCommand().endsWith("Stop")) { printable = false; // try { // Thread.sleep(10000); // // } catch (InterruptedException e1) { // // TODO Auto-generated catch block // e1.printStackTrace(); // } } else if (e.getActionCommand().equals("Continue")) { if (!printable) { printable = true; new Thread(new PaintThread()).start(); // 线程启动 } // System.out.println("继续"); } else if (e.getActionCommand().equals("Exit")) { printable = false; Object[] options = { "确定", "取消" }; int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (response == 0) { System.out.println("退出"); System.exit(0); } else { printable = true; new Thread(new PaintThread()).start(); // 线程启动 } } else if (e.getActionCommand().equals("help1")) { printable = false; JOptionPane.showMessageDialog(null, "随着人们精神文化生活的日益丰富,游戏成为了人们生活中必不可少的一部分\n90《坦克大战》更是90后一代人童年的回忆,也是一款经典游戏。\n" + "开发java版坦克大战有利用更深入理解java面向对象编程、swing界面编程、多线程编程"+"\n"+"参考:马士兵坦克大战视频教程、以及互联网资源"+"\n"+"作者邮箱:[email protected]"+"\n" +"若有关内容侵犯了您的权益,请及时联系作者删除,谢谢!", "提示!", JOptionPane.INFORMATION_MESSAGE); this.setVisible(true); printable = true; new Thread(new PaintThread()).start(); // 线程启动 } else if(e.getActionCommand().equals("help2")){ printable = false; JOptionPane.showMessageDialog(null, "用→ ← ↑ ↓控制方向,CTRL键盘发射,J超级炮弹,K复活(只能复活一次),R重新开始!\n"+"作者邮箱:[email protected]"+"\n" +"若有关内容侵犯了您的权益,请及时联系作者删除,谢谢!", "提示!", JOptionPane.INFORMATION_MESSAGE); this.setVisible(true); printable = true; new Thread(new PaintThread()).start(); // 线程启动 } else if (e.getActionCommand().equals("level1")) { //Tank.count = 12; Tank.XSPEED = 5; Tank.YSPEED = 5; Missile.XSPEED = 10; Missile.YSPEED = 10; this.dispose(); new TankClient().lauchFrame();; } else if (e.getActionCommand().equals("level2")) { //Tank.count = 12; Tank.XSPEED = 8; Tank.YSPEED = 8; Missile.XSPEED = 14; Missile.YSPEED = 14; this.dispose(); new TankClient().lauchFrame();; } else if (e.getActionCommand().equals("level3")) { //Tank.count = 20; Tank.XSPEED = 12; Tank.YSPEED = 12; Missile.XSPEED = 18; Missile.YSPEED = 18; this.dispose(); new TankClient().lauchFrame();; } else if (e.getActionCommand().equals("level4")) { //Tank.count = 20; Tank.XSPEED = 16; Tank.YSPEED = 16; Missile.XSPEED = 24; Missile.YSPEED = 14; this.dispose(); new TankClient().lauchFrame();; }else if(e.getActionCommand().equals("startMain")){ printable = false; JOptionPane.showMessageDialog(null, "开启后不能关闭!请不要重复点击!", "提示!", JOptionPane.INFORMATION_MESSAGE); this.setVisible(true); printable = true; // 线程启动 new Thread(new PaintThread()).start(); new Audio(0); } } /* * 设置关卡 */ public void setLevel(int level) { //设置三关 if (level > 10) { level = 10; } this.level = level; } } 复制代码