飛機大戰部分算法,思維解析,附有源代碼.

##源代碼:http://download.csdn.net/download/u012234452/9583953
##QQ:1151315936


##完成後的基本樣式圖:
這裏寫圖片描述
##結構圖:
這裏寫圖片描述
###父類:FlyingObject,子類:Airplane(小飛機),BigPlane(大飛機),Bullet子彈,Bee(蜜蜂),Hero(英雄機),Sky(天空).世界:World.接口:Award(獎勵),Enemy(敵人)
##根據邏輯分析把大飛機,小飛機,蜜蜂,子彈,英雄機的公共屬性和方法泛化到父類(FlyingObject)中去.
這裏寫圖片描述
##讓World類繼承JPanel,構建出窗口,重寫paint方法
這裏寫圖片描述
#FlyingObject.java
##1.默認構造器,用於初始化

	public FlyingObject() {
		life = 1;
		state = ACTIVE;
	}

##2.有參構造器

	public FlyingObject(double width,double heigth){
		this();//調用無參數的構造器,必須寫在第一行.
		this.x = (int)(Math.random()*(480-width));
		this.y = -heigth;
		this.width = width;
		this.heigth = heigth;
		step = Math.random()*3+0.8;//初始化step爲[0.8,3.8)之間的數
	}

##3.重寫了toString()方法可以用來測試用

	public String toString() {
		return x+","+y+","+width+","+heigth+","+image;
	}

##4.父類中重寫了paint(Graphics g)方法,方便了子類的使用,用於繪圖

	public void paint(Graphics g) {
		g.drawImage(image, (int)x, (int)y, null);//繪製圖片
	}

##5.父類中重構了move方法,實現了各種飛行物的移動和播放銷燬動畫功能

	public void move(){
		if(state == ACTIVE){
			y += step;
			return ;
		}
		if(state == DEAD){
			//從子類對象中獲取下一張照片
			BufferedImage img = nextImage();
			if(img == null){
				state = REMOVE;//沒有照片則回收
			}else{
				image = img;//否則把子類的圖片傳給image
			}
			//越界則銷燬
			if(y>=825){
				state = REMOVE;
			}
		}
	}

##6.子類中必須有的方法,返回下一個要播放的照片引用,如果返回null表示沒有可播放的照片了.(雖然只有一句,但是非常重要)

	protected abstract BufferedImage nextImage();

##7.飛行物被打了一下,生命-1,當生命等於0的時候爲死亡狀態
這裏寫圖片描述

	public void hit(){
		if(life>0){
			life--;
		}
		if(life==0){
			state = DEAD;
		}
	}

##8.經典算法:碰撞檢測的方法,用於檢測物體的位置是否在碰撞的範圍內
這裏寫圖片描述

	public boolean duang(FlyingObject obj){
		//this(x,y,w,h)
		//obj(x,y,w,h)
		double x1 = this.x - obj.width;
		double x2 = this.x + this.width;
		double y1 = this.y - obj.width;
		double y2 = this.y + this.heigth;
		return x1<obj.x&&obj.x<x2&&y1<obj.y&&obj.y<y2;
	}

##9.狀態檢查方法,用於返回狀態

	public boolean isDead(){
		return state == DEAD;
	}
	/** 檢查飛行物是否活動的 */
	public boolean isActive(){
		return state == ACTIVE;
	}
	/** 檢查飛行是否可以被刪除*/
	public boolean canRemove(){
		return state == REMOVE;
	}
	/** 飛行物添加"去死"方法*/
	public void goDead(){
		if(isActive()){
			state = DEAD;
		}
	}

#Airplane.java
##1.獲取的小飛機的資源

	private static BufferedImage[] imgs;// 定義一個唯一的圖片素材
	// 靜態代碼塊用於獲取圖片資源
	static {
		imgs = new BufferedImage[5];
		try {
			for (int i = 0; i < imgs.length; i++) {
				String png = "cn/feike/shoot/airplane" + i + ".png";//獲取圖片路徑
				imgs[i] = ImageIO.read(Airplane.class.getClassLoader().getResourceAsStream(png));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

##2.初始化小飛機,同時初始化照片

	public Airplane() {
		super(49, 36);// 初始化小飛機
		this.image = imgs[0];
	}

##3.繼承父類的抽象方法,播放下一張圖片,用於銷燬

	protected BufferedImage nextImage() {
		index++;
		if(index >= imgs.length){
			return null;//如果下標大於等於數組長度,無圖可播,返回null.
		}
		return imgs[index];
	}

#BigPlane.java
##1.初始化大飛機,讓生命等於3(即子彈打三下才打死)

	public BigPlane() {
		super(69,99);//初始化大飛機
		this.image = imgs[0];
		life = 3;
	}

#BigPlaneAward.java
##1.繼承了大飛機,重寫了paint方法,在飛機外面加上一個框框

	public void paint(Graphics g){
		super.paint(g);//重載父類的paint方法
		//在大飛機外面加上一個框框
		g.drawRect((int)x, (int)y, (int)width, (int)heigth);
	}

##2.固定獎勵,打掉返回雙倍獎勵

	public int getAward() {
		return DOUBLE_FIRE;//獲取雙倍獎勵
	}

#Hero.java
##1.初始化英雄飛機,讓其在地圖的下方出現

	public Hero() {
		this.width = 97;
		this.heigth = 124;
		this.x = (480-width)/2;
		this.y =500;
		this.image = imgs[0];
	}

##2.重寫了move()的無參方法,用於顯示動態效果和英雄飛機的銷燬效果
這裏寫圖片描述

	public void move() {
		if(isActive()){
			n++;
			int i = n%2;//用這個i的值來控制切換的圖片(i=n%2)的切換速度快於(i=n/2%2)
			this.image = imgs[i];//來回切換圖片,顯示出動畫的效果.
		}
		if(isDead()){
			//從子類獲取下一張照片
			BufferedImage img = nextImage();
			if(img==null){
				state = REMOVE;
			}else{
				image = img;
			}
		}
	}

##3.再次重寫了move()有參方法,用於英雄飛機隨着鼠標移動

	public void move(int x,int y){
		this.x = x-width/2;
		this.y = y-heigth/2;
	}

##4.射擊方法,實現是單槍射擊還是雙槍射擊
這裏寫圖片描述

	public Bullet[] shoot(int type){
		int x = (int)(this.x+width/2-8/2);//子彈的出場的x位置
		int y = (int)(this.y-30);//子彈的出場的y位置
		if(type == 1){//單槍發射
			return new Bullet[]{
					new Bullet(x,y)//創建一顆子彈
			};
		}
		if(type == 2){
			return new Bullet[]{
					new Bullet(x-30, y),//第一個子彈原出場的位子向左移動30
					new Bullet(x+30,y)//第二個子彈原出場的位子向右移動30
			};
		}
		return new Bullet[0];
	}

#Bullet.java
##1.初始化子彈,因爲子彈跟着英雄飛機一起走,所以需要傳入兩個參數.

	public Bullet(int x,int y) {
		this.x = x;
		this.y = y;
		width = 8;
		heigth = 14;
		this.image = img;
	}

##2.重寫子彈move方法,子彈是從下往上移動

	public void move() {
		if(state == ACTIVE){
			y -= 8;
			if(y<=-heigth){
				state = REMOVE;
			}
		}
	}

##3.重寫了nextImage()和hit()方法,子彈撞擊後直接銷燬

	protected BufferedImage nextImage() {
		return null;
	}
	public void hit(){
		state = REMOVE;//子彈撞擊後銷燬
	}

#Bee.java
##1.初始化蜜蜂,蜜蜂的出場方向是隨機的

	public Bee() {
		super(60, 50);
		this.image = imgs[0];
		direction = Math.random() > 0.5 ? -2 : 2;// 初始化蜜蜂的移動方向,各佔50%的概率.
	}

##2.重寫了move()方法,蜜蜂的移動方向實現斜着的
這裏寫圖片描述

	public void move() {
		super.move();// y += step;即把y座標交個父類來處理
		if (state == ACTIVE) {
			x += direction;
			// 如果蜜蜂移動到右邊邊界,則反方向移動
			if (x >= 480 - width) {
				direction = -2;
			}
			// 如果蜜蜂移動到左邊邊界,則反方向移動
			if (x < 0) {
				direction = 2;
			}
		}
		// 蜜蜂的動漫效果
		n++;
		int i = n % 2;// 用來控制蜜蜂的扇翅膀的速度
		this.image = imgs[i];// 來回切換圖片,顯示出動畫的效果.
	}

#Sky.java
##1.初始化天空,改變了天空的移動速度,和第二種圖片的高度

	public Sky() {
		this.x = 0;
		this.y = 0;
		this.width=480;
		this.heigth=825;
		this.image = img;
		this.step = 1;//初始化圖片移動的速度
		y1 = -heigth;//初始化第二張圖片的位置
	}

##2.重寫了move()方法,兩張圖片同時移動,一張圖片移除下邊界就重新定義高度再移動.
這裏寫圖片描述

	public void move() {
		y++;
		y1++;
		if(y>=heigth){
			y = -heigth;//如果第一張圖移動出下邊界位置,則返回頂部
		}
		if(y1>=heigth){
			y1 = -heigth;//如果第二張圖移動出下邊界位置,則返回頂部
		}
	}

##3.重寫了paint,用於繪製兩張背景圖片的位置.

	public void paint(Graphics g) {
		g.drawImage(image, (int)x, (int)y, null);
		g.drawImage(image, (int)x, (int)y1, null);
	}

##4.天空沒有銷燬圖片,所以返回null

	protected BufferedImage nextImage() {
		return null;
	}

#Award.java
##1.獎勵接口,用於獲取獎勵

public interface Award {
	int LIFE = 0;
	int FIRE = 1;
	int DOUBLE_FIRE = 2;
	
	int getAward();
}

#Enemy.java
##1.敵人接口,用於獲取得分

public interface Enemy {
	int getScore();
}

#World.java
##1.初始化三張開始,暫停,結束照片

	private static BufferedImage pause;//暫停
	private static BufferedImage ready;//開始
	private static BufferedImage gameOver;//結束
	
	static{
		try {
			String png = "cn/feike/shoot/start.png";
			ready = ImageIO.read(World.class.getClassLoader().getResourceAsStream(png));
			png = "cn/feike/shoot/pause.png";
			pause = ImageIO.read(World.class.getClassLoader().getResourceAsStream(png));
			png = "cn/feike/shoot/gameover.png";
			gameOver = ImageIO.read(World.class.getClassLoader().getResourceAsStream(png));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

##2.初始化世界中的物體

	public World() {
		flyingObjects = new FlyingObject[]{};//初始化飛行物數組
		bullets = new Bullet[]{};//初始化子彈數組
		hero = new Hero();//初始化英雄飛機
		sky = new Sky();//初始化一片天空
		nextTime = System.currentTimeMillis()+1000;//初始化下一秒的系統時間
	}

##3.做每隔一秒出場一個飛行物
這裏寫圖片描述

	//nextOne方法被定時(1/24秒)調用一次
	public void nextOne(){
		//方法調用一次,獲取一下當前時間.
		long now = System.currentTimeMillis();//獲取當前系統時間的毫秒數
		if(now>=nextTime){
			nextTime = now + 1000;//控制飛行物的出場時間間隔,即每隔一秒出場一個飛行物
			FlyingObject obj = randomOne();//調用randomOne方法,隨機生成一個飛行物
			flyingObjects = Arrays.copyOf(flyingObjects, flyingObjects.length+1);//數組擴容
			flyingObjects[flyingObjects.length-1]=obj;//隨機生成的飛行物放到數組中去
		}
	}

##4.隨機出場一個飛行物,出場的概率不同

	private static FlyingObject randomOne(){
		int n = (int)(Math.random()*10);//[0,10)
		//這個上面這個隨機數的大小也可以調整不同飛行物的出場概率
		switch(n){
		case 0 : return new Bee();
		case 1 : 
		case 2 : return new BigPlane();
		case 3 : 
		case 4 : return new BigPlaneAward();
		default : return new Airplane();//概率最高
		}
	}

##5.重寫了paint(Graphics g)方法,繪製出天空的物體
這裏寫圖片描述
這裏寫圖片描述

	public void paint(Graphics g) {
		sky.paint(g);//畫出天空,即背景圖片
		hero.paint(g);//畫出英雄飛機
		for(FlyingObject fly : flyingObjects){
			fly.paint(g);//繪製每個飛行物
		}
		for(Bullet bullet : bullets){
			bullet.paint(g);//繪製每個子彈
		}
		//添加分數,生命,子彈類型的顯示
		g.drawString("SCORE:"+score, 20, 30);
		g.drawString("LIFE:"+life, 20, 50);
		g.drawString("FIRE:"+fireType, 20, 70);
		
		switch(state){
		case PAUSE : g.drawImage(pause, 0, 0, null);
		case READY : g.drawImage(ready, 0, 0, null);
		case GAME_OVER : g.drawImage(gameOver, 0, 0, null);
		}
	}

##6.添加一個定時器和啓動定時器的方法.每1/24秒一個動作
這裏寫圖片描述

	public void action(){
		timer = new Timer();//初始化一個定時器
		timer.schedule(new TimerTask() {
			
			@Override
			public void run() {
				if(state == RUNNING){
					nextOne();//每(1/24秒)生成一個飛行物
					move();//各種物體的移動
					shoot();//射擊封裝到shoot方法
					duangDuang();//碰撞方法
					removeObjects();//回收方法
					heroLifeCircle();//英雄的聲明週期
				}
				repaint();//重新繪製JPanel
			}
		}, 0,1000/24);//從0開始,間隔爲(1/24)秒
		//創建鼠標的監聽即開啓鼠標的監聽
		MouseAdapter l = new MouseAdapter() {
			@Override
			public void mouseMoved(MouseEvent e) {
				if(state == RUNNING){
					int x = e.getX();
					int y = e.getY();
					hero.move(x,y);//傳遞移動的x,y座標.
				}
			}
			@Override
			public void mouseExited(MouseEvent e) {
				if(state == RUNNING){
					state = PAUSE;
				}
			}
			@Override
			public void mouseEntered(MouseEvent e) {
				if(state == PAUSE){
					state = RUNNING;
				}
			}
			@Override
			public void mouseClicked(MouseEvent e) {
				if(state == READY){
					state = RUNNING;
				}
				if(state == GAME_OVER){//遊戲結束所有參數重置。
					score = 0;
					life = 3;
					fireType = 1;
					hero = new Hero();
					bullets = new Bullet[0];
					flyingObjects = new FlyingObject[0];
					state = READY;
				}
			}
		};
		//將監聽器加入到當前的面板中
		addMouseListener(l);
		addMouseMotionListener(l);
	}

##7.英雄飛機的生命週期

	public void heroLifeCircle(){
		//檢查飛行物和英雄的碰撞
		if(hero.isActive()){
			for(FlyingObject plane : flyingObjects){
				if(plane.isActive()&&plane.duang(hero)){
					plane.goDead();
					hero.goDead();
				}
			}
		}
		if(hero.canRemove()){
			if(life>0){
				life--;
				hero = new Hero();//新生一個英雄
				//清場,死了的瞬間,再碰撞英雄不死,碰撞物死.
				for(FlyingObject plane : flyingObjects){
					if(plane.isActive()&&plane.duang(hero)){
						plane.goDead();
					}
				}
			}else{
				//遊戲結束
				state = GAME_OVER;
			}
		}
	}

##8.刪除掉沒有用的子彈和飛機
這裏寫圖片描述

	public void removeObjects(){
		//刪除掉廢棄的子彈
		Bullet[] ary = {};//初始化一個子彈數組
		for(Bullet b : bullets){
			if(b.canRemove()){
				continue;//忽略掉要刪除的子彈
			}
			ary = Arrays.copyOf(ary, ary.length+1);//數組擴容
			ary[ary.length-1] = b;//把沒有刪掉的子彈重新添加到數組中去
		}
		bullets = ary;//bullets變量引用到ary數組上,拋棄原來的引用
		//刪除掉廢棄的飛機
		FlyingObject[] arr = {};//初始化一個飛行物數組
		for(FlyingObject obj : flyingObjects){
			if(obj.canRemove()){
				continue;//忽略掉廢棄的飛行物
			}
			arr = Arrays.copyOf(arr, arr.length+1);//數組擴容
			arr[arr.length-1] = obj;////把沒有刪掉的飛行物添加到數組中去
		}
		flyingObjects = arr;
	}

##9.在world裏面添加碰撞檢測方法
這裏寫圖片描述

	public void duangDuang(){
		for(FlyingObject plane : flyingObjects){
			if(plane.isActive()){
				if(shootByBullet(plane)){
					plane.hit();
				if(plane.isDead()){
					//計分,獲取獎勵
					if(plane instanceof Enemy){
						Enemy enemy = (Enemy)plane;
						int s = enemy.getScore();
						score += s;
					}
					if(plane instanceof Award){
						Award award =(Award)plane;
						int awd = award.getAward();
						if(awd == Award.LIFE){
							life++;
						}else if(awd == Award.FIRE){
							fireType = 1;
						}else if(awd == Award.DOUBLE_FIRE){
							fireType = 2;
						}
					}
				}
				}
			}
			
		}
	}

##10.檢查每個子彈是否和飛行物碰撞

	public boolean shootByBullet(FlyingObject plane){
		for(Bullet bullet : bullets){
			if(bullet.duang(plane)){
				bullet.hit();
				return true;
			}
		}
		return false;
	}

##11.射擊控制方法被定時器定時調用,控制英雄飛機射擊子彈的速度

	public void shoot(){
		//方法調用一次,獲取一下當前時間.
		long now = System.currentTimeMillis();//獲取當前系統時間的毫秒數
		if(now>nextShootTime){
			nextShootTime=now + 500;//半秒發射一發
			Bullet[] arr = hero.shoot(fireType);//子彈射擊類型
			bullets = Arrays.copyOf(bullets, bullets.length+arr.length);
			System.arraycopy(arr, 0, bullets, bullets.length-arr.length, arr.length);
		}
	}

##12.各種物體的移動

	private void move() {
		//每個飛行物移動一下,重新繪製JPanel方法
		for(FlyingObject fly : flyingObjects){
			fly.move();
		}
		//每個子彈的移動
		for(Bullet bullet : bullets){
			bullet.move();
		}
		sky.move();//天空移動
		hero.move();//英雄飛機移動
	}

##13.main()方法,設置窗口大小並顯示

	public static void main(String[] args) {
		World world = new World();//創建一個world面板
		JFrame frame = new JFrame();//創建一個窗口
		frame.setSize(400, 680);//窗口大小
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//關閉窗口即關閉程序
		frame.setLocationRelativeTo(null);//讓窗口居中
		frame.add(world);//把world面板加到JFrame窗口裏去
		frame.setVisible(true);//窗口可見
		
		world.action();//啓動定時器
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章