Java飛機大戰小遊戲(四):敵我飛機的相愛相殺

  • 項目:飛機大戰小遊戲之繪製敵我飛機的碰撞檢測
  • 項目簡介:飛機大戰小遊戲的角色特徵+行爲
  • 開發工具:eclipse
  • 語言:Java
  • 知識點:Rectangle、模塊化、監聽器、定時器

代碼架構:程序分兩個包,共七個類,將前面的代碼整合一個,然後修改主程序即可
在這裏插入圖片描述

操作效果圖:
在這裏插入圖片描述

ImageUtil.java:遊戲加載圖片的工具類

HitUtil.java:碰撞檢測工具類

package com.demo.util;

import java.awt.Rectangle;
import java.awt.image.BufferedImage;

import com.demo.model.EnemyBullet;
import com.demo.model.HeroBullet;
import com.demo.model.HeroPlane;
import com.demo.model.SmallPlane;

/**
 * 碰撞工具類
 */
public class HitUtil {
	/**
	 * 第一種碰撞邏輯:2個矩形碰撞
	 *  矩形1的參數是:左上角的座標是(x1,y1),寬度是w1,高度是h1;
	 *  矩形2的參數是:左上角的座標是(x2,y2),寬度是w2,高度是h2。
	 */
	private static boolean isHit(int x1, int y1, int w1,int h1, int x2,int y2,int w2, int h2) {
		
		//兩者的矩形範圍	
		Rectangle tank1 = new Rectangle(x1,y1,w1,h1);
		Rectangle tank2 = new Rectangle(x2,y2,w2,h2);
		//判斷兩個矩形是否有交集,crash 爲 true 說明碰撞了
		return tank1.intersects(tank2);
	}
	
	//英雄子彈與敵機子彈碰撞
	public static boolean heroBulletHitBullet(HeroBullet hb, EnemyBullet eb) {
		BufferedImage x = hb.getImage();
		BufferedImage y = eb.getImage();
		return isHit(hb.getX(), hb.getY(), x.getWidth(), x.getHeight(), eb.getX(), eb.getY(), y.getWidth(), y.getHeight());
	}
	//英雄子彈與小敵機碰撞
	public static boolean heroBulletHitPlane(HeroBullet hb, SmallPlane eb) {
		BufferedImage x = hb.getImage();
		BufferedImage y = eb.getImage();
		return isHit(hb.getX(), hb.getY(), x.getWidth(), x.getHeight(), eb.getX(), eb.getY(), y.getWidth(), y.getHeight());
	}
	
	//英雄飛機與敵機子彈碰撞
	public static boolean heroPlaneHitBullet(HeroPlane hb, EnemyBullet eb) {
		BufferedImage x = hb.getImage();
		BufferedImage y = eb.getImage();
		return isHit(hb.getX()-x.getWidth()/6, hb.getY()-x.getHeight()/6, x.getWidth()/3, x.getHeight()/3, eb.getX(), eb.getY(), y.getWidth(), y.getHeight());
	}
	
	/**
	 * 第一種碰撞邏輯:2個矩形碰撞,取矩形內最大圓碰撞,即2個矩形轉爲2個圓形碰撞
	 *   r1  r2 分別爲2個圓的半徑
	 *   第一個圓圓心:(x1,y1) 
	 *   第二個圓圓心:(x2,y2)  
	 *   
	 * 原理:勾股定理  
	 * 公式:(x1-x2)^2 + (y1 - y2)^2 < r1 + r2   
	 *  
	 */
	//英雄飛機與敵機碰撞
	public static boolean heroPlaneHitEnamyPlane(HeroPlane hb, SmallPlane eb) {
		BufferedImage x = hb.getImage();
		BufferedImage y = eb.getImage();
		
		int centerX1 =hb.getX();
		int centerY1 =hb.getY();
		int centerX2 = eb.getX() + y.getWidth()/2, centerY2 = eb.getY() + y.getHeight()/2;
		// 求兩圓的圓心距
		double length = Math.sqrt(Math.pow(centerX1 - centerX2, 2)+ Math.pow(centerY1 - centerY2, 2));
		
		int r1 = Math.min(x.getWidth(), x.getHeight())/2;
		int r2 = Math.min(y.getWidth(), y.getHeight())/2;
		
		return length < (r1 + r2);
	}
	
}

HeroPlane.java / HeroBullet.java:我方飛機和子彈

SmallPlane.java:添加敵機狀態 - - 活着、死了

package com.demo.model;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.demo.util.ImageUtil;

/**
 * 小敵機
 */
public class SmallPlane {

	private int x; // 飛機放置在面板上的x軸
	private int y; // 飛機放置在面板上的y軸
	
	// 定義構造函數
	public SmallPlane() {
		this.x = new Random().nextInt(GameStart.WIDTH - IMAGES[0].getWidth());
		this.y = -IMAGES[0].getHeight();
	}
	
	//狀態
	public static final int ALIVE = 0; //活着
	public static final int DEATH = 1;	//死了,擊中目標:飛機/對方子彈
	public static final int DELETE = 2; //可刪除, 畫布中不需要畫出來
	
	private  int state = ALIVE;		//默認是表示活着

    private static BufferedImage[] IMAGES;
	static {
		IMAGES = new BufferedImage[5];
		for (int i = 0; i < IMAGES.length; i++) {
			IMAGES[i] = ImageUtil.readImage("smallplane"+i+".png");
		}
	}
	//小敵機的圖標
	private int index = 1; //圖標索引
	public BufferedImage getImage() {
		if(state == ALIVE) {
			return IMAGES[0];
		}else {
			if(state == DEATH) {
				if(index == IMAGES.length) {
					state = DELETE;
				}else {
					return IMAGES[index];
				}
			}
			index ++;
		}
		return null;
	}

	/**
	 * 行爲
	 */
	// 移動
	public void movePlane() {
		this.y += 3;
	}

	// 子彈的移動
	public void moveBullet() {
		this.y += 3;
	}

	// 開火的方法
	public List<EnemyBullet> fire() {
		List<EnemyBullet> list = new ArrayList<EnemyBullet>();
		list.add(new EnemyBullet(this.x + IMAGES[0].getWidth() / 2, this.y + IMAGES[0].getHeight() / 2));
		return list;
	}
	
	//是否越界
	public boolean isOutOfBound() {
		return  this.y > GameStart.HEIGHT + IMAGES[0].getHeight();
	}
	
	//getters/setters方法
}

HeroBullet.java:添加敵機子彈狀態 - - 活着、死了

package com.demo.model;

import java.awt.image.BufferedImage;

import com.demo.util.ImageUtil;

/**
 * 飛機子彈
 */
public class HeroBullet{
	
	protected int x;  //畫布的x軸
	protected int y;  //畫布的y軸
	
	//子彈的狀態
	public static final int ALIVE = 0; //活着
	public static final int DEATH = 1;	//死了,擊中目標:飛機/對方子彈
	public static final int DELETE = 2; //可刪除, 畫布中不需要畫出來
	
	private  int state = ALIVE;			//默認是表示活着
	
	//構造函數
	public HeroBullet(int x,int y){
		this.x = x;
		this.y = y;
	}
	
	//定義多張圖片
	private static BufferedImage[] IMAGES;
	static {
		IMAGES = new BufferedImage[5];
		for (int i = 0; i < IMAGES.length; i++) {
			IMAGES[i] = ImageUtil.readImage("herobullet"+i+".png");
		}
	}
	//飛機子彈的圖標
	private int index = 1; //圖標索引
	public BufferedImage getImage() {
		if(state == ALIVE) {
			return IMAGES[0];
		}else {
			if(state == DEATH) {
				if(index == IMAGES.length) {
					state = DELETE;
				}else {
					return IMAGES[index];
				}
			}
			index ++;
		}
		return null;
	}
	
	//行爲->	移動
	public void move(){
		this.y -= 3; 
	}
	//出界
	public boolean isOutOfBound(){
		return this.y<0;
	}

	//getters/setters方法
}

GameStart.java:主程序

package com.demo.model;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.demo.util.HitUtil;
import com.demo.util.ImageUtil;

/**
 * 飛機大戰小遊戲(四):碰撞喫雞
 * @author suoyue_zhan
 */
public class GameStart extends JPanel {
	public static final int WIDTH = 400;		//遊戲界面寬度
	public static final int HEIGHT = 654; 		//遊戲界面長度

	private BufferedImage background1 = ImageUtil.readImage("background.jpg");		// 背景圖片:靜態
	
	private HeroPlane heroPlane = new HeroPlane(); //英雄機
	//英雄飛機子彈對象->list集合
	private List<HeroBullet> heroBullets = new ArrayList<HeroBullet>();
		
	 //將背景繪製到面板中
	private void paintHeroPlane(Graphics g) {
		BufferedImage image = heroPlane.getImage();
		
		if(heroPlane.getX() == 0) { //飛機剛出來時候
			g.drawImage(image,WIDTH/2-image.getWidth()/2, HEIGHT/2+image.getHeight(), null);
		}else {
			g.drawImage(image,heroPlane.getX()-image.getWidth()/2, heroPlane.getY() - image.getHeight()/2, null);
		}
	}
	
	//繪製飛機的分數與生命
	private void  paintScoreAndLife(Graphics g) {
		g.setColor(new Color(255, 0, 0));				//設置畫筆顏色	紅0	綠0	藍0	(0~255)
		g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 20));	//設置字體	字體、樣式、字號
		g.drawString("score: "+heroPlane.getScore(), 10, 25);
		g.drawString("life: "+heroPlane.getLife(), 10, 45);
	}
	//繪製飛機子彈
	public void paintHeroBullet(Graphics g){
		for(int i=0;i<heroBullets.size();i++){
			//每個英雄的子彈
			HeroBullet heroBullet = heroBullets.get(i);
			BufferedImage image = heroBullet.getImage();
			
			if(image != null && !heroBullet.isOutOfBound()) {

				g.drawImage(image, heroBullet.getX(), heroBullet.getY(), null);
		
			}
		}
	}
	//飛機發射子彈
	private void heroPlaneFire(){
		List<HeroBullet> list = heroPlane.fire();
		
		//收集子彈,動態繪製
		heroBullets.addAll(list);
	}
	//飛機子彈移動
	private void heroBulletMove() {
		for (HeroBullet heroBullet : heroBullets) {
			heroBullet.move();
		}
	}
	
	//敵機的集合
	private List<SmallPlane> enemyPlanes = new ArrayList<>();
	//敵機登場
	private void enemyPlaneEnter() {
		enemyPlanes.add(new SmallPlane());
	}
	//繪製敵機
	private void paintEnemyPhane(Graphics g) {
		for (int i = 0; i < enemyPlanes.size(); i++) {
			SmallPlane enemyPlane = enemyPlanes.get(i);
			BufferedImage image = enemyPlane.getImage();
			if(image != null && !enemyPlane.isOutOfBound()) {
				g.drawImage(image, enemyPlane.getX(), enemyPlane.getY(), null);
			}
		}
	}
	//小敵機移動
	public void enemyPlaneMove() {
		for (SmallPlane enemyPlane : enemyPlanes) {
				enemyPlane.movePlane();

		}
	}
	private List<EnemyBullet> enemyBullets = new ArrayList<EnemyBullet>(); //敵機子彈集合
	//敵機發送子彈
	private void enemyPlaneFire() {
		for (int i = 0; i < enemyPlanes.size(); i++) {
			SmallPlane enemyPlane = enemyPlanes.get(i);
			List<EnemyBullet> list = enemyPlane.fire();
			enemyBullets.addAll(list);
		}
	}
	//繪製敵機子彈:當子彈出界後,不要繪製出來
	private void paintEnemyBullet(Graphics g) {
		for(int i = 0; i < enemyBullets.size(); i++) {
			EnemyBullet enemyBullet = enemyBullets.get(i);
			BufferedImage image = enemyBullet.getImage();
			if(image != null && !enemyBullet.isOutOfBound()) {
				g.drawImage(image, enemyBullet.getX(), enemyBullet.getY(), null);
			}
		}
	}
	//敵機子彈移動
	private void enemyBulletMove() {
		for (EnemyBullet enemyBullet : enemyBullets) {
			enemyBullet.move();
		}
	}
	
	//檢查是否碰撞
	private void checkHit() {
		
		//飛機子彈與敵機子彈碰撞
		for(HeroBullet heroBullet : heroBullets) {
			for(EnemyBullet enemyBullet : enemyBullets) {
				if(heroBullet.getState() == HeroBullet.ALIVE && 
						enemyBullet.getState() == EnemyBullet.ALIVE &&
							HitUtil.heroBulletHitBullet(heroBullet, enemyBullet)
						) {
					heroBullet.setState(HeroBullet.DEATH);
					enemyBullet.setState(EnemyBullet.DEATH);
					
					System.out.println("飛機子彈與敵機子彈撞了.....");
				}
			}
		}
		
		//飛機子彈與敵機碰撞 
		for(HeroBullet heroBullet : heroBullets) {
			for(SmallPlane enemyPlane : enemyPlanes) {
				if(heroBullet.getState() == HeroBullet.ALIVE && 
						enemyPlane.getState() == SmallPlane.ALIVE &&
							HitUtil.heroBulletHitPlane(heroBullet, enemyPlane)
						) {
					heroBullet.setState(HeroBullet.DEATH);
					enemyPlane.setState(SmallPlane.DEATH);
					
					System.out.println("飛機子彈與敵機撞了.....");
				}
			}
		}
		
		//英雄飛機與敵機子彈碰撞		
		//英雄飛機與敵機碰撞
		
	}
		
	//清除碰撞
	private void clearHitObject() {
		//英雄子彈
		synchronized (heroBullets) {
			Iterator<HeroBullet> iter1 = heroBullets.iterator();
			while(iter1.hasNext()) {
				HeroBullet next = iter1.next();
				if(next.isOutOfBound() || next.getState() != HeroBullet.ALIVE) {
					iter1.remove();
				}
			}
		}
		
		//敵機子彈
		synchronized (enemyBullets) {			
			Iterator<EnemyBullet> iter2 = enemyBullets.iterator();
			while(iter2.hasNext()) {
				EnemyBullet next = iter2.next();
				if(next.isOutOfBound() || next.getState() != EnemyBullet.ALIVE) {
					iter2.remove();			//從底層集合中刪除此迭代器返回的最後一個元素(可選操作)
				}
			}
		}
		
		//敵機
		synchronized (enemyPlanes) {
			//敵機
			Iterator<SmallPlane> iter3 = enemyPlanes.iterator();
			while(iter3.hasNext()) {
				SmallPlane next = iter3.next();
				if(next.isOutOfBound() || next.getState() != SmallPlane.ALIVE) {
					iter3.remove();
				}
			}
		}
		
	}	
	
	// 重寫JPanel的繪製方法-->>所有圖片都在該方法上執行
	@Override
	public void paint(Graphics g) {
		
		//繪製背景圖
		g.drawImage(background1, 0, 0, null);
		
		this.paintHeroPlane(g);			//調用方法,繪製飛機
		this.paintHeroBullet(g);		//調用方法繪製飛機子彈
		this.paintScoreAndLife(g);		//繪製飛機的分數與生命
		
		this.paintEnemyPhane(g);		//繪製敵機
		this.paintEnemyBullet(g);		//繪製敵機子彈

	}
	
	/**
	 * 窗口初始化
	 */
	public void init() {
		JFrame jFrame = new JFrame("飛機大戰"); // 設置窗口標題

		jFrame.add(this); // this表示主類,也表示畫板

		jFrame.setSize(WIDTH, HEIGHT); // 設置窗口大小
		jFrame.setLocationRelativeTo(null); // 設置窗口居中
		jFrame.setAlwaysOnTop(true); // 設置窗口總是在頂端
		jFrame.setResizable(false); // 設置不允許拖拉
		jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 關閉窗口時退出程序
		jFrame.setIconImage(new ImageIcon("./images/icon.png").getImage()); // 設置圖標
		jFrame.setVisible(true); // 使窗口顯示出來
				
		initListener();		//安裝程序監聽器	
		initTimer();		//設置定時器,用於實現遊戲中所有的動態邏輯效果
	}
	
	/**
	 * 監控鼠標的監聽器
	 */
	private void initListener() {
		//使用鼠標監聽器的適配器
		MouseAdapter adapter = new MouseAdapter() {
			@Override
			public void mouseMoved(MouseEvent e) {
				//獲取鼠標的x座標與y座標
				System.out.println("x: " + e.getX() + "y: " + e.getY());
				//飛機跟隨鼠標移動
				heroPlane.move(e.getX(), e.getY());
				repaint();  //重新繪製飛機, 底層是重寫執行paint方法
			}
		};		
		//添加鼠標監聽器
		this.addMouseListener(adapter);
		this.addMouseMotionListener(adapter);		
	}
	
	/**
	 * 定時器
	 */
	private long count = 0;	//控制頻率
	public void initTimer(){
		
		Timer timer = new Timer();		//定時器對象
		long delay = 10; 
		long period = 10;
		//定時執行操作
		//參數1:每隔一段時間執行的操作
		//參數2:幾毫米之後執行定時器
		//參數3:每個幾毫米執行一次
		timer.schedule(new TimerTask() {
			@Override
			public void run() {		
			
				//飛機發射子彈
				if(count % 10 == 0 ){
					heroPlaneFire(); 	
				}				
				//英雄子彈移動
				//if(count % 2 == 0){
					heroBulletMove();	
				//}
							
				//敵機登場
				if(count % 50 == 0) {
					enemyPlaneEnter();
				}			
				//敵機移動
				if(count % 2 == 0) {
					enemyPlaneMove();
				}
				//敵機發送子彈
				if(count % 50 == 0) {
					enemyPlaneFire();
				}
				//敵機子彈的移動
				enemyBulletMove();
				
				//檢查是否碰撞了
				checkHit();
				
				//清除碰撞後的對象
				if(count % 10 == 0) {
					clearHitObject();
				}
				
				if(count == Long.MAX_VALUE-1){
					count = 0;
				}
				count++;		
			
				repaint(); //重新繪製頁面
			}
		}, delay,period);
	}

	/*
	 * 程序入口main()
	 */
	public static void main(String[] args) {
		System.out.println("遊戲開始了.....");

		GameStart gameStart = new GameStart();
		gameStart.init(); //顯示主界面
	}
	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章