Java项目-飞机大战


typora-root-url: img

Java项目-飞机大战

1.项目概述

Shoot游戏是一款十分有趣的射击类小游戏,流畅的画面,高难度的挑战。游戏中,玩家驾驶英雄机,在空中进行战斗。点击并移动自己的英雄机,发射炮弹,打掉敌机以及蜜蜂,来获得奖励和分数,打掉一架敌机得5分,打掉一只蜜蜂得1条命或者获得20次双倍火力,如果撞上敌机或者小蜜蜂,将减少命双倍火力清零。每撞到一次蜜蜂或者是敌机命数减1,当命数为0时,则游戏结束。

2.准备工作

2.1.工程创建

首先,新建名为Shoot的java工程;然后在src目录下新建包cn.tedu.shoot;最后将该工程所需的图片拷贝到该包下,工程结构如下:

在这里插入图片描述

2.2.创建父类和接口

2.2.1.创建FlyingObject类

由上图可以分析出英雄机、敌机、子弹以及蜜蜂都属于整个游戏中的飞行物,且都有x,y,width以及height属性,因此,将这些属性抽取到父类FlyingObject中。另外,他们在界面上都以图片形式显示,因此在父类FlyingObject中添加image属性,表示它们的贴图,并提供上述5个属性的getter和setter方法,代码如下:

package cn.tedu.shoot;

import java.awt.image.BufferedImage;

/**
 * 飞行物
 * @author MaYF
 *
 */
public class FlyingObject {
	protected BufferedImage image;//图片
	protected int width;  //宽
	protected int height; //高
	protected int x;      //x坐标
	protected int y;      //y坐标
}

因为以上属性要被子类使用故使用protected修饰。

2.2.2.创建Enemy接口

创建Enemy接口,表示敌人。如果子弹击中敌机,英雄机可以获取分数,因此,在Enemy接口中提供获取分数的方法,代码如下:

package cn.tedu.shoot;
/**
 * 敌人
 * @author MaYF
 *
 */
public interface Enemy {
	/**
	 * 得分
	 * @return
	 */
	public int getScore();
}
2.2.3.创建Award接口

创建接口Award,实现该接口的类表示奖励。如果子弹 击中了蜜蜂,英雄机可以获取奖励。奖励有两种形式,分别是双倍活力或者增命。因此,提供获取奖励类型的方法,代码如下:

package cn.tedu.shoot;

public interface Award {
	/**
	 * 火力值
	 */
	public int DOUBLE_FIRE=0;
	/**
	 * 英雄机命
	 */
	public int LIFE=1;
	/**
	 * 获取奖励类型
	 * @return(0或者1)
	 */
	public int getType();
}

上述代码中,如果奖励类型为0,则表示奖励双倍火力;如果奖励类型为1,则表示奖励1条命。

2.3.创建子类

2.3.1.创建Airplane类

新建类Airplane,表示敌飞机。敌机属于飞行物,因此继承FlyObject类;敌机也属于敌人,因此需要实现Enemy接口。敌机可以向下移动,因此有移动的速度作为属性,代码如下所示:数据私有化,方法公开化;

package cn.tedu.shoot;
/**
 * 敌机,既是飞行物也是敌人
 * @author MaYF
 *
 */
public class Airplane extends FlyingObject implements Enemy {
	/**
	 * 敌机走步的步数
	 */
	private int speend=2;
    /**
	 * 图片数组
	 */
	private BufferedImage[] images;
    /**
	 * 小敌机的切换频率
	 */
	private int index;
	/**
	 * 重写getScore()方法
	 */
	@Override
	public int getScore() {
		return 0;
	}
}
2.3.1.创建BigAirplane类

新建类BigAirplane,表示大敌飞机。大敌机属于飞行物,因此继承FlyObject类;大敌机也属于敌人,因此需要实现Enemy接口。大敌机可以向下移动,因此有移动的速度作为属性,代码如下所示:数据私有化,方法公开化;

package cn.tedu.shoot;
/**
 * 敌机,既是飞行物也是敌人
 * @author MaYF
 *
 */
public class BigAirplane extends FlyingObject implements Enemy {
	/**
	 * 敌机走步的步数
	 */
	private int speend=2;
    /**
	 * 图片数组
	 */
	private BufferedImage[] images;
    /**
	 * 大敌机的切换频率
	 */
	private int index;
	/**
	 * 重写getScore()方法
	 */
	@Override
	public int getScore() {
		return 0;
	}
}
2.3.2.创建Bee类

蜜蜂属于飞行物,因此继承自FlyingObject类;击中蜜蜂可以获得奖励,因此,需要实现Award接口,并且有奖励类型作为属性。蜜蜂可以左右移动、向下移动,因此有移动速度作为属性,代码如下:

package cn.tedu.shoot;

public class Bee extends FlyingObject implements Award {
	/**
	 * x坐标走步步数
	 */
	private int xspeed=1;
	/**
	 * y坐标走步步数
	 */
	private int yspeed=2;
	/**
	 * 奖励类型(0或)1
	 */
	private int awardType;
    /**
	 * 图片数组
	 */
	private BufferedImage[] images;
    /**
	 * 小蜜蜂的切换频率
	 */
	private int index;
	/**
	 * 重写getType()
	 * @return 返回奖励类型
	 */
	@Override
	public int getType() {
		return awardType;
	}
}
2.3.3.创建Bullet类

子弹属于飞行物,因此,继承自FlyingObject类;子弹可以向上移动,因此有移动的速度作为属性,代码如下:

package cn.tedu.shoot;
/**
 * 子弹是飞行物
 * @author MaYF
 *
 */
public class Bullet extends FlyingObject {
	/**
	 * 子弹走步的步数
	 */
	private int speed=3;
}
2.3.4.创建Hero类

英雄机属于飞行物,因此继承自FlyingObject类;英雄机发出子弹,击中蜜蜂可以获取双倍活力或者增命。因此,将双倍火力的子弹数量和命的数量作为该类的属性,代码如下:

package cn.tedu.shoot;

import java.awt.image.BufferedImage;
/**
 * 英雄机是飞行物
 * @author MaYF
 *
 */
public class Hero extends FlyingObject {
	/**
	 * 命
	 */
	private int life;
	/**
	 * 火力值
	 */
	private int doubleFire;
	/**
	 * 图片数组
	 */
	private BufferedImage[] images;
	/**
	 * 控制切换的频率
	 */
	private int index;
}

上述代码中,还有images属性和index属性,其中images属性表示Hero的贴图,Hero的贴图由两张图片组成,因此使用数组类型;index属性是控制两张图片进行交替显示的频率值;

2.4.创建ShootGame主类

新建类ShootGame,该类继承自JPanel,在该类中,使用静态常量定义面板的宽和高,并使用ImageIO的read方法加载图片,代码如下:

package cn.tedu.shoot;

import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
/**
 * 主程序类
 * @author MaYF
 *
 */
public class ShootGame{
	public static final int WIDTH = 400; //窗口宽
	public static final int HEIGHT = 654; //窗口高
	
	public static BufferedImage background;//背景图
	public static BufferedImage start;//启动图
	public static BufferedImage pause;//暂停图
	public static BufferedImage gameover;//游戏结束图
	public static BufferedImage bullet;//子弹
	public static BufferedImage airplane0;//敌机0
	public static BufferedImage airplane1;//敌机1
	public static BufferedImage bigairplane0;//大敌机0
	public static BufferedImage bigairplane1;//大敌机1
	public static BufferedImage bee0;//蜜蜂0
	public static BufferedImage bee1;//蜜蜂1
	public static BufferedImage hero0;//英雄机0
	public static BufferedImage hero1;//英雄机1
	public static BufferedImage bom1;//爆炸效果1
	public static BufferedImage bom2;//爆炸效果2
	public static BufferedImage bom3;//爆炸效果3
	public static BufferedImage bom4;//爆炸效果4
	static {//初始化静态资源(加载图片)
		try {
			//读取图片到静态图片变量中
			background = ImageIO.read(ShootGame.class.getResource("background.png"));
			bigairplane0 = ImageIO.read(ShootGame.class.getResource("bigairplane0.png"));
			bigairplane1 = ImageIO.read(ShootGame.class.getResource("bigairplane1.png"));
			airplane0 = ImageIO.read(ShootGame.class.getResource("airplane0.png"));
			airplane1 = ImageIO.read(ShootGame.class.getResource("airplane1.png"));
			bee0 = ImageIO.read(ShootGame.class.getResource("bee0.png"));
			bee1 = ImageIO.read(ShootGame.class.getResource("bee1.png"));
			bullet = ImageIO.read(ShootGame.class.getResource("bullet.png"));
			hero0 = ImageIO.read(ShootGame.class.getResource("hero0.png"));
			hero1 = ImageIO.read(ShootGame.class.getResource("hero1.png"));
			bom1 = ImageIO.read(ShootGame.class.getResource("bom1.png"));
			bom2 = ImageIO.read(ShootGame.class.getResource("bom2.png"));
			bom3 = ImageIO.read(ShootGame.class.getResource("bom3.png"));
			bom4 = ImageIO.read(ShootGame.class.getResource("bom4.png"));
			pause = ImageIO.read(ShootGame.class.getResource("pause.png"));
			gameover = ImageIO.read(ShootGame.class.getResource("gameover.png"));
			start = ImageIO.read(ShootGame.class.getResource("start.png"));	
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		
	}
}

3.初始化属性

3.1.Airplane类添加构造

给Airplane添加构造初始化属性

在这里插入图片描述

    /**
	 * 构造方法
	 */
	public Airplane() {
		image=ShootGame.airplane0;//敌机图片
		width=image.getWidth();//敌机宽
		height=image.getHeight();//敌机高
		Random rand=new Random();//随机数对象
		//x的范围为0到窗口宽-敌机宽之间的随机数
		x=rand.nextInt(ShootGame.WIDTH-this.width);
		//y=-this.height;//y为负的敌机的高
		y=200;
        	//小敌机图片数组赋值
		images=new BufferedImage[]{
				ShootGame.airplane0,ShootGame.airplane1};
        index=0;
	}

3.2.BigAirplane类添加构造

/**
	 * 构造方法
	 */
	public BigAirplane() {
		image=ShootGame.bigairplane0;//敌机图片
		width=image.getWidth();//敌机宽
		height=image.getHeight();//敌机高
		Random rand=new Random();//随机数对象
		//x的范围为0到窗口宽-敌机宽之间的随机数
		x=rand.nextInt(ShootGame.WIDTH-this.width);
		//y=-this.height;//y为负的敌机的高
		y=200;
        images = new BufferedImage[]{ShootGame.bigairplane0, ShootGame.bigairplane1};
		index=0;
	}

3.3.Bee类添加构造

给Bee添加构造初始化属性

在这里插入图片描述

    /**
	 * 构造方法
	 */
	public Bee() {
		image=ShootGame.bee0;//蜜蜂图片
		width=image.getWidth();//宽
		height=image.getHeight();//高
		Random rand=new Random();//随机数
		//x为0到屏幕宽-蜜蜂之间的随机数
		x=rand.nextInt(ShootGame.WIDTH-this.width);
		//y=-this.height;//y为负的蜜蜂的高
		y=200;
		//奖励类型为0到1之间的随机数
		awardType=rand.nextInt(2);
        //蜜蜂图片数组赋值
		images=new BufferedImage[]{ShootGame.bee0,ShootGame.bee1};
		index=0;
	}

3.4.Bullet类添加构造

给Bullet添加构造初始化属性

/**
	 * 构造方法
	 * @param x 子弹的x坐标
	 * @param y 子弹的y坐标
	 */
	public Bullet(int x,int y) {
		image=ShootGame.bullet;//图片
		width=image.getWidth();//子弹宽
		height=image.getHeight();//子弹高
		this.x=x;//x坐标随着英雄机动
		this.y=y;//y坐标随着英雄机动
	}

3.5.Hero类添加构造

给Hero添加构造初始化属性

在这里插入图片描述

    /**
	 * 构造方法
	 */
	public Hero() {
		image = ShootGame.hero0;//英雄机图片
		width = image.getWidth();//英雄机宽
		height = image.getHeight();//英雄机高
		x = 150;//x坐标:固定
		y = 400;//y坐标:固定
		life = 3;//命数为3
		doubleFire = 0;//火力值为0,代表单倍火力
		//图片数组hero0和hero1
		images = new BufferedImage[]{ShootGame.hero0, ShootGame.hero1};
		index=0;//控制切换的频率	
	}

4.编写main方法

在该方法中设置窗口的大小,居中、点击敞口的右上角“X”关闭窗口以及设置窗口可见,代码如下:

public class ShootGame extends JPanel{}
public static void main(String[] args) {
		JFrame frame = new JFrame("飞机大战");//创建窗口对象
		ShootGame game = new ShootGame(); //创建面板对象
		frame.add(game); //将面板添加到窗口中
		frame.setSize(WIDTH, HEIGHT); //设置窗口的大小
		frame.setAlwaysOnTop(true); //设置其总在最上
		//设置窗口默认的关闭操作(关闭窗口则退出程序)
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
		//frame.setIconImage(new ImageIcon("images/icon.jpg").getImage()); // 设置窗体的图标
		frame.setLocationRelativeTo(null); //设置窗体初始位置(居中)
		frame.setVisible(true); //设置窗口状态为显示
	}

5.绘制界面

在ShootGame中编写如下代码:

private Hero hero = new Hero(); //英雄机
	private FlyingObject[] flyings = {}; //敌人(敌机+小蜜蜂)
	private Bullet[] bullets = {}; //子弹数组
	//private Ember[] embers = {}; // 灰烬
	
	public ShootGame() {
		flyings=new FlyingObject[3];//3个飞行物
		flyings[0]=new Airplane();//小敌机
        flyings[1]=new BigAirplane();//大敌机
		flyings[2]=new Bee();//蜜蜂
		bullets=new Bullet[1];//1发子弹
		bullets[0]=new Bullet(50, 100);
	}

编写绘画方法

/** 画英雄机 */
	private  void paintHero(Graphics g) {
		g.drawImage(hero.image, hero.x, hero.y, null);//画英雄机
	}
	/** 画敌人对象(敌人+小蜜蜂) */
	private  void paintFlyingObjects(Graphics g) {
		for (int i = 0; i < flyings.length; i++) {//遍历所有敌人
			FlyingObject f=flyings[i];//获取每一个敌人
			g.drawImage(f.image, f.x, f.y, null);//画敌人对象
		}
	}
	/** 画子弹对象 */
	private  void paintBullets(Graphics g) {
		for (int i = 0; i < bullets.length; i++) {//遍历所有子弹
			Bullet b=bullets[i];//获取每一个子弹
			g.drawImage(b.image, b.x, b.y, null);//画子弹对象
		}
	}

修改Bee和Airplane中坐标

//y=-this.height;//y为负的蜜蜂的高
y=200;
//y=-this.height;//y为负的敌机的高
y=200;

重写paint

@Override
	public void paint(Graphics g) {
		g.drawImage(background, 0, 0, null);//画背景图
		paintHero(g);//画英雄机
		paintFlyingObject(g);//画敌人(敌人+小蜜蜂)
		paintBullets(g);//画子弹
	}

运行ShootGame效果图如图所示:

在这里插入图片描述

6.实现敌人(敌机+蜜蜂)入场

6.1.准备工作

打开ShootGame将构造方法删除;

还原Airplane、BigAirplane和Bee的y坐标;

y=-this.height;//y为负的敌机的高
//y=200;
y=-this.height;//y为负的大敌机的高
//y=200;
y=-this.height;//y为负的蜜蜂的高
//y=200;

6.2.分析

/** 画敌人对象(敌人+小蜜蜂) */
	private  void paintFlyingObjects(Graphics g) {
		for (int i = 0; i < flyings.length; i++) {//遍历所有敌人
			FlyingObject f=flyings[i];//获取每一个敌人
			g.drawImage(f.image, f.x, f.y, null);//画敌人对象
		}
	}
	/** 画子弹对象 */
	private  void paintBullets(Graphics g) {
		for (int i = 0; i < bullets.length; i++) {//遍历所有子弹
			Bullet b=bullets[i];//获取每一个子弹
			g.drawImage(b.image, b.x, b.y, null);//画子弹对象
		}
	}

已上代码分析,入场时在界面能画出来,f代表数组中的一个对象,画flyings数组;此时只能画英雄机,因为flyings和bullets数组的长度为0;因此分析如下:

敌人(蜜蜂+敌机)入场

1.创建敌人对象–随机

2.把敌人对象添加到flyings数组中:实现只要运行程序什么都不做,敌机和蜜蜂仍然出现在页面;要实现此功能要使用定时器;

6.4.创建敌人对象

6.4.1.添加创建敌人方法

创建敌人对象过程中是随机的,有可能是小敌机、大敌机、也有可能是小蜜蜂;

工具方法:

Math.sqrt(25);
Math.random();

工厂方法都是生产对象的;

此处采用向上造型;

找到ShootGame类,在paint方法上面添加如下代码:

若方法操作仅仅与参数相关而与对象无关时用静态方法,方法分为工具方法和工厂方法,工厂方法返回值类型为对象。

/**
	 * 创建敌人对象(小敌机、大敌机、小蜜蜂)
	 * @return
	 */
	public static FlyingObject nextOne() {
		Random rand=new Random();//随机数对象
		int type=rand.nextInt(20);//随机生成0-19之间的随机数
		if(type==0) {//随机数为0则返回蜜蜂对象
			return new Bee();
		}else if(type<10) {//随机数为1-9则返回小敌机对象
			return new Airplane();
		}else {//随机数为10-19则返回大敌机对象
			return new BigAirplane();
		}
	}
6.4.2.添加启动程序方法

已上敌人对象添加到flyings数组中,但是此时程序不认识nextOne方法,只认识main。

1.在paint方法上面位置写action方法:

	/**
	 * 启动程序的方法
	 */
	public void action() {
		
	}

2.在main中添加如下调用代码:

game.action();
6.4.3.定时器

此时如果不需要干什么,就能使敌机小蜜蜂依然会出现在界面上时,需要用到定时器;

定时器:

1.事件间隔。

2.定时干事:此时需要定时做的事有敌人入场、子弹入场和走步。

第一个参数需要TimerTask类型

Timer.schedule(new TimerTask(),10,10);

这样写肯定错误,TimerTask是抽象类,不能new一个对象;抽象类不能实例化;但是可以造型,将TimerTask当做父类,传入一个子类;创建一个子类给子类new一个对象

Timer.schedule(new Aoo(),10,10);

class Aoo extends TimerTask{

}

new Aoo()只需要创建一次,此时采用匿名内部类;

测试定时器:代码如下

private Timer timer;//定时器
	private int intervel=10;//时间间隔(毫秒)
	/**
	 * 启动程序的方法
	 */
	public void action() {
		timer=new Timer();//创建定时器对象
		timer.schedule(new TimerTask() {//定时器任务
			//run方法是定时器任务要执行的操作,只要到时间就自动调用run()
			@Override
			public void run() {
				System.out.println(111);
			}
		}, intervel, intervel);
	}

解释:

第一个10:从程序启动到第一次定时触发间隔。

第二个10:第一次定时到第二次定时,第二次定时到第三次定时。

此时控制台自动输出111

6.4.4.添加敌人入场方法

定时需要敌人入场;因此在run中写enterAction方法,专门实现敌人入场。

1.在nextOne方法下面写enterAction方法;

int flyEnteredIndex=0;//敌人入场计数
	/**
	 * 敌人(大小敌机+小蜜蜂)入场
	 */
	public void enterAction() {//10毫秒走一次
		flyEnteredIndex++;//10毫秒增1
		//40次才成立一次,相当于400(40*10)毫秒
		if(flyEnteredIndex%40==0) {
			//40,80,120,160,200
			FlyingObject obj=nextOne();//获取敌人对象
		}
	}

10毫秒走一次,意味着1秒中创建100个对象,有100个敌机,因此要加以控制;每400毫秒走一次,一秒钟两个半敌机

2.在action方法的定时器任务中调用enterAction方法;

	//run方法是定时器任务要执行的操作,只要到时间就自动调用run()
			@Override
			public void run() {
				enterAction();//调用敌人入场方法
			}

6.5.将敌人对象添加到flyings数组中

	private Hero hero = new Hero(); //英雄机
	private FlyingObject[] flyings = {}; //敌人(敌机+小蜜蜂)
	private Bullet[] bullets = {}; //子弹数组

由于此时flying数组目前为空没有元素;要装元素就要扩容,数组的长度在创建后是不可改变的,所谓扩容是指创建一个更大的数组并将原有数组的内容复制到其中。

在这里插入图片描述

int flyEnteredIndex=0;//敌人入场计数
	/**
	 * 敌人(大小敌机+小蜜蜂)入场
	 */
	public void enterAction() {//10毫秒走一次
		flyEnteredIndex++;//10毫秒增1
		//40次才成立一次,相当于400(40*10)毫秒
		if(flyEnteredIndex%40==0) {
			//40,80,120,160,200
			FlyingObject obj=nextOne();//获取敌人对象
			flyings=Arrays.copyOf(flyings, flyings.length+1);//扩容
			//将敌人对象添加到flyings数组的最后一个元素位置
			flyings[flyings.length-1]=obj;
		}
	}

目前敌人已经装入;此时看不出效果,要想看到效果,修改xy坐标并重画。

6.6.重画

paint()被调用2种方式:

1…setVisible(true);

2.repaint();

//run方法是定时器任务要执行的操作,只要到时间就自动调用run()
			@Override
			public void run() {
				enterAction();//调用敌人入场方法
				repaint();//重绘,调用paint方法
			}

修改Airplane、BigAirplane和Bee的y坐标;

//y=-this.height;//y为负的敌机的高
y=200;
//y=-this.height;//y为负的大敌机的高
y=100;
//y=-this.height;//y为负的蜜蜂的高
y=300;

执行得到如下效果:

在这里插入图片描述

7.飞行物走步

修改回Airplane、BigAirplane和Bee原来坐标

7.1.添加飞行物走步方法

飞行物走步是定时干的事;因此写到定时器中;调用stepAction()方法;

	//run方法是定时器任务要执行的操作,只要到时间就自动调用run()
			@Override
			public void run() {
				enterAction();//调用敌人入场方法
				stepAction();//调用飞行物走步方法
				repaint();//重绘,调用paint方法
			}

在enterAction下面定义stepAction方法:飞行物走步不需要控制了,让10ms走一次比较连续;

    /**
	 * 飞行物走步
	 */
	public void stepAction() {//10毫秒走一次
		
	}

7.2.添加step方法

此时,敌机能走步,蜜蜂能走步,蜜蜂子弹和英雄机(两个图片在切换)能走步:所有飞行物都有走步行为,并且必须有。

共有行为放在FlyingObject父类中,并将step方法和FlyingObject类都设置为abstract抽象类,当继承抽象类必须重写抽象方法。

/**
 * 飞行物
 * @author MaYF
 *
 */
public abstract class FlyingObject {
	protected BufferedImage image;//图片
	protected int width;  //宽
	protected int height; //高
	protected int x;      //x坐标
	protected int y;      //y坐标
	public abstract void step();//方法抽象,类也必须抽象
}

只定义方法没有方法体;

此时子类报错;要重写抽象方法;
在这里插入图片描述

7.2.1.关于小敌机

小敌机从上向下移动,所以y坐标向下移动speed步数。

    /**
	 * 重写step()方法
	 */
	@Override
	public void step() {
        image=images[index++/10%images.length];
		y+=speed;
	}
7.2.2.关于大敌机

大敌机从上向下移动,所以y坐标向下移动speed步数。

    /**
	 * 重写step()方法
	 */
	@Override
	public void step() {
        image=images[index++/10%images.length];
		y+=speed;
	}
7.2.3.关于子弹

子弹从下向上移动,所以y坐标向下移动speed步数。

    /**
	 * 重写step()方法
	 */
	@Override
	public void step() {
		y-=speed;
	}
7.2.4.对于蜜蜂

因为蜜蜂是斜下移动,所以x坐标向右y坐标向下移动相应步数。

/**
	 * 重写step()方法
	 */
	@Override
	public void step() {
        image=images[index++/10%images.length];
		x+=xspeed;
		y+=yspeed;
		if(x>ShootGame.WIDTH-this.width) {
			xspeed-=1;
		}
		if(x<0) {
			xspeed=1;
		}
	}
7.2.5.关于英雄机

此时英雄机的两个图片在切换,代码如下:

/**
	 * 重写step()方法
	 */
	@Override
	public void step() {
		image=images[index++/10%images.length];
	}

// index++;

// int a = index/10;

// int b = a%2;

// image = images[b];

​ image = images[index++/10%images.length];//英雄机每100ms切换一次

​ /*

​ * 10MS index = 1 a = 1/10=0 b = 0; image = image[0]

​ * 20MS index = 2 a = 0; b = 0;

​ * 30MS index = 3 a = 0; b = 0;

​ * ……

​ * 100MS index = 10 a = 1; b = 1;image = image[1]

​ * 110MS image = image[1]

​ * ……

​ * 200MS image = image[0]

​ */

7.3.调用step

属于定时发生,在ShootGame中stepAction方法中调用英雄机走步方法,并调用敌人(大小敌机)和蜜蜂的走步方法,子弹的走步方法。

/**
	 * 飞行物走步
	 */
	public void stepAction() {//10毫秒走一次
		hero.step();
		for (int i = 0; i < flyings.length; i++) {//遍历所有飞行物
			flyings[i].step();
		}
		for (int i = 0; i < bullets.length; i++) {//遍历所有子弹
			bullets[i].step();
		}
	}

此处运行效果如下;

在这里插入图片描述

8.子弹入场

8.1.分析

子弹入场:

子弹是英雄机发射出来的,定时发生的。

1.创建子弹对象

2.将子弹对象添加到bullets数组中

8.2.添加入场方法

因为发射子弹是定时发生的,所以在action中添加子弹发射方法shootAction。

shootAction();//调用子弹入场方法

8.3.定义入场方法

在ShootGame类中添加入场方法,如下

/**
	 * 子弹入场方法
	 */
public void shootAction(){//10毫秒走一次

}

8.4.创建子弹对象

因为发射子弹是英雄机的行为,所以在Hero中添加如下代码:

    /**
	 * 英雄机发射子弹
	 */
	public Bullet[] shoot(){
	
	}

注意:英雄机发射子弹其实就是创建子弹对象,发射子弹有可能单发也有可能双发。所以返回类型为Bullet[]数组。

8.4.1.判断火力值

根据火力值判断单发还是双发。

/**
	 * 英雄机发射子弹
	 */
	public Bullet[] shoot(){
	if(doubleFire>0){//双倍火力
		Bullet[] bullets=new Bullet[2];//创建子弹数组,存储两个子弹对象
		return bullets;
	}else{//单倍火力
		Bullet[] bullets=new Bullet[1];//创建子弹数组,存储一个子弹对象
		return bullets;
	}
	}
8.4.2.传参创建子弹对象

传参:

New 子弹对象要传参,Bullet类构造中有x和y参数

单倍火力在中心发;双倍火力在一半的一半;将英雄机的宽分为4分;单倍火力在1/2位置;

双倍火力在1/4和3/4位置;下来就是得到这些点;

单倍火力:

子弹的x是英雄机的x+1/2英雄机的宽;

双倍火力:

子弹的x是英雄的x+1/4英雄机宽

子弹的x是英雄的x+3/4英雄机宽

Y是英雄机的y-20像素;

/**
	 * 英雄机发射子弹
	 */
	public Bullet[] shoot(){
		int xStep=this.width/4;//英雄机的四分之一
	if(doubleFire>0){//双倍火力
		Bullet[] bullets=new Bullet[2];//创建子弹数组,存储两个子弹对象
		//子弹x坐标:英雄机的x+1/4英雄机宽
		//子弹y坐标:英雄机的y-20
		bullets[0]=new Bullet(this.x+xStep, this.y-10);
		//子弹x坐标:英雄机的x+3/4英雄机宽
		//子弹y坐标:英雄机的y-20
		bullets[1]=new Bullet(this.x+xStep*3, this.y-10);
		doubleFire-=2;//双倍火力二十次
		return bullets;
	}else{//单倍火力
		Bullet[] bullets=new Bullet[1];//创建子弹数组,存储一个子弹对象
		//子弹x坐标:英雄机的x+1/2英雄机宽
		//子弹y坐标:英雄机的y-20
		bullets[0]=new Bullet(this.x+xStep*2,this.y-10);
		return bullets;
	}
	}

8.5.编写入场方法

回到ShootGame类的shootAction()方法中,调用shoot()方法将子弹对象扩容添加到bullets数组中,并控制子弹的创建为300ms一次。

/**
	 * 子弹入场方法
	 */
	int shootIndex=0;//子弹入场计数
	public void shootAction(){//10毫秒走一次
		shootIndex++;
		if(shootIndex%30==0){//0 30 60 90 120 150
			Bullet[] bs=hero.shoot();//英雄机发射子弹
			//扩容bullets数组
			bullets=Arrays.copyOf(bullets, bullets.length+bs.length);
            //将子弹数组bs中的子弹添加到bullets数组中
			System.arraycopy(bs, 0, bullets, bullets.length-bs.length, bs.length);
		}	
	}

此时是单发,在Hero中将doubleFire改成40就变成双倍火力,由于没发一次减2所以一会就会变成单发。

在这里插入图片描述

9.控制英雄机

9.1.分析

**事件:**发生了一个事

**事件处理:**发生这个事之后所做的响应

事件:

鼠标点击------启动状态变为运行状态

鼠标移动------英雄机随着动

鼠标移开------运行状态变为暂停状态

鼠标移入------暂停状态变为运行状态

如何将事件和事件处理联系在一起,采用监听器

采用监听器需如下两步:

1.创建监听器对象

2.安装监听器

谁在操作-----鼠标在操作

这些事件都是在对谁操作-----面板

因此要将监听器安装在面板上;

//创建监听器对象

MouseListener l = new MouseListener(){

 重写mouseClicked

 重写mousePressed

 重写mouseReleased

 重写mouseEntered

 重写mouseExited

}

MouseMotionListener ml = new MouseMotionListener(){

 重写mouseDragged

 重写mouseMoved

}//将侦听器安装到面板上

this.addMouseListener(l);

this.addMouseMotionListener(ml);

我们采用

MouseAdpater是MouseListener,MouseMotionListener的孩子

l是MouseAdpter的子类的对象

l是Listener的孩子的孩子

MouseAdapter implements MouseListener,MouseMotionListener{

}

addMouseListener(MouseListener l){

}

addMouseMotionListener(MouseMotionListener l){

}

9.2.英雄机随鼠标动

在ShootGame中添加如下程序:

public void action(){
		//创建监听器对象
		MouseAdapter l=new MouseAdapter(){
			
			};
    //在面板上安装监听器
		this.addMouseListener(l);
		this.addMouseMotionListener(l);
}

9.2.1.重写方法

让英雄机随着鼠标动,需要重写mouseMoved鼠标移动方法

public void action(){
		//创建监听器对象
		MouseAdapter l=new MouseAdapter(){
			@Override
			public void mouseMoved(MouseEvent e) {//鼠标移动事件
			
				}
			};
}

9.2.2.获得鼠标的坐标
public void mouseMoved(MouseEvent e) {//鼠标移动事件
				 int x=e.getX();//鼠标的x坐标
				 int y=e.getY();//鼠标的y坐标
				}
9.2.3.添加moveTo方法

设置英雄机改变位置,这个移动是英雄机的行为,找到Hero类添加

在这里插入图片描述

英雄机的x=鼠标的x-1/2英雄机的宽

英雄机的y=鼠标的y-1/2英雄机的高

/**
	 * 英雄机移动
	 * @param x 鼠标的x坐标
	 * @param y 鼠标的y坐标
	 */
	public void moveTo(int x,int y){
		this.x=x-this.width/2;
		this.y=y-this.height/2;
	}
9.2.4.调用moveTo方法
@Override
			public void mouseMoved(MouseEvent e) {//鼠标移动事件
				 int x=e.getX();//鼠标的x坐标
				 int y=e.getY();//鼠标的y坐标
				 hero.moveTo(x, y);//调用英雄机移动方法
			}

10.子弹与敌机的碰撞

10.1.分析

子弹撞到敌人,敌人消失,子弹不消失,分析撞的行为属于哪个对象?

在这里插入图片描述

10.2.定义碰撞方法

由图可以看出,当子弹的x坐标在蜜蜂的x与x+width之间,并且子弹的y坐标在蜜蜂的y与y+height之间时,子弹击中敌人;

回到FlyingObject中编写如下程序:

/**
	 * 飞行物(大小敌机、蜜蜂)被子弹碰撞的方法
	 * @param bullet 子弹
	 * @return 是否被碰撞(是 true 否 false)
	 */
	public boolean shootBy(Bullet bullet){
		int x1=this.x;//代表被碰撞飞行物的x坐标
		int x2=this.x+this.width;//飞行物的x+飞行物的宽
		int y1=this.y;//代表被碰撞飞行物的y坐标
		int y2=this.y+this.height;//飞行物的y+飞行物的高
		int x=bullet.x;//子弹的x坐标
		int y=bullet.y;//子弹的y坐标
		//返回x在x1与x2之间,并且y在y1与y2之间的判断结果。
		return x>x1&&x<x2&&y>y1&&y<y2;
	}

子弹与敌人撞上之后

1.英雄机得分或者得奖励

  • ​ 若得分则加分

  • ​ 若得奖励,判断奖励类型是命还是火力值

    若为火力则增火力

    若为命则增命

2.敌人消失

10.3.编写奖励方法

如果被子弹打中,奖励命或者火力值;回到Hero中编写如下两个方法。

/**
	 * 英雄机加命
	 */
	public void addLife(){
		life++;
	}
/**
	 * 英雄机增加火力值
	 */
	public void addDoubleFire(){
		doubleFire+=40;
	}

10.4.检测子弹与敌人撞

10.4.1.调用碰撞方法

检查敌人和子弹撞、子弹和敌人撞也是定时触发的,因为什么不动,子弹撞该得分得分,该增命增命;

找到Action编写子弹与敌人撞方法bangAction;所有子弹和所有敌人一起撞因此要遍历子弹敌人;

@Override
			public void run() {
					enterAction();//调用飞行物入场方法
					stepAction();//调用飞行物走步方法
					shootAction();//调用子弹入场方法
					bangAction();//调用子弹与飞行物碰撞的方法
              }
10.4.2.定义bangAction方法

在action外写bangAction方法

/**
	 * 检测所有子弹与所有飞行物(大小敌机、小蜜蜂)碰撞
	 */
    int score=0;//总分数
	public void bangAction(){
        
    }
10.4.3.定义bang方法

子弹与敌人的碰撞

1.判断子弹与额敌人是否撞上了;

2.英雄机得分或者得奖励

3.敌人消失

第一步:

所有敌人和一个子弹撞,需要遍历飞行物数组。

第二步:

获取到被撞敌人后得分或者得火力值,此时要判断被撞对象是什么东西;

第三步:

将被撞的敌人对象消失——从flying数组中删除

1.消失意味着在页面上不再画出来了

2.画的是flyings数组中的对象

3.flyings数组中有的就能画出来

flyings数组中没有的就画不出来

4.消失意味着在flyings中不存在了

5.将被撞的敌人对象从flyings中删除

缩容保证最后的元素是被撞对象即可,将被撞对象和最后一个元素交换

/**
	 * 检测一个子弹与所有飞行物(大小敌机、小蜜蜂)的碰撞
	 * @param b 被撞子弹
	 * @param j 被撞子弹坐标
	 */
	public void bang(Bullet b,int j){
		int index=-1;//记录被撞敌人的下标,不能为0
		for (int i = 0; i < flyings.length; i++) {//遍历所有敌人
			FlyingObject f=flyings[i];//得到每一个敌人
			if(f.shootBy(b)){//判断是否发生碰撞
				index=i;//记录被撞敌人的下标
				break;//其余的敌人不再做比较
			}
		}
		if(index!=-1){//有被撞的敌人
			FlyingObject one=flyings[index];//获取被撞的对象
			if(one instanceof Enemy){//判断被撞对象是否是敌人
			  Enemy e=(Enemy)one;//强制转换
			  score+=e.getScore();//将敌人的分数得到并加到总分数上
			}
			if(one instanceof Award){//判断被撞对象是否是奖励
				Award a=(Award)one;//强转
				int type=a.getType();//获取奖励类型
				switch(type){//判断是双倍火力还是加命
				 case Award.DOUBLE_FIRE:
					 hero.addDoubleFire();//增加火力值
					 break;
				 case Award.LIFE:
					 hero.addLife();//增加命
					 break;
				}
			}
			//让被撞敌人消失
			FlyingObject f=flyings[index];//取出被撞敌人
			//将数组最后一个敌人添加到被撞敌人的位置
			flyings[index]=flyings[flyings.length-1];
			//将被撞敌人添加到数组的最后一个位置等待数组缩容
			flyings[flyings.length-1]=f;
			//缩容 会删除数组的最后一个元素
			flyings=Arrays.copyOf(flyings, 
					flyings.length-1);
            //让被撞子弹消失
			//将数组最后一个子弹添加到被撞子弹的位置
			bullets[j]=bullets[bullets.length-1];
			//将被撞子弹添加到数组的最后一个位置等待数组缩容
			bullets[bullets.length-1]=b;
			//缩容 会删除数组的最后一个元素
			bullets=Arrays.copyOf(bullets, 
					bullets.length-1);
		}
	}
10.4.4.编写bangAction()方法

所有敌人和所有子弹撞,需要遍历子弹数组。

/**
	 * 检测所有子弹与所有飞行物(大小敌机、小蜜蜂)碰撞
	 */
	int score=0;//分数
	public void bangAction(){
		//取出所有子弹
		for (int i=0;i<bullets.length;i++) {
			//将子弹数组中的所有子弹都进行检测
			bang(bullets[i],i);
		}
	}

11.画分画命

11.1.添加方法

得到score就行,命是英雄机的命

找到Hero添加getLife方法

/**
	 * 获取英雄机的命
	 * @return
	 */
	public int getLife() {
		return life;
	}

找到Airplane和BigAirplane添加getScore方法

/**
	 * 重写后的getScore()
	 */
	@Override
	public int getScore() {
		return 5;//打中小敌机得5分
	}
/**
	 * 重写后的getScore()
	 */
	@Override
	public int getScore() {
		return 10;//打中大敌机得10分
	}

11.2.添加绘制方法

/**
	 * 画分画命
	 */
	private void paintScore(Graphics g) {
		g.setColor(Color.WHITE);
		//设置样式: SANS_SERIF  BOLD加粗     22字体大小
		g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,22));
		g.drawString("SCORE:"+score, 10, 25);//画分
		g.drawString("LIFE:"+hero.getLife(), 10, 45);//画命
	}

11.3.调用绘制方法

/**
	 * 绘制方法
	 */
	@Override
	public void paint(Graphics g) {
		paintHero(g);//画英雄机
		paintFlyingObjects(g);//画飞行物
		paintBullets(g);//画子弹
		paintScore(g);//调用画分画命
    }

12.删除越界飞行物

12.1.分析

越界是所有对象均有的,放到父类FlyingObject;

12.2.定义越界方法

/**
	 * 检测飞行物是否越界
	 * @return
	 */
	public abstract  boolean outOfBounds();

12.3.重写越界方法

对于大小敌机和蜜蜂

/**
	 * 重写越界方法outOfBounds()
	 */
	@Override
	public boolean outOfBounds() {
		return this.y>ShootGame.HEIGHT;//小敌机的y坐标大于窗口的高
	}

对于子弹

/**
	 * 重写越界方法
	 */
	@Override
	public boolean outOfBounds() {
		return this.y<-this.height;
	}

对于英雄机

英雄机永不越界

/**
	 * 英雄机不越界
	 */
	@Override
	public boolean outOfBounds() {
		return false;
	}

12.4.定义删除越界飞行物方法

找到ShootGame类定义outOfBoundsAction()方法

/**
	 * 删除越界飞行物
	 */
	public void outOfBoundsAction() {
		//不越界数组的下标和不越界飞行物的数量
		int index=0;
		//创建一个新飞行物数组用来存储不越界的飞行物
		FlyingObject[] flyingLives=new FlyingObject[flyings.length];
		for (int i = 0; i < flyings.length; i++) {//遍历所有飞行物
			FlyingObject f=flyings[i];//获取每一个飞行物
			if(!f.outOfBounds()) {//不越界的飞行物
				flyingLives[index]=f;//将不越界的飞行物添加到新数组中
				index++;//下标加一,不越界飞行物数量加一
			}
		}
		//将不越界数组中的飞行物全部复制给flyings数组
		flyings=Arrays.copyOf(flyingLives, index);
		//删除越界子弹
        //将下标和数量归零
		index=0;
		//创建一个新的子弹数组存储不越界的子弹
		Bullet[] bulletsLives=new Bullet[bullets.length];
		for (int i = 0; i < bullets.length; i++) {//遍历所有子弹
			Bullet b=bullets[i];//获取每一个子弹
			if(!b.outOfBounds()) {//不越界的子弹
				bulletsLives[index]=b;//将还不越界的子弹添加到新数组中
				index++;//下标加一,不越界飞行物数量加一
			}
		}
		//将不越界的数组中子弹全部复制给bullets数组
		bullets=Arrays.copyOf(bulletsLives, index);
	}

12.5.调用删除方法

检查越界后删掉,属于定时发生的;因此写到action中的run中调用;

public void run() {
					enterAction();//调用飞行物入场方法
					stepAction();//调用飞行物走步方法
					shootAction();//调用子弹入场方法
					bangAction();//调用子弹与飞行物碰撞的方法
					outOfBoundsAction();//调用删除越界飞行物的方法
}

13.检查英雄机与敌人的碰撞

13.1.分析

13.2.编写碰撞方法

在Hero中编写hit方法:

/**
	 * 英雄机和飞行物碰撞的方法
	 * @param other 飞行物(大小敌机、小蜜蜂)
	 * @return true 碰撞   false未碰撞
	 */
	public boolean hit(FlyingObject other) {
	  int x1=other.x-this.width/2;
	  int x2=other.x+other.width+this.width/2;
	  int y1=other.y-this.height/2;
	  int y2=other.y+other.height+this.height/2;
	  int hx=this.x+this.width/2;//英雄机的中心点x坐标
	  int hy=this.y+this.height/2;//英雄机的中心点y坐标
	  return hx>x1 && hx<x2 && hy>y1 && hy<y2;
	}

13.3.编写减命减火力方法

英雄机和敌人撞上了

英雄机减命,火力值归0;

敌人消失;

在Hero中编写:

/**
	 * 英雄机减命
	 */
	public void reduceLife() {
		life--;
    }
	/**
	 * 设置英雄机火力值为单倍
	 */
	public void setDoubleFire(int doubleFire) {
		this.doubleFire=doubleFire;
	}

13.4.判断游戏是否结束

什么时候游戏结束:英雄机命数小于等于0即游戏结束,英雄机命数为1时蜜蜂和敌机同时撞英雄机命数为-1;游戏结束

在ShootGame中编写isGameOver

/**
	 * 判断游戏游戏是否结束
	 * @return true 结束 false 未结束
	 */
	public boolean isGameOver() {
		for (int i = 0; i < flyings.length; i++) {//遍历所有敌人
			FlyingObject f=flyings[i];//获取每一个敌人
			if(hero.hit(f)) {//判断英雄机是否与当前这个敌人相撞
				//减命
				hero.reduceLife();
				//英雄机活力值清零
				hero.setDoubleFire(0);
				//将数组最后一个敌人添加到被撞敌人的位置
				flyings[i]=flyings[flyings.length-1];
				//将被撞敌人添加到数组的最后一个位置等待数组缩容
				flyings[flyings.length-1]=f;
				//缩容 会删除数组的最后一个元素
				flyings=Arrays.copyOf(flyings, flyings.length-1);
			}
		}
		return hero.getLife()<=0;
	}

13.5.定义检查游戏结束方法

在ShootGame中编写

/**
	 * 检测游戏结束
	 */
	public void checkGameOver() {
		if(isGameOver()) {//游戏结束
			System.out.println("游戏结束!");
		}
	}

13.6.调用检查方法

英雄机和敌人撞也是定时发生的;

检查是否结束也是定时发生的

回到Action中的run调用

public void run() {
					enterAction();//调用飞行物入场方法
					stepAction();//调用飞行物走步方法
					shootAction();//调用子弹入场方法
					bangAction();//调用子弹与飞行物碰撞的方法
					outOfBoundsAction();//调用删除越界飞行物的方法
					checkGameOver();//调用检测游戏结束的方法
}

14.画状态

14.1.分析

游戏分为四种状态,分别为START、RUNNING、PAUSE、GAME_OVER,表示游戏开始状态、运行状态、暂停状态以及游戏结束状态。

首先结束下鼠标事件对状态的影响,当执行鼠标点击事件时,会对游戏中的START状态、GAME_OVER状态产生影响。如果点击鼠标时为START状态,则将游戏的状态设置为RUNNING,即点击鼠标游戏进入运行状态,如果点击鼠标时为GAME_OVER状态,则将flyings数组、bullets数组、hero对象、score变量设置为初始状态,并将状态设置为START状态。

14.2.编写四个常量和状态属性

public class ShootGame extends JPanel{
	public static final int WIDTH=400;//游戏窗口的宽
	public static final int HEIGHT=700;//游戏窗口的高
	
	//定义四个常量代表游戏的四种状态
	public static final int START=0;//启动状态
	public static final int RUNNING=1;//运行状态
	public static final int PAUSE=2;//暂停状态
	public static final int GAME_OVER=3;//结束状态
	private int state=START;//记录当前的状态,初始状态为未启动状态
}

14.3.状态切换

14.3.1.定义绘制状态方法
/**
	 * 绘制游戏的状态 
	 * @param g
	 */
	private void paintState(Graphics g) {
		switch(state) {
		case START:
			g.drawImage(start,0,0,null);
		    break;
		case PAUSE:
			g.drawImage(pause,0,0,null);
			break;
		case GAME_OVER:
			g.drawImage(gameover,0,0,null);
			break;
		}
	}
14.3.2.调用绘制方法

找到paint方法

/**
	 * 绘制方法
	 */
	@Override
	public void paint(Graphics g) {
		paintHero(g);//画英雄机
		paintFlyingObjects(g);//画飞行物
		paintBullets(g);//画子弹
		paintScore(g);//画分画命
		paintState(g);//画游戏状态
	}
14.3.3.调用状态

回到run中,此时运行程序敌人不见;只有英雄机在动。

@Override
			public void run() {
			  if(state==RUNNING) {
					enterAction();//调用飞行物入场方法
					stepAction();//调用飞行物走步方法
					shootAction();//调用子弹入场方法
					bangAction();//调用子弹与飞行物碰撞的方法
					outOfBoundsAction();//调用删除越界飞行物的方法
					checkGameOver();//调用检测游戏结束的方法
				}
				repaint();//在if外,重新绘制
			}

此时鼠标动英雄机不动了;

	@Override
			public void mouseMoved(MouseEvent e) {//鼠标移动事件
				if(state==RUNNING) {
				 int x=e.getX();//鼠标的x坐标
				 int y=e.getY();//鼠标的y坐标
				 hero.moveTo(x, y);//调用英雄机移动方法
				}
			}
13.3.4.添加事件

添加鼠标点击事件

此时启动能变运行,但是没法将结束变为启动。

@Override
			public void mouseClicked(MouseEvent e) {//鼠标点击事件
				switch(state) {//根据当前状态操作
				 case START://启动状态
					 state=RUNNING;//修改为运行状态
			         break;
				 case GAME_OVER://结束状态
					 state=START;//修改为启动状态
					 break;
				}
			}

回到检测游戏结束方法中,此时结束状态变启动

/**
	 * 检测游戏结束
	 */
	public void checkGameOver() {
		if(isGameOver()) {//游戏结束
			state=GAME_OVER;
		}
	}

此时还需要所有数据清零;

@Override
			public void mouseClicked(MouseEvent e) {//鼠标点击事件
				switch(state) {//根据当前状态操作
				 case START://启动状态
					 state=RUNNING;//修改为运行状态
			         break;
				 case GAME_OVER://结束状态
					 score=0;//分数清零
					 flyings=new FlyingObject[0];//敌人清空
					 bullets=new Bullet[0];//子弹清空
					 hero=new Hero();//英雄机重新创建
					 state=START;//修改为启动状态
					 break;
				}
			}

添加鼠标移出和移入事件

在移出和移入事件中添加暂停状态

	@Override
			public void mouseExited(MouseEvent e) {//鼠标移出事件
				//在运行状态下出现鼠标移出事件则将状态改为暂停
				if(state==RUNNING) {
					state=PAUSE;
				}
			}
			@Override
			public void mouseEntered(MouseEvent e) {//鼠标移入事件
				//在暂停状态下出现鼠标移入事件则将状态改为运行状态
				if(state==PAUSE) {
					state=RUNNING;
				}
			}

15.背景图轮播

15.1.定义天空类

package cn.tedu.shoot;
/**
 * 表示天空
 * @author Administrator
 *
 */
public class Sky extends FlyingObject{
   /**
    * 构造方法
    */
	public Sky() {
	   image=ShootGame.background;//背景图片
	   height=image.getHeight();//背景图的高度
	   y1=-ShootGame.HEIGHT;
   }
	/**
	 * 天空的走步步数
	 */
	private int speed=1;
	/**
	 * 第二张天空的y坐标
	 */
	protected int y1;
	/**
	 * 重写走步方法
	 */
	@Override
	public void step() {
		y+=speed;//第一张y增加向下走
		y1+=speed;//第二张y增加向下走
		if(y>=ShootGame.HEIGHT) {//第一张图片移动到窗口下方了
			y=-ShootGame.HEIGHT;//将第一张图片调整到窗口的上方
		}
		if(y1>=ShootGame.HEIGHT) {//第二张图片移动到窗口下方了
			y1=-ShootGame.HEIGHT;//将第二张图片调整到窗口的上方
		}
	}
	@Override
	public boolean outOfBounds() {
		return false;//背景图不轮播
	}
}

15.2.创建天空对象

在ShootGame中创建天空对象

    private Hero hero=new Hero();//英雄机
	private Sky sky=new Sky();//天空对象
	private FlyingObject[] flyings={};//飞行物数组(大敌机、小敌机、小蜜蜂)
	private Bullet[] bullets={};//子弹数组

15.3.定义绘制天空方法

/**
	 * 绘制天空
	 * @param g
	 */
	private void paintSky(Graphics g) {
		g.drawImage(sky.image,sky.x,sky.y,null);//画背景图
		g.drawImage(sky.image,sky.x,sky.y1,null);//画背景图
	}

15.4.调用绘制方法

找打paint()方法

/**
	 * 绘制方法
	 */
	@Override
	public void paint(Graphics g) {
		paintHero(g);//画英雄机
		paintFlyingObjects(g);//画飞行物
		paintBullets(g);//画子弹
		paintScore(g);//画分画命
		paintState(g);//画游戏状态
        paintSky(g);//调用绘制天空
	}

15.5.调用天空走步方法

找到stepAction()方法

/**
	 * 飞行物走步的启动方法
	 */
	public void stepAction(){
		sky.step();//天空走步
		hero.step();//英雄机走步
		for (int i = 0; i < flyings.length; i++) {//遍历所有飞行物(大小敌机、蜜蜂)
			flyings[i].step();//大小敌机、蜜蜂走步
		}
		for (int i = 0; i < bullets.length; i++) {//遍历所有子弹
			bullets[i].step();//子弹走步
		}
	}

15.6.天空清空

@Override
			public void mouseClicked(MouseEvent e) {//鼠标点击事件
				switch(state) {//根据当前状态操作
				 case START://启动状态
					 state=RUNNING;//修改为运行状态
			         break;
				 case GAME_OVER://结束状态
					 score=0;//分数清零
					 flyings=new FlyingObject[0];//敌人清空
					 bullets=new Bullet[0];//子弹清空
					 hero=new Hero();//英雄机重新创建
					 sky=new Sky();//天空清空
					 embers=new Ember[0];//爆炸清空
					 state=START;//修改为启动状态
					 break;
				}
			}

16.爆炸效果

16.1.定义爆炸类

package cn.tedu.shoot;

import java.awt.image.BufferedImage;

/**
 * 灰烬 飞机被打掉以后的残影
 * @author Robin
 */
public class Ember {
	private BufferedImage[] images={};
	private int index;
	private int i;
	private BufferedImage image;
	private int intevel = 10;
	private int x,y;
	public Ember(FlyingObject object) {
		images = new BufferedImage[] {ShootGame.bom1,ShootGame.bom2,ShootGame.bom3,ShootGame.bom4};
		image = ShootGame.bom1;
		x = object.x;
		y = object.y;
		index = 0;
		i = 0;
	}
	
	public boolean burnDown(){
		i++;
		if(i%intevel==0){
			if(index == images.length){
				return true;
			}
			image = images[index++];
		}
		return false;
	}
	public int getX() {
		return x;
	}
	public int getY() {
		return y;
	}
	public BufferedImage getImage() {
		return image;
	}
}

16.2.创建爆炸对象数组

	private Hero hero=new Hero();//英雄机
	private Sky sky=new Sky();//天空
	private FlyingObject[] flyings={};//飞行物数组(大敌机、小敌机、小蜜蜂)
	private Bullet[] bullets={};//子弹数组
	private Ember[] embers = {}; // 灰烬

16.3.绘制爆炸方法

/**
	 * 绘制爆炸方法
	 * @param g
	 */
	public void paintEmber(Graphics g) {
		for (int i = 0; i < embers.length; i++) {
			Ember e = embers[i];
			g.drawImage(e.getImage(), e.getX(), e.getY(), null);
		}
	}

16.4.调用绘制爆炸方法

/**
	 * 绘制方法
	 */
	@Override
	public void paint(Graphics g) {
		paintSky(g);//画天空
		paintHero(g);//画英雄机
		paintFlyingObjects(g);//画飞行物
		paintBullets(g);//画子弹
		paintScore(g);//画分画命
		paintState(g);//画游戏状态
		paintEmber(g);//调用绘制爆炸方法
	}

16.5.删除爆炸方法

	private void emberAction() {
		Ember[] live = new Ember[embers.length];
		int index = 0;
		for (int i = 0; i < embers.length; i++) {
			Ember ember = embers[i];
			if(! ember.burnDown()){
				live[index++]=ember;
			}
		}
		embers = Arrays.copyOf(live, index);
	}

16.6.调用删除爆炸方法

public void run() {
			if(state==RUNNING) {
				enterAction();//调用飞行物入场方法
				stepAction();//调用飞行物走步方法
				shootAction();//调用子弹入场方法
				bangAction();//调用子弹与飞行物碰撞的方法
				outOfBoundsAction();//调用删除越界飞行物的方法
				checkGameOverAction();//检查游戏是否结束
				emberAction();//调用删除爆炸方法
			}

16.7.飞行物爆炸

让飞行物爆炸找到bang方法

/**
	 * 检测一个子弹与所有飞行物(大小敌机、小蜜蜂)的碰撞
	 */
	public void bang(Bullet b){
		int index=-1;//记录被撞敌人的下标,不能为0
		for (int i = 0; i < flyings.length; i++) {//遍历所有敌人
			FlyingObject f=flyings[i];//得到每一个敌人
			if(f.shootBy(b)){//判断是否发生碰撞
				index=i;//记录被撞敌人的下标
				break;//其余的敌人不再做比较
			}
		}
		if(index!=-1){//有被撞的敌人
			FlyingObject one=flyings[index];//获取被撞的对象
			if(one instanceof Enemy){//判断被撞对象是否是敌人
			  Enemy e=(Enemy)one;//强制转换
			  score+=e.getScore();//将敌人的分数得到并加到总分数上
			}
			if(one instanceof Award){//判断被撞对象是否是奖励
				Award a=(Award)one;//强转
				int type=a.getType();//获取奖励类型
				switch(type){//判断是双倍火力还是加命
				 case Award.DOUBLE_FIRE:
					 hero.addDoubleFire();//增加火力值
					 break;
				 case Award.LIFE:
					 hero.addLife();//增加命
					 break;
				}
			}
			//让被撞敌人消失
			FlyingObject f=flyings[index];//取出被撞敌人
			//将数组最后一个敌人添加到被撞敌人的位置
			flyings[index]=flyings[flyings.length-1];
			//将被撞敌人添加到数组的最后一个位置等待数组缩容
			flyings[flyings.length-1]=f;
			//缩容 会删除数组的最后一个元素
			flyings=Arrays.copyOf(flyings, 
					flyings.length-1);
			//飞行物变成灰烬
			Ember ember = new Ember(one);
			embers = Arrays.copyOf(embers, embers.length+1);
			embers[embers.length-1]=ember;
		}
	}

16.8.删除爆炸物

/**
	 * 判断游戏是否结束
	 * @return
	 */
	public boolean isGameOver() {
		for (int i = 0; i < flyings.length; i++) {
			FlyingObject f=flyings[i];//获取每一个敌人
			if(hero.hit(f)) {//撞上了
				hero.reduceLife();//英雄机减命
				hero.setDoubleFire(0);//英雄机火力清零
				Ember ember = new Ember(hero);
				embers = Arrays.copyOf(embers, embers.length+1);
				embers[embers.length-1]=ember;
				//让被撞敌人消失
				//FlyingObject f=flyings[i];//取出被撞敌人
				//将数组最后一个敌人添加到被撞敌人的位置
				flyings[i]=flyings[flyings.length-1];
				//将被撞敌人添加到数组的最后一个位置等待数组缩容
				flyings[flyings.length-1]=f;
				//缩容 会删除数组的最后一个元素
				flyings=Arrays.copyOf(flyings, flyings.length-1);
				Ember ember1 = new Ember(f);
				embers = Arrays.copyOf(embers, embers.length+1);
				embers[embers.length-1]=ember1;
			}
		}
		return hero.getLife()<=0;//英雄机命数小于等于0游戏结束
	}

16.9.清空爆炸效果

  • 24
    点赞
  • 128
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
要下载飞机大战Java项目并在Eclipse中运行,需按照以下步骤进行: 第一步:去相应的网站查找飞机大战Java项目的下载链接,可以在搜索引擎中输入相关关键词来寻找,下载链接可能会提供一个压缩包或一个完整项目文件。 第二步:打开Eclipse,选择一个合适的工作空间,点击"File"(文件)菜单,选择"Import"(导入)来打开导入窗口。 第三步:在导入窗口中,展开"General"(通用)文件夹,选择"Existing Project into Workspace"(从现有项目导入工作区)并点击"Next"(下一步)按钮。 第四步:在导入窗口的"Select root directory"(选择根目录)部分,点击"Browse"(浏览)按钮,找到之前下载的飞机大战Java项目所在的文件夹,选择并点击"OK"(确定)按钮。 第五步:在导入窗口中,将飞机大战Java项目的名称选中,确保"Copy projects into workspace"(将项目复制到工作空间)选项被选中,然后点击"Finish"(完成)按钮。 第六步:Eclipse将自动导入并构建项目。一旦项目导入完成,可以在"Package Explorer"(包资源管理器)中看到它。 第七步:现在可以在Eclipse中打开和运行飞机大战Java项目了。在"Package Explorer"中找到项目并展开它,然后找到主类并右键点击它,选择"Run As"(以此方式运行)来启动项目。 以上是下载飞机大战Java项目并在Eclipse中运行的步骤。希望对你有所帮助。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值