2D飛機躲避炸彈遊戲設計思路及源碼Java實現

項目的GitHub地址:https://github.com/KeneathGuo/PlaneGame
 


v0.1

1.畫一個主窗口 MyGameFrame 繼承自 JFrame用於初始化主窗口

2.設置title、大小、位置。

使用匿名內部類,重寫windowClosing方法,通過點擊右上角的×來真關閉窗口(進程)。

在main方法創建一個主窗口對象f測試。

//匿名內部類,使得可以通過右上角關閉窗口
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);//0表示正常關閉
			}
		});

v0.2

1.在MyGameFrame類中加入重寫的 paint方法(自動被調用,當首次顯示窗口或者需要修理時自動調用),變量g相當於一支畫筆,在f這個窗口實例中畫各種各類圖形。

遊戲中所有物體都可以看成矩形。查看API Graphics。可以設置g這支畫筆顏色

 

2.提供一個GameUtil類,提供靜態方法以加載圖像。

import java.awt.Image;
import java.awt.image.*;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;;

public class GameUtil {
	//工具類最好將構造器私有化,都是static方法,沒必要去new一個對象
	private GameUtil() {
		
	}
	
	//返回指定路徑文件的圖片對象
	public static Image getImage(String path) {
		BufferedImage bi = null;
		try {
			URL u = GameUtil.class.getClassLoader().getResource(path);
			bi=ImageIO.read(u);
		}catch(IOException e) {
			e.printStackTrace();
		}
		
		return bi;
	}
}

3.建立兩個Image實例,放入兩張圖片,然後調用g.drawImage方法,在窗口裏畫出兩張圖,注意順序。


v0.3

1.利用多線程讓圖片動起來。定義一個內部類PaintThread,因爲內部類可以使用外部類的屬性和方法。

//內部類可以直接使用外部類的屬性和方法很方便
	class PaintThread extends Thread{
		@Override
		public void run() {
			while(true) {
				System.out.println("窗口被重畫了一次!!!");
				repaint();//這個方法定義在外部類MyGameFrame裏,重畫。
				
				try {
					Thread.sleep(40);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		
	}

這個Paint Thread類幫助我們反覆重畫窗口。我們調用repaint,這是JFrame的父類Component的方法。

2.在初始化窗口裏啓動線程(寫在launchFrame()方法裏)。

//啓動重畫窗口的線程
new PaintThread().start();

3.定義飛機的座標---planex和planey。  paint方法里加入 planex++ ,這樣每次重畫時飛機右移一個像素。


v0.4

1.使用awt的Frame會有雙緩衝問題。而使用swing的JFrame就不會有這個問題。

2.創建GameObject類,作爲遊戲中飛機、坦克、車、炸彈等物體的父類。設計並提取它們的共同點:圖片、橫縱座標、寬度、高度等等(private)

3.定義一個方法 drawSelf()畫出自己。

4.爲了方便創造,重載幾個構造方法,包括一個無參構造器。

5.設計飛機類Plane繼承自 GameObject。然後在主窗口中畫出一個飛機。還可以畫很多個,可以利用容器或者數組來畫。

6.在GameObject類中加入一個方法:

/**
* 返回物體所在的矩形,便於後續的碰撞檢測,很重要的後期的一個方法
* @return
*/
public Rectangle getRectangle() {
	return new Rectangle((int)x,(int)y, width, height);//爲以後的碰撞檢測做準備
	}

v0.5

1.鍵盤控制原理。先在MyGameFrame裏定義這樣的內部類加入鍵盤監聽:

//定義鍵盤監聽的內部類
class KeyMonitor extends KeyAdapter{
	@Override
	public void keyPressed(KeyEvent e) {
                //System.out.println(e.getKeyCode());//測試是否監聽成功
		plane.addDirection(e);
	}
	
	@Override
	public void keyReleased(KeyEvent e) {
		plane.minusDirection(e);
		
	}
}

然後在launchFrame()方法中增加代碼來給主窗口增加鍵盤監聽:

addKeyListener(new KeyMonitor());//給窗口增加鍵盤監聽。結合1中的測試代碼來測試

2.給飛機加上方向。在Plane類中加入四個boolean類型變量left,up,right,down分別對應上、下、左、右,同時修改drawSelf()方法以及添加一個addDirection()方法和一個minusDirection()方法:

	@Override
	public void drawSelf(Graphics g) {
		g.drawImage(img, (int)x, (int)y, null);
		
		if(left) {
			x-=speed;
		}
		if(right) {
			x+=speed;
		}
		if(up) {
			y-=speed;
		}
		if(down) {
			y+=speed;
		}
	}
	//按下某個鍵增加相應的方向
	public void addDirection(KeyEvent e) {
		//System.out.println("####"+e.getKeyCode());
		switch(e.getKeyCode()) {
		case KeyEvent.VK_LEFT:
			left=true;
			break;
		case KeyEvent.VK_UP:
			up = true;
			break;
		case KeyEvent.VK_RIGHT:
			right = true;
			break;
		case KeyEvent.VK_DOWN:
			down = true;
			break;
			
			
		}

	}
	//擡起某個鍵取消相應的方向
		public void minusDirection(KeyEvent e) {
			switch(e.getKeyCode()) {
			case KeyEvent.VK_LEFT:
				left=false;
				break;
			case KeyEvent.VK_UP:
				up = false;
				break;
			case KeyEvent.VK_RIGHT:
				right = false;
				break;
			case KeyEvent.VK_DOWN:
				down = false;
				break;
				
				
			}

		

3.在KeyMonitor內部類裏使用2中的兩個方法。

	//定義鍵盤監聽的內部類
	class KeyMonitor extends KeyAdapter{
		@Override
		public void keyPressed(KeyEvent e) {
			plane.addDirection(e);
		}
		
		@Override
		public void keyReleased(KeyEvent e) {
			plane.minusDirection(e);
			
		}
	}

這裏可以把speed調高一些,比如調成3.


v0.6

1.炮彈類Shell基本設計。這裏用實心的黃色橢圓實現(也可以加載新圖片實現)。炮彈的方向隨機,遇到邊界會反彈。

Shell繼承自GameObject類。增加屬性: 角度degree 。在構造器中設置隨機的degree:

	public Shell() {
		x=200;//橫座標
		y=200;//縱座標
		width=10;
		height=10;
		speed=3;
		degree = Math.random()*Math.PI*2;//生成一個0-2Π之間的隨機數,弧度制
		
	}

2.寫一個draw()方法畫出自己:

	public void draw(Graphics g) {
		Color c= g.getColor();
		g.setColor(Color.YELLOW);
		
		g.fillOval((int)x, (int)y, width, height);
		
		//炮彈沿着任意角度去飛
		x += speed*Math.cos(degree);
		y += speed*Math.sin(degree);
		
		if(x<0||x>Constant.GAME_WIDTH-width) {
			degree =Math.PI-degree;//左右方向翻轉,關於y軸對稱
		}
		if(y<30||y>Constant.GAME_HEIGHT-height/*30爲標題欄的寬度*/) {
			degree=-degree;
		}
		
		
		g.setColor(c);
	}

3.在主窗口中畫出一個炮彈Shell實例。  這裏建立一個Constant類專門放置所有常量,便於同步修改。


v0.7

1.使用數組來裝入多個炮彈,並且要實現(畫出來)。 在paint方法裏改。


v0.8

1.用了JFrame之後還在閃爍。當然,用Frame的話閃爍更嚴重。

雙緩衝技術繪圖過程如下:

1).在內存中創建與畫布一致的緩衝區

2)在緩衝區畫圖

3)將緩衝區位圖拷貝到當前畫布上

4)釋放內存緩衝區

雙緩衝即在內存中創建一個與屏幕繪圖區域一致的對象,先將圖形繪製到內存中的這個對象上,再一次性將這個對象上的圖形拷貝到屏幕上,這樣也能大大加快繪圖的速度。

在主窗口中加入以下代碼:

	//解決雙緩衝問題的代碼
	private Image offScreenImage = null;
	public void update(Graphics g) {
		if(offScreenImage == null)
			offScreenImage=this.createImage(Constant.GAME_WIDTH, Constant.GAME_HEIGHT);
		
		Graphics gOff = offScreenImage.getGraphics();
		paint(gOff);
		g.drawImage(offScreenImage, 0, 0, null);
	}

然後改爲繼承自Frame(效果更好)。

2.增加碰撞檢測。使用v0.4的第6步中的方法。

判斷兩個矩形是否相交:Rectangle的intersects()方法。

在主窗口的paint()方法中加入代碼:


		//飛機和炮彈的碰撞檢測
		boolean peng= shells[i].getRect().intersects(plane.getRect());

還要修改飛機的構造器裏的寬和高,直接設置爲圖片的寬和高。

		this.width=img.getWidth(null);
		this.height=img.getHeight(null);	

3.給飛機增加 boolean類型的live變量,定義飛機的生死。

4.增加爆炸類Explode的實現。爆炸類實際上存儲了一系列爆炸的圖片,然後,進行輪播。

package cn.gxg.game;

import java.awt.Graphics;
import java.awt.Image;

/*
 * 爆炸類
 */
public class Explode {
	double x, y;//爆炸的位置
	
	static Image[] imgs = new Image[16];//靜態數組,因爲圖片不要反覆加載,節省資源
	
	/**
	 * 靜態初始化塊,圖片是從1-16.循環是0-15
	 */
	static {
		for (int i = 0; i < 16; i++) {
			imgs[i] = GameUtil.getImage("images/explode/e" + (i + 1) + ".gif");
			imgs[i].getWidth(null);
		}
	}

	int count;//計數,依次畫第幾張圖片

	public void draw(Graphics g) {
		if (count <= 15) {
			g.drawImage(imgs[count], (int) x, (int) y, null);
			count++;
		}
	}

	public Explode(double x, double y) {
		this.x = x;
		this.y = y;
	}
}

5.主窗口中加入爆炸相關代碼。先定義,paint的時候在根據情況爆炸(如果碰撞)。

6.加入計時功能。主類中定義一個int類型的period。

遊戲開始時,定義一個startTime;遊戲結束時,定義一個endTime。兩者相減,即遊戲持續時間。


總結和展望:

1.能夠根據此例子開發大多數2D遊戲了。

2.需要加入更多功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章