坦克大战--这款游戏,相信大家小时候都玩过,想当年还在读小学的时候,我和我表哥整个暑假拿着一台小霸王游戏机接上电视,就开始玩坦克大战,通关根本停不下来,

简直打遍天下无敌手。现在我上了大学,学的是软件工程专业,学了java,现在我自己用java做了这款小游戏,和大家分享一下,也说一下我在做这个游戏过程中遇到的一些问题,

希望对大家起到一定的帮助。源代码在附件里,大家可以下载

首先简略的说下整个坦克大战的大致流程、要实现的功能和最后的效果图

 

主要有以下这些类:


nfs多线程支持_nfs多线程支持


 效果图:

 

nfs多线程支持_c#_02


 


 


 

 

 

大致流程如下
          1、首先得有一个面板
          2、加入一辆坦克
          3、让坦克移动
          4、加入子弹
          5、加入敌人的坦克
          6、让敌人的坦克动起来,并且能发子弹
          7、子弹能够消灭坦克

实现功能:能够四处移动
                 能够打击敌人

                敌人能够移动

                能够模拟爆炸
                能够产生障碍
                能够增长生命

 

1,如何给游戏加上坦克等图片?

        下面使用到了ClassLoader类装载器获取系统资源
ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回这个类的class对象。

ClassLoader提供了两个方法用于从装载的类路径中取得资源:

     public URL getResource(String name);
     public InputStream getResourceAsStream(String name);

     这里name是资源的类路径,它是相对与“/”根路径下的位置。getResource得到的是一个URL对象来定位资源,而getResourceAsStream取得该资源输入流的引用保证程序可以从正确的位置抽取数据。
然而,程序中调用的通常并不是ClassLoader的这两个方法,而是Class的getResource和getResourceAsStream方法,因为Class对象可以从你的类得到(如YourClass.class或YourClass.getClass()),
而ClassLoader则需要再调用一次YourClass.getClassLoader()方法,但根据JDK文档的说法,Class对象的这两个方法其实是“委托”(delegate)给装载它的ClassLoader来做的,

所以只需要使用Class对象的这两个方法就可以了。
从文件中装入图像,使用类装载器的public URL getResource(String name);方法打开文件

 

这个要用到类的反射的相关知识,大家还有不懂的可以去查API文档或者百度

具体实现代码(大家可以在Tank类中找到这段代码):

private static Toolkit tk = Toolkit.getDefaultToolkit();
    private static Image[] tankImages = null;
    private static  Map<String,Image> imgs = new HashMap<String,Image>();
    //静态代码区
    static{
         tankImages=new Image[]{
                tk.getImage(Tank.class.getClassLoader().getResource("image/tankL.gif")),
                tk.getImage(Tank.class.getClassLoader().getResource("image/tankLU.gif")),
                tk.getImage(Tank.class.getClassLoader().getResource("image/tankU.gif")),
                tk.getImage(Tank.class.getClassLoader().getResource("image/tankRU.gif")),
                tk.getImage(Tank.class.getClassLoader().getResource("image/tankR.gif")),
                tk.getImage(Tank.class.getClassLoader().getResource("image/tankRD.gif")),
                tk.getImage(Tank.class.getClassLoader().getResource("image/tankD.gif")),
                tk.getImage(Tank.class.getClassLoader().getResource("image/tankLD.gif"))
        };
         
         imgs.put("L",tankImages[0]);
         imgs.put("LU",tankImages[1]);
         imgs.put("U",tankImages[2]);
         imgs.put("RU",tankImages[3]);
         imgs.put("R",tankImages[4]);
         imgs.put("RD",tankImages[5]);
         imgs.put("D",tankImages[6]);
         imgs.put("LD",tankImages[7]);
       
         
    }

 加入爆炸、子弹的图片和加入坦克的图片一样,这里就不做详细说明了

2,打第一颗子弹打出时没有爆炸产生

当我们照上面的方法加上爆照的图片后,发现第一发子弹打到敌人坦克时没有爆炸产生,从第二发开始都有爆炸产生
打一颗子弹时爆炸图片还木有到内存中来,第二次画爆照图片时已经到内存中里。我们定义的图片数组是static的,在类加载的时候,首先执行的就是static,这个时候还没有执行draw()方法,那么肯定不是这个原因

原因:getResource()执行时只拿到了图片的虚框(很多软件显示比较大的图片时也会出现这种情况),当调用draw()方法,还没有拿到数据

解决方案:

在Explode类中加入boolean类型 init变量

private static boolean init = false;

 在Explode类draw()方法里面第一行加入以下代码

if(!init){
		for (int i = 0; i < imgs.length; i++) {
			g.drawImage(imgs[i], -100, -100, null);
	        }
	init=true;
}

 

3、解决炮弹不消亡的问题
步骤:
         加入控制子弹生死的量bLive(Missle)
         当子弹已经死去就不需要对其重画
         当子弹飞出边界就死亡
         当子弹死亡就从容器中去除

package com.zkx.tank;
import java.awt.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;



public class Missile {
	public static final int XSPEED=10;
	public static final int YSPEED=10;
	
	public static final int WIDTH=10; 
	public static final int HEIGHT=10;
	int x,y;
	Direction dir;
	private boolean good;
	private boolean live=true;
	private TankClient tc;
	
	private static Toolkit tk = Toolkit.getDefaultToolkit();
	private static Image[] missileImages = null;
	private static  Map<String,Image> imgs = new HashMap<String,Image>();
	//静态代码区
	static{
		 missileImages=new Image[]{
				tk.getImage(Missile.class.getClassLoader().getResource("image/missileL.gif")),
				tk.getImage(Missile.class.getClassLoader().getResource("image/missileLU.gif")),
				tk.getImage(Missile.class.getClassLoader().getResource("image/missileU.gif")),
				tk.getImage(Missile.class.getClassLoader().getResource("image/missileRU.gif")),
				tk.getImage(Missile.class.getClassLoader().getResource("image/missileR.gif")),
				tk.getImage(Missile.class.getClassLoader().getResource("image/missileRD.gif")),
				tk.getImage(Missile.class.getClassLoader().getResource("image/missileD.gif")),
				tk.getImage(Missile.class.getClassLoader().getResource("image/missileLD.gif"))
		};
		 
		 imgs.put("L",missileImages[0]);
		 imgs.put("LU",missileImages[1]);
		 imgs.put("U",missileImages[2]);
		 imgs.put("RU",missileImages[3]);
		 imgs.put("R",missileImages[4]);
		 imgs.put("RD",missileImages[5]);
		 imgs.put("D",missileImages[6]);
		 imgs.put("LD",missileImages[7]);
		
		 
	}
	
	public boolean isLive() {
		return live;
	}
	
	public Missile(int x, int y,Direction dir) {
		this.x = x;
		this.y = y;
		this.dir = dir;
	}
	
	public Missile(int x,int y,boolean good,Direction dir,TankClient tc){
		this(x,y,dir);
		this.good=good;
		this.tc=tc;
	}
	public void draw(Graphics g){
		if(!live){
			tc.missiles.remove(this);
			return;
		}
		switch(dir){
		case L:
			g.drawImage(imgs.get("L"), x, y, null);
			break;	
		case LU:
			g.drawImage(imgs.get("LU"), x, y, null);
			break;	
		case U:
			g.drawImage(imgs.get("U"), x, y, null);
			break;	
		case RU:
			g.drawImage(imgs.get("RU"), x, y, null);
			break;
		case R:
			g.drawImage(imgs.get("R"), x, y, null);
			break;	
		case RD:
			g.drawImage(imgs.get("RD"), x, y, null);
			break;	
		case D:
			g.drawImage(imgs.get("D"), x, y, null);
			break;
		case LD:
			g.drawImage(imgs.get("LD"), x, y, null);
			break;	
		case STOP:
			break;	
			}
		
	}
	private void move() {
		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(x<0 || y<0 || x>TankClient.GAME_WIDTH||y>TankClient.GAME_HEIGHT){
			live=false;
			
		}
		
	}
	
	public Rectangle getRect(){
		return new Rectangle(x,y,WIDTH,HEIGHT);
	}
	
	public boolean hitTank(Tank t){
		if(this.live&&this.getRect().intersects(t.getRect())&&t.isLive() && this.good!=t.isGood()){
			if(t.isGood()){
				t.setLife(t.getLife()-20);
				if(t.getLife()<=0) t.setLive(false);
			}else{
				t.setLive(false);
			}
			this.live=false;
			Explode e=new Explode(x,y,tc);
			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;
	}
	
	public boolean hitWall(Wall w){
		if(this.live&&this.getRect().intersects(w.getRect())){
			this.live=false;
			return true;
		}
		return false;
		
	}
	
}

 

4、如何让坦克向8个方向行走?
步骤:
          添加记录按键状态的布尔量
           添加代表方向的量(使用枚举)
         根据按键状态确定Tank方向
           根据方向进行下一步的移动(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;	
	}