Java小项目--坦克大战<TankWar1.0>
这个小项目主要是练习j2se的基础内容和面向对象的思想。项目实现了基本的简单功能,我方一辆坦克,用上下左右键控制移动方向,按F键为发射炮弹,敌方有10辆坦克,我方阵亡或者敌方挂光了游戏就结束了。用大小实心圆表示坦克和炮弹。这是最初版本,界面不够友好图形很土鳖,没有网络功能。在后续版本中会添加更炫的图片和网络功能,并加入聊天室,可以局域网内聊天和对战。
效果图:
项目包括4个文件,我把它们贴出来。
文件一:TankClient.java
TankClient为一个类,他的作用就像大管家,将坦克、炮弹、爆炸联系起来。首先是建立界面,然后重写了画图的paint方法,系统循环自动调用paint,所以在paint中调用坦克、炮弹、爆炸类各自的draw方法将各自画出来,而坦克、炮弹各自的draw是如何实现的画了什么大管家是不管它的,这也正是面向对象语言的一个思想。
Java的集合可以装对象,3个List容器分别装敌方坦克,所有炮弹,所有爆炸;
/* * 版本: version0.9 * 功能: @添加爆炸效果,添加多辆坦克 */ import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.List; import java.util.*; public class TankClient extends Frame{ public final int SCREENWIDTH=800,SCREENHEIGHT=600; Tank myTank = new Tank(130,230,true,this); //new了一辆坦克出来,第3个参数分别是我方坦克还是敌方坦克。 Missile missile=null; Image screenImage = null; List<Missile> missiles = new ArrayList<Missile> (); //这个容器装炮弹,任何坦克每打出一发炮弹都装进这个容器中,当炮弹击中对方坦克或者炮弹出界都将该//炮弹从容器中remove; List<Explode> explodes = new ArrayList<Explode> (); List<Tank> tanks = new ArrayList<Tank> (); public void update(Graphics g) { if(screenImage == null) screenImage = this.createImage(SCREENWIDTH,SCREENHEIGHT); Graphics graphics = screenImage.getGraphics(); Color c = graphics.getColor(); graphics.setColor(new Color(150,240,215)); graphics.fillRect(0, 0, SCREENWIDTH, SCREENHEIGHT); graphics.setColor(c); paint(graphics); g.drawImage(screenImage, 0, 0, null); } public void paint(Graphics g) { g.drawString("Missiles Counts: "+missiles.size(), 30, 50); g.drawString("Explodes Counts: "+explodes.size(), 30, 70); g.drawString("Tanks Counts: "+tanks.size(), 30, 90); myTank.draw(g); // enemyTank.draw(g); for(int i=0;i<missiles.size();i++) { Missile m = missiles.get(i); //将所有炮弹从容器中取出,调用hitTank方法去判断有没有集中对方坦克,若击中则该炮弹和被击中的坦克都将从相应的容器中移除; m.hitTanks(tanks); m.hitTank(myTank); //敌人可击打我方。 m.draw(g); //将每一发炮弹draw出来 } for(int i= 0;i<explodes.size();i++) { Explode e = explodes.get(i); e.draw(g); } for(int i=0;i<tanks.size();i++) { Tank t = tanks.get(i); //取出容器中剩余的坦克将他们画出来。 t.draw(g); } } class PaintThread implements Runnable { public void run() { while(true) { try { repaint(); //在这个线程中进行重画。每隔一段时间调用repaint,repaint会调用updata,updata会调用paint方法。 Thread.sleep(50);//这个时间决定了所有物体的运动速度。 } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { TankClient tankclient = new TankClient(); tankclient.launch(); } public void launch() { this.setResizable(false); this.setTitle("TankWar"); this.setBackground(new Color(150,240,215)); setLocation(200,200); setSize(SCREENWIDTH,SCREENHEIGHT); setVisible(true); this.addWindowListener(new Monitor_Window()); this.addKeyListener(new KeyMonitor()); new Thread(new PaintThread()).start(); for(int i=0; i<10;i++) { //new出来10辆敌方坦克放在容器中。 Tank t = new Tank(50+40*(i+1),80+i,false,Tank.Direction.D,this); tanks.add(t); } } class KeyMonitor extends KeyAdapter { public void keyReleased(KeyEvent e) { myTank.keyReleased(e); } //按键按下或释放这调用tank类里的方法进行判断处理。 public void keyPressed(KeyEvent e) { myTank.KeyPress(e); } } } class Monitor_Window extends WindowAdapter { //窗口关闭事件 public void windowClosing(WindowEvent e) { System.exit(0); } }
文件二:Tank.java
Tank类包含坦克的一些属性,包括坦克的尺寸、速度、位置等,也包含一些方法包括开火fire方法、移动后坐标的计算、按键的处理。
import java.awt.*; import java.awt.event.*; import java.util.*; public class Tank { int x,y; TankClient tc=null; private boolean good = true; //用来分辨敌我坦克,若某坦克good属性为true则该坦克为我方坦克 boolean live = true; //用来表明该坦克是否还存活,若live属性为false表示该坦克已死则在draw方法里不将该坦克画出来并从list容器中移除。 public static final int SCREENWIDTH=800,SCREENHEIGHT=600; public static final int WIDTH=30,HEIGHT=30; public static final int XSPEED=5,YSPEED=5; boolean bL=false,bR=false,bU=false,bD=false,bB=false; enum Direction { L,LD,LU,U,D,R,RD,RU,STOP }; //枚举类型,用来表示坦克的运动方向也决定了炮弹的方向 private Direction dir=Direction.STOP,mdir=Direction.RD; private static Random r = new Random(); //定义的随机数 private static int dm = r.nextInt(12)+3; 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,TankClient tc) { this.x = x; this.y = y; this.good = good; this.tc = tc; } public Tank(int x, int y,boolean good,Direction dir,TankClient tc) { this.x = x; this.y = y; this.good = good; this.dir = dir; this.tc = tc; } public boolean isGood() { return good; } public boolean isLive() { return live; } public void setLive(boolean live) { this.live = live; } public void draw(Graphics g) { if(!this.isLive()) { if(!good) tc.tanks.remove(this); //如果该坦克是敌方坦克,而且死了,则将该坦克从容器中移除。 return; } Color c = g.getColor(); if(good) g.setColor(new Color(100,25,200)); //敌方坦克和我方坦克画成不同的颜色 else g.setColor(Color.red); g.fillOval(x, y, WIDTH, HEIGHT); g.setColor(Color.blue); move(); switch(mdir) { //画该坦克的炮台,炮台的方向为坦克的最后运动方向,如果坦克为stop状态则保持上次方向。 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 LD: g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x, y+Tank.HEIGHT); break; case R: g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y+Tank.HEIGHT/2); break; case RU: g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y); break; case RD: g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y+Tank.HEIGHT); break; case U: g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH/2, y); break; case D: g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH/2, y+Tank.HEIGHT); break; case STOP: break; } g.setColor(c); } public Missile fire() { int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2; int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2; //根据坦克的和位置运动方向画出炮弹的位置和方向,第四个属性表示炮弹的敌我属性和坦克的敌我属性一致,坦克的good为true则打出的炮弹也为true。 Missile m = new Missile(x,y,mdir,good,this.tc); tc.missiles.add(m); //将炮弹加入进容器 return m; //返回该炮弹 } public void move() { switch(dir) { //根据方向对坦克位置进行更新 case L: x -=XSPEED; break; case LU: x -=XSPEED; y -=YSPEED; break; case LD: x -=XSPEED; y +=YSPEED; break; case R: x +=XSPEED; break; case RU: x +=XSPEED; y -=YSPEED; break; case RD: x +=XSPEED; y +=YSPEED; break; case U: y -=YSPEED; break; case D: y +=YSPEED; break; case STOP: break; } if(x<0) //坦克不能出界 x=0; else if(x>tc.SCREENWIDTH-Tank.WIDTH) x=tc.SCREENWIDTH-Tank.WIDTH; if(y<30) y=30; else if(y>tc.SCREENHEIGHT-Tank.HEIGHT) y=tc.SCREENHEIGHT-Tank.HEIGHT; // dir = Direction.STOP; //加了这句速度很慢啊。 if(!good) { //如果为敌方坦克,则运动方向为随机的。 Direction [] dirs = Direction.values(); int rn = r.nextInt(dirs.length-1); dm--; //注意dirs[dirs.length]为STOP。 if(dm<=0) { dir = dirs[rn]; mdir = dir; dm=r.nextInt(15)+3; } if(r.nextInt(50)>48) this.fire(); } } public void locationDir() { //根据按下的按键决定方向 if(bR && !bL && !bU && !bD) { dir = Direction.R; } else if(bR && !bL && bU && !bD) dir = Direction.RU; else if(bR && !bL && !bU && bD) dir = Direction.RD; else if(!bR && bL && !bU && !bD) dir = Direction.L; else if(!bR && bL && bU && !bD) dir = Direction.LU; else if(!bR && bL && !bU && bD) dir = Direction.LD; else if(!bR && !bL && bU && !bD) dir = Direction.U; else if(!bR && !bL && !bU && bD) dir = Direction.D; else dir = Direction.STOP; if(dir != Direction.STOP) //mdir保存最后一次方向,坦克停下来了保持最后的方向。 mdir=dir; } public void KeyPress(KeyEvent e) { int key = e.getKeyCode(); switch(key) { case KeyEvent.VK_RIGHT: System.out.println("R"); bR = true; break; case KeyEvent.VK_LEFT: bL = true; break; case KeyEvent.VK_UP: System.out.println("U"); bU = true; break; case KeyEvent.VK_DOWN: System.out.println("D"); bD = true; break; } locationDir(); } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); switch(key) { case KeyEvent.VK_RIGHT: System.out.println("R"); bR = false; break; case KeyEvent.VK_LEFT: bL = false; break; case KeyEvent.VK_UP: System.out.println("U"); bU = false; break; case KeyEvent.VK_DOWN: System.out.println("D"); bD = false; break; case KeyEvent.VK_F: System.out.println("F"); if(this.isLive()) //坦克存活时才能发子弹。 tc.missile = fire(); break; } locationDir(); } public Rectangle getRec() { return new Rectangle(x,y,Tank.WIDTH,Tank.HEIGHT); } }
文件三:Missile.java
炮弹类包含炮弹的属性和方法:尺寸、速度、位置、方向;坐标的计算,击打坦克等;
import java.awt.*; import java.util.List; import java.util.*; public class Missile { int x,y; TankClient tc; Tank.Direction dir =Tank.Direction.R; public static final int XSPEED=10,YSPEED=10; public static final int WIDTH=10,HEIGHT=10; private boolean live = true; private boolean good; public boolean isGood() { return good; } public boolean isLive() { return live; } public Missile(int x, int y, Tank.Direction dir) { this.x = x; this.y = y; this.dir = dir; } public Missile(int x, int y, Tank.Direction dir,boolean good,TankClient tc) { this.x = x; this.y = y; this.dir = dir; this.good = good; //tank的good属性和炮弹的good属性一致; this.tc = tc; } public void draw(Graphics g) { if(!this.live) { //如果炮弹已死则将它从容器中移除并返回,不在把他画出来。 tc.missiles.remove(this); return; } Color c = g.getColor(); if(!this.isGood()) g.setColor(Color.red); else g.setColor(new Color(100,25,200)); g.fillOval(x, y, WIDTH, HEIGHT); g.setColor(c); move(); } public void move() { if(dir == Tank.Direction.STOP) System.out.println("@@@@@@@@@@@@@@@@"); //测出bug,敌人坦克发出来停止的哑弹。 switch(dir) { case L: x -=XSPEED; break; case LU: x -=XSPEED; y -=YSPEED; break; case LD: x -=XSPEED; y +=YSPEED; break; case R: x +=XSPEED; break; case RU: x +=XSPEED; y -=YSPEED; break; case RD: x +=XSPEED; y +=YSPEED; break; case U: y -=YSPEED; break; case D: y +=YSPEED; break; } System.out.println(x+"=x,y= "+y); if(x<0 || y<30 || x>tc.SCREENWIDTH-10 || y>tc.SCREENHEIGHT-10) { this.live = false; //如果炮弹出界则死亡 } } public Rectangle getRec() { return new Rectangle(x,y,WIDTH,HEIGHT); } public boolean hitTank(Tank t) { if(this.getRec().intersects(t.getRec()) && t.isLive() && this.good!=t.isGood()) { t.setLive(false); this.live = false; Explode e = new Explode(x,y,this.tc); e.setLive(true); tc.explodes.add(e); return true; } return false; } public boolean hitTanks(List<Tank> tanks) { for(int i=0;i<tanks.size();i++) { if(hitTank(tanks.get(i))) return true; } return false; } }
文件四:Explode.java
爆炸效果类就是画几个圈,主要包含draw方法,当发生爆炸的时候将爆炸效果draw出来。
import java.awt.*; public class Explode { int x,y; private boolean live = false; public boolean isLive() { return live; } public void setLive(boolean live) { this.live = live; } TankClient tc; int [] diameter = {4,8,12,22,34,32,20,13,6}; int step = 0; public Explode(int x,int y,TankClient tc) { this.x = x; this.y = y; this.tc = tc; } public void draw(Graphics g) { if(!this.live) { tc.explodes.remove(this); return; } if(step==diameter.length) { step = 0; this.live = false; tc.explodes.remove(this); } Color c = g.getColor(); g.setColor(Color.GREEN); g.fillOval(x, y, diameter[step], diameter[step]); g.setColor(c); step++; } }