zoukankan      html  css  js  c++  java
  • 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++;
    	}
    	
    }
    




  • 相关阅读:
    IOS Charles(代理服务器软件,可以用来拦截网络请求)
    Javascript中addEventListener和attachEvent的区别
    MVC中实现Area几种方法
    Entity Framework Code First 中使用 Fluent API 笔记。
    自定义JsonResult解决 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    An entity object cannot be referenced by multiple instances of IEntityChangeTracker 的解决方案
    Code First :使用Entity. Framework编程(8) ----转发 收藏
    Code First :使用Entity. Framework编程(6) ----转发 收藏
    Code First :使用Entity. Framework编程(5) ----转发 收藏
  • 原文地址:https://www.cnblogs.com/pangblog/p/3265286.html
Copyright © 2011-2022 走看看