- 項目:飛機大戰小遊戲之繪製敵我飛機的碰撞檢測
- 項目簡介:飛機大戰小遊戲的角色特徵+行爲
- 開發工具: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(); //顯示主界面
}
}