• Java小项目--坦克大战(version1.0)


    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++;
    	}
    	
    }
    




  • 相关阅读:
    RAID技术
    敏捷开发
    如何写出高质量的代码?现在知道还不晚
    Java大型互联网架构技术经验
    Chrome精品插件
    2018 java BAT最新面试宝典
    Java成神之路(2018版)
    三分钟读懂摘要算法
    我的Mac应用清单
    事务隔离级别
  • 原文地址:https://www.cnblogs.com/pangblog/p/3265286.html
Copyright © 2020-2023  润新知