- 项目:飞机大战小游戏之绘制敌我飞机的碰撞检测
- 项目简介:飞机大战小游戏的角色特征+行为
- 开发工具: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(); //显示主界面
}
}