天天看點

java小遊戲坦克大戰(二)

今天繼續昨天的代碼繼續完善坦克大戰這個小遊戲:

主要完成如下的功能:

   5:讓坦克可以開火。

 * 6: 現在的子彈的處理方式是否有問題??把飛出螢幕的子彈移除掉。子彈飛出螢幕就不再繪制。

 * 7:添加地圖,繪制地圖

 * 8:解決閃屏:雙緩沖

 * 9:作業:坦克出生在螢幕的左下角、嘗試,在螢幕的右上角和左上角,添加兩個坦克。

5、第一步我們完成讓坦克的開炮功能,首先,我們先定義一個炮彈類Bullet,初步完成子彈的飛行軌迹。

代碼如下:

/**
* 炮彈類
* 屬性:坐标,速度,方向,傷害,大小,炮彈的敵我,顔色
* @author gc
*
*/
public class Bullet {
	private int x,y;
	private int speed = 8;
	private int dir;
	
	private int atk;
	private int diameter = 4;//diameter
	private boolean isFriendly;//炮彈的敵我屬性
	private Color color;//bullet's color
	
	//子彈是否可見
	private boolean visible = true;
	
	public Bullet(int x, int y, int dir, int atk, boolean isFriendly, Color color) {
		super();
		this.x = x;
		this.y = y;
		this.speed = speed;
		this.dir = dir;
		this.atk = atk;
		this.diameter = diameter;
		this.isFriendly = isFriendly;
		this.color = color;
	}
	
	public void draw(Graphics g) {
		//一旦子彈飛出螢幕,這代碼就不應該再被執行
		//如果不可見
		if (!visible)return;
		g.setColor(color);
		int w = diameter>>1;
		g.fillArc(x-w, y-w, diameter, diameter, 0, 360);
		//子彈的飛行軌迹
		logic();
		
		
	}

	//子彈的飛行軌迹
	private void logic() {
		switch (dir) {
		case Tank.DIR_UP:
			y -= speed;
			if(y < 0)visible = false;
			break;
		case Tank.DIR_DOWN:
			y += speed;
			if(y > Constant.FRAME_HEIGHT)visible = false;
			break;
		case Tank.DIR_LEFT:
			x -= speed;
			if (x < 0)visible = false;
			break;
		case Tank.DIR_RIGHT:
			x += speed;
			if(x > Constant.FRAME_WIDTH)visible = false;
			break;
		}
		
	}
/
	//增加在其他的類通路子彈是否可見的屬性
	public boolean isVisible() {
		return visible;
	}
	public void setVisible(boolean visible) {
		this.visible = visible;
	}

}
           

接着,在Tank的類中,完成讓坦克發射炮彈的功能,代碼如下:

//坦克發射炮彈功能
		public Bullet fire(){
			int w = width >> 1;
			int bulletX = 0;
			int bulletY = 0;
			//根據坦克(根據方向)的坐标來計算炮彈的坐标
			switch(dir){
			case DIR_UP:
				bulletX = x + w;
				bulletY = y - w;
				break;
			case DIR_DOWN:
				bulletX = x + w;
				bulletY = y + 3*w;
				break;
			case DIR_LEFT:
				bulletX = x - w;
				bulletY = y + w;
				break;
			case DIR_RIGHT:
				bulletX = x + 3*w;
				bulletY = y + w;
				break;
			}
			//生成的子彈的方向和攻擊力還有顔色都和坦克一緻
			return new Bullet(bulletX, bulletY, dir, atk, true, color);
		}
           

6、優化一下炮彈處理方式,防止容器中的炮彈數量過大,把飛出螢幕的子彈移除掉。子彈飛出螢幕就不再繪制。

代碼如下:

public void draw(Graphics g) {
		//一旦子彈飛出螢幕,這代碼就不應該再被執行
		//如果不可見
		if (!visible)return;
		g.setColor(color);
		int w = diameter>>1;
		g.fillArc(x-w, y-w, diameter, diameter, 0, 360);
		//子彈的飛行軌迹
		logic();
		
           

7、完成了Tank的發射炮彈的功能,接着我們開始繪制遊戲的地圖。在制作2d遊戲地圖時,我們要注意地圖有如下的幾層:/**

 * 地圖類。

 * 1:第一層,地表層,沒有碰撞

 * 2:碰撞層。

 * 3:精靈層

 * 4:遮擋層

今天,我們僅僅完成了,第一層,地表層和第二層碰撞層的繪制,首先我們根據定義的視窗,自定義地圖的資料,放在MapData類中。具體的資料數組如下:

package com.hbust.entity;
/**
 * 地圖碰撞層的資料的類
 * @author gc
 *
 */
public class MapData {
//	800*600  25  cols 32  rows 24 
	//0 代表沒有碰撞的塊。1代表可以碰撞的地圖塊
	public static int[][] mapDate0 = {
			{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
			{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
			{0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
			{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
			{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
			{0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0},
			{0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0},
			{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
			{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
			{0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0},
			{0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0},
			{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
			{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}	
	};
}
           

效果如下圖所示:

java小遊戲坦克大戰(二)

(這是完成地圖繪制之後的成品圖!)

接着,我們開始繪制地圖中的磚塊以及相應顔色填充,建立一個map類,具體代碼如下:

/**
 * 地圖類。
 * 1:第一層,地表層,沒有碰撞
 * 2:碰撞層。
 * 3:精靈層
 * 4:遮擋層
 * @author yhl
 * 屬性:寬度高度,單元格的寬度和高度tile,地圖資料
 *
 */

public class Map {
	//red block data
	public static final int RED_BLOCK_DATA = 1;
	// map width and height
	private int width , height;
	//地圖單元格的寬度,正方形
	public static final int TILE_WIDTH = 25;
	//地圖單元格的行數和列數
	private int rowTileCount,colTileCount;
	public Map(int rowTileCount, int colTileCount) {
		super();
		this.rowTileCount = rowTileCount;
		this.colTileCount = colTileCount;
		width = colTileCount * TILE_WIDTH;
		height = rowTileCount * TILE_WIDTH;
	}
	
	public void draw(Graphics g) {
		drawLayer0(g);
		drawLayer1(g);
		
	}

	

	//繪制第一層,地表層
	//單色填充, 應該有地表層的資料
	private void drawLayer0(Graphics g) {
		g.setColor(Color.GREEN);
		g.fillRect(0, Constant.TITLE_BAR_HEIGHT, width, height);
		
	}
	//繪制碰撞層
	private void drawLayer1(Graphics g) {
		for(int i=0;i<rowTileCount;i++){//i 代表行數
			for(int j = 0;j<colTileCount;j++){//j代表列數
				int mapData = MapData.mapDate0[i][j];
				if(mapData == RED_BLOCK_DATA){
					//繪制紅色的磚塊
					int x = j*TILE_WIDTH;
					int y = i*TILE_WIDTH;
					drawRedTile(g, x, y);
					}
				}
			}
				
	}
//		/**
//		 * 繪制一塊磚的方法
//		 */
//	private void drawRedTile(Graphics g, int x, int y) {
//			g.setColor(Color.RED);
//			g.fillRect(x, y, TILE_WIDTH, TILE_WIDTH);
//			g.setColor(Color.BLACK);
//			g.drawRect(x, y, TILE_WIDTH, TILE_WIDTH);
//	}
	/**
	 * 繪制一塊磚的方法
	 */
	private void drawRedTile(Graphics g, int x , int y){
		
		g.setColor(Color.RED);
		g.fillRect(x, y, TILE_WIDTH, TILE_WIDTH);
		g.setColor(Color.BLACK);
		g.drawRect(x, y, TILE_WIDTH, TILE_WIDTH);
		
		g.drawLine(x, y+8, x+24, y+8);
		g.drawLine(x+16, y, x+16, y+8);
		g.drawLine(x, y+16, x+24, y+16);
		g.drawLine(x+8, y+8, x+8, y+16);
		g.drawLine(x, y+25, x+24, y+25);
		g.drawLine(x+16, y+16, x+16, y+25);

	}
}
           

8、在運作的過程中,我們發現生成的視窗一直在閃爍,為什麼呢?大概是因為繪制的東西太多,而全部重新整理地時間不夠,于是這裡提出一種雙緩沖的技術來解決這個問題。具體代碼如下:

//地圖對象
	private Map map = new Map(Constant.FRAME_HEIGHT/Map.TILE_WIDTH,
			Constant.FRAME_WIDTH/Map.TILE_WIDTH);
	//雙緩沖技術:定義一張圖檔和地圖的大小一緻。用圖檔的畫筆将需要繪制的内容全部繪制到圖檔上來。
	//然後,再用系統畫筆,将圖檔一次性的繪制到視窗上來。
	private BufferedImage bufImg = new BufferedImage(Constant.FRAME_WIDTH, 
						Constant.FRAME_HEIGHT, BufferedImage.TYPE_3BYTE_BGR);
	private Graphics ImgG;
           

修改繪圖重新整理的方法(注意使用update方法):

public void update(Graphics g) {
		if (ImgG  == null) {
			ImgG = bufImg.getGraphics();		}
//		map.draw(g);
//		myTank.draw(g);
//		drawBullets(g);
		map.draw(ImgG);
		myTank.draw(ImgG);
		Tank1.draw(ImgG);
		Tank2.draw(ImgG);
		drawBullets(ImgG);
		//将圖檔一次性的會知道視窗上使用系統畫筆
				g.drawImage(bufImg, 0, 0, null);
//		g.drawString("子彈數量 = "+ bullets.size(),20, 100);
	}
           

9、今天的作業,調整坦克出生的位置好處理,主要我們生成另外的坦克,其實也不難,了解了坦克這麼生成的就行,主要修改GameFrame類中的initMytank方法,

myTank = new Tank(20,Constant.FRAME_HEIGHT-Constant.TITLE_BAR_HEIGHT-20,
				Tank.DIR_UP);
		Tank1 = new Tank(20,20,Tank.DIR_DOWN);
		Tank2 = new Tank(Constant.FRAME_WIDTH-Constant.TITLE_BAR_HEIGHT-20,20,
				Tank.DIR_DOWN);
           

接着,在更新的方法中更新建立的坦克:

myTank.draw(ImgG);
		Tank1.draw(ImgG);
		Tank2.draw(ImgG);
           

今天學得東西,比較多,比如雙緩沖技術還是不是非常了解,另外對2D遊戲地圖的制作有了相應的了解。

希望明天能繼續學到相應的知識。