小敌机类
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
/* *小敌机类*/
public class Airplane extends FiyingObject implements EnemyScore{
//宽高 大小 移动速度
private int speed;//移动速度
public Airplane(){//构造方法
super(48,50);
speed = 2;
}
//重写step()飞行物移动
public void step(){//派生类的方法要大于等于超类方法访问权限
y += speed;//y坐标向下移动
}
private int index = 1;//下标
/** 重写getImage()获取图片*/
public BufferedImage getImage(){
if (islife()) {//若活着的
return images.airs[0];//返回第一张图即可
}else if (isDead()) {//若死了的
BufferedImage img = images.airs[index++];//获取爆破图
if(index == images.airs.length){//若到最后一张图
state = REMOVE;//则将状态修改为REMOVE
}
return img;//返回爆破图
}
return null;//删除状态时,不返回图片
/*假设为DEAD死了的
* index = 1
* 10ms img = airs[1] index = 2 返回airs[1]
* 20ms img = airs[2] index = 3 返回airs[2]
* 30ms img = airs[3] index = 4 返回airs[3]
* 40ms img = airs[4] index = 5(REMOVE)返回airs[4]
* 50ms 返回null(不返回图片)
*/
}
/**重写getScore 得分*/
public int getScore(){
return 1;//打掉小敌机得1分
}
}
大敌机类
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
/** 大敌机类*/
public class Bigairplane extends FiyingObject implements EnemyScore{
//宽高 大小 移动速度
private int speed;//移动速度
public Bigairplane(){//构造方法
super(66,89);
speed = 2;
}
public void step(){
y += speed;//y坐标向下移动
}
private int index = 1;//下标
/** 重写getImage()获取图片*/
public BufferedImage getImage(){
if (islife()) {//若活着的
return images.bairs[0];//返回第一张图即可
}else if (isDead()) {//若死了的
BufferedImage img = images.bairs[index++];//获取爆破图
if(index == images.bairs.length){//若到最后一张图
state = REMOVE;//则将状态修改为REMOVE
}
return img;//返回爆破图
}
return null;//删除状态时,不返回图片
/*假设为DEAD死了的
* index = 1
* 10ms img = bairs[1] index = 2 返回bairs[1]
* 20ms img = bairs[2] index = 3 返回bairs[2]
* 30ms img = bairs[3] index = 4 返回bairs[3]
* 40ms img = bairs[4] index = 5(REMOVE)返回bairs[4]
* 50ms 返回null(不返回图片)
*/
}
/**重写getScore 得分*/
public int getScore(){
return 3;//打掉大敌机得3分
}
}
小蜜蜂类
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
import java.util.Random;
/** 小蜜蜂类 */
public class Bee extends FiyingObject implements EnemyAward{
//宽高 大小 移动速度
private int xSpeed;//x坐标移动速度
private int ySpeed;//y坐标移动速度
private int awardType;//奖励类型
public Bee(){//构造方法
super(60,51);
xSpeed = 1;
ySpeed = 2;
Random rand = new Random();
awardType = rand.nextInt(2);//0到1之间的随机数
}
public void step(){//小蜜蜂移动轨迹
x += xSpeed;//x向左或向右移动
y += ySpeed;//y向下移动
if(x>=World.WIDTH-this.width || x<=0){//若x<=0或>=World.WIDTH-this.width(左或右碰壁)
xSpeed*=-1;//切换方向
}
}
private int index = 1;//下标
/** 重写getImage()获取图片*/
public BufferedImage getImage(){
if (islife()) {//若活着的
return images.bees[0];//返回第一张图即可
}else if (isDead()) {//若死了的
BufferedImage img = images.bees[index++];//获取爆破图
if(index == images.bees.length){//若到最后一张图
state = REMOVE;//则将状态修改为REMOVE
}
return img;//返回爆破图
}
return null;//删除状态时,不返回图片
/*假设为DEAD死了的
* index = 1
* 10ms img = bees[1] index = 2 返回bees[1]
* 20ms img = bees[2] index = 3 返回bees[2]
* 30ms img = bees[3] index = 4 返回bees[3]
* 40ms img = bees[4] index = 5(REMOVE)返回bees[4]
* 50ms 返回null(不返回图片)
*/
}
/** 重写getAwardType()接口*/
public int getAwardType(){
return awardType;//返回奖励类型
}
}
英雄机类
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
/** 英雄机类 */
public class Hero extends FiyingObject{
// 大小 宽高 命值 火力值
private int life;//生命值
private int fire;//火力值
public Hero(){//构造方法
super(97,139,140,400);
life = 3;
fire = 0;
}
public void step(){}
private int index = 0;//下标
/** 重写getImage()获取图片*/
public BufferedImage getImage(){//每10毫秒走一次
return images.heros[index++%images.heros.length];
/*
* index = 0
* 10ms 返回heros[0] index = 1
* 20ms 返回heros[1] index = 2
* 30ms 返回heros[0] index = 3
* 40ms 返回heros[1] index = 4
* 50ms 返回heros[0] index = 5
* ......
*/
}
/** 英雄级发射子弹(生成子弹对象)*/
public Bullet[] shoot(){
int xStep = this.width/4;//xStep:1/4英雄机的宽
int yStep = 20;//yStep:固定值20
if (fire > 0) {//双倍火力
Bullet[] bs = new Bullet[2];//2发子弹
bs[0] = new Bullet(this.x+1*xStep,this.y-yStep);
bs[1] = new Bullet(this.x+3*xStep,this.y-yStep);
fire -= 2;//火力值-2
return bs;
} else {//单倍火力
Bullet[] bs = new Bullet[1];//1发子弹
bs[0] = new Bullet(this.x+2*xStep,this.y-yStep);
return bs;
}
}
/** 英雄机随鼠标移动*/
public void moveTo(int x,int y){//x,y表示鼠标的x,y坐标
this.x = x - this.width/2;//英雄机的x = 鼠标x - 英雄机宽度的一半
this.y = y - this.height/2;//英雄机的y = 鼠标y - 英雄机高度的一半
}
/** 英雄机增命*/
public void addLife(){
life++;//生命值增加
}
/** 英雄机减生命值*/
public void aubtractLife(){
life--;//命数-1
}
/** 英雄机增火力*/
public void addFire(){
fire +=40;//火力值增加40
}
/** 英雄机清空火力值*/
public void clearFire(){
fire = 0;//清空
}
/** 获取生命值*/
public int getLife(){
return life;//返回生命值
}
}
天空类
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
//天空
public class Sky extends FiyingObject{
private int y1;//第二张图片的y坐标
private int speed;//移动速度
public Sky(){//构造方法
super(World.WIDTH,World.HEIGHT,0,0);
y1 = -World.HEIGHT;
speed = 1;
}
public void step(){
y += speed;
y1 += speed;
if (y>= World.HEIGHT) {//若y>=窗口的高,表示到窗口的
y = -World.HEIGHT;//则修改y的值为负的窗口的高
}
if (y1>= World.HEIGHT) {
y1 = -World.HEIGHT;
}
}
/** 重写getImage()获取图片*/
public BufferedImage getImage(){
return images.sky;//直接返回sky图片即可
}
/** 获取y1坐标*/
public int getY1(){
return y1;//返回y1坐标
}
}
子弹类
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
/** 子弹 */
public class Bullet extends FiyingObject{
//宽高 大小 移动速度
private int speed;//移动速度
public Bullet(int x,int y ){//子弹构造带参是因为有很多发子弹,
super(8,20,x,y); //二每发子弹的位置根据英雄机的位置而固定
speed = 3;
}
public void step(){
y -= speed;//y坐标向上移动
}
/** 重写getImage()获取图片*/
public BufferedImage getImage(){
if(islife()){//若为活着的
return images.bullet;//则直接返回bullet图片
}else if(isDead()){//若为死了的
state = REMOVE;//则将状态修改为REMOVE删除的
}
return null;//死了的和删除的,都不返回图片
/*
* 若为活着的:返回子弹图片
* 若为死了的:将状态改为REMOVE,同时不返回图片
* 若为删除的:不返回图片
*/
}
public boolean isOutOfBunds(){//判断子弹是否越界
return y <= -height;//敌人的y<= -高,即为越界
}
}
飞行物超类
package cn.tedu.shoot;
import java.util.Random;
import java.awt.image.BufferedImage;
//飞行物
public abstract class FiyingObject {
public static final int LIFE = 0;//活着的
public static final int DEAD = 1;//死了的
public static final int REMOVE = 2;//删除的
public int state = LIFE;//当前状态(默认活着的)
protected int width;//宽
protected int height;//高
protected int x;//x坐标
protected int y;//y坐标
//专门为大小敌机,小蜜蜂提供
public FiyingObject( int width,int height){
this.width = width;
this.height = height;
Random rand = new Random();//随机数对象
x = rand.nextInt(World.WIDTH - width);//0 - (窗口宽-宽的随机数生成)
y = -height;//-高
}
//专门为英雄机,子弹 ,天空提供
//因为英雄机,子弹 ,天空的宽高都不同,都要写活
public FiyingObject(int width,int height,int x,int y){
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
/** 飞行物移动 */
public abstract void step();
/**获取对象的图片 */
public abstract BufferedImage getImage();
/** 判断是否是活着的*/
public boolean islife(){
return state == LIFE;//若当前状态为LIFE ,则返回true,否则返回false
}
public boolean isDead(){
return state == DEAD;//若当前状态为DEAD ,则返回true,否则返回false
}
public boolean isRemove(){
return state == REMOVE ;//若当前状态为REMOVE ,则返回true,否则返回false
}
/** 判断敌人是否越界*/
public boolean isOutOfBunds(){
return y >= World.HEIGHT;//敌人的y>=窗口的高,即为越界
}
/** 判断敌人是否与子弹/英雄机碰撞 this:敌人 other:子弹/英雄机*/
public boolean isHit(FiyingObject other){
int x1 = this.x - other.width;//x1:敌人X-子弹宽
int x2 = this.x + this.width;//x2:敌人X+子弹宽
int y1 = this.y - other.height;//y1:敌人Y-子弹高
int y2 = this.y + this.height;//y2:敌人Y+子弹高
int x = other.x;//子弹/英雄机的x
int y = other.y;//子弹/英雄机的y
return x >= x1 && x <= x2 && y >= y1 && y <= y2;//x在x1与x2之间 ,y在y1与y2之间
}
public void goDead(){
state = DEAD;//将对象状态修改为DEAD死了的
}
}
获取图片类
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
//图片工具类
public class images {
// 公开的 静态的 图片数据类型 变量名(引用)
public static BufferedImage sky;//天空图片
public static BufferedImage bullet;//子弹图片
public static BufferedImage[] heros;//英雄机图片数组
public static BufferedImage[] airs;//小敌机图片数组
public static BufferedImage[] bairs;//大敌机图片数组
public static BufferedImage[] bees;//小蜜蜂图片数组
public static BufferedImage start;//启动图
public static BufferedImage pause;//暂停图
public static BufferedImage gameover;//游戏结束图
static{//初始化静态资源
start = readImage("start.png");
pause = readImage("pause.png");
gameover = readImage("gameover.png");
sky = readImage("background.png");//sky = 读取background.png图片
bullet = readImage("bullet.png");//Bullet = 读取bullet.png图片
heros = new BufferedImage[2];//获取英雄机两张图
heros[0] = readImage("hero0.png");//Hero[0] = 读取hero0.png图片
heros[1] = readImage("hero1.png");//Hero[1] = 读取ground.png图片
airs = new BufferedImage[5];
bairs = new BufferedImage[5];
bees = new BufferedImage[5];
airs[0] = readImage("airplane.png");
bairs[0] = readImage("bigairplane.png");
bees[0] = readImage("bee.png");
for(int i = 1;i<airs.length;i++){
airs[i] = readImage("bom"+i+".png");
bairs[i] = readImage("bom"+i+".png");
bees[i] = readImage("bom"+i+".png");
}
}
//读取图片
public static BufferedImage readImage(String fileName){//fileName 图片文件名
try{
BufferedImage img = ImageIO.read(FiyingObject.class.getResource(fileName));//读取与FlyingObject类在同一包中的图片
return img;
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException();
}
}
}
得分接口
package cn.tedu.shoot;
/**得分接口*/
public interface EnemyScore {
/** 得分*/
public int getScore();
}
奖励接口
package cn.tedu.shoot;
/** 奖励接口 */
public interface EnemyAward {
public int FIRE = 0;//火力值
public int LIFE = 1;//命值
/** 获取奖励类型 */
public int getAwardType();
}
综合执行类
package cn.tedu.shoot;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Random;
import java.util.Arrays;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
//窗口类
public class World 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;//当前状态(默认为启动状态)
private Hero h =new Hero();//英雄机
private Sky s = new Sky();//天空
private FiyingObject[] enemies = { };//敌人组
private Bullet[] bullets = {};//子弹组
/*
FiyingObject[] enemiess; //输出长度会发生空指针异常
FiyingObject[] enemiess = null; //输出长度会发生空指针异常
FiyingObject[] enemies = {}; //输出长度为0;
FiyingObject[] enemies = new FlyingObject[0]; //输出长度为0;
*/
/** 创建敌人(小敌机,大敌机,小蜜蜂)对象*/
public FiyingObject nextOne(){
Random rand = new Random();//随机数对象
int type = rand.nextInt(20);//0-19之间的随机数
if (type<5) {//0-4时,返回小蜜蜂对象
return new Bee();
}else if (type<14) {//5-13时,返回小敌机对象
return new Airplane();
}else{//14-19时,返回大敌机对象
return new Bigairplane();
}
}
private int enterIndex = 0;//敌人入场计数器
/** 敌人(大敌机,小敌机,小蜜蜂)入场*/
public void enterAction(){//每10ms走一次
enterIndex ++;//每10ms自增1
if (enterIndex%40 == 0) {//每400(40*10)ms走一次
FiyingObject obj = nextOne();//获取敌人对象
enemies = Arrays.copyOf(enemies, enemies.length+1);//扩容数组
enemies[enemies.length-1] = obj;//将敌人装到最后一个元素
}
}
private int shootIndex = 0;//子弹入场计数器
/** 子弹入场*/
public void shootAction(){//每10ms走一次
shootIndex++;
if (shootIndex%30==0) {//每300(30*10)走一次
Bullet[] bs = h.shoot();
bullets = Arrays.copyOf(bullets, bullets.length+bs.length);//扩容(bs有几个元素就扩大几个)
System.arraycopy(bs, 0, bullets, bullets.length-bs.length, bs.length);//数组追加
}
}
/** 飞行物移动*/
public void stepAction(){//每10ms走一次
s.step();
for (int i = 0; i < enemies.length; i++) {
enemies[i].step();
}
for (int i = 0; i < bullets.length; i++) {
bullets[i].step();
}
}
/** 删除越界的敌人和子弹*/
public void OutOfBoundsAction(){//每10ms走一次
for (int i = 0; i < enemies.length; i++) {//遍历所有敌人
if (enemies[i].isOutOfBunds() || enemies[i].isRemove()) {//越界或者是删除的
enemies[i] = enemies[enemies.length-1];//用最后一个敌人替换越界敌人
enemies = Arrays.copyOf(enemies, enemies.length-1);//缩容
}
}
for (int j = 0; j < bullets.length; j++) {//遍历所有子弹
if (bullets[j].isOutOfBunds() || bullets[j].isRemove()) {//判断越界或删除的
bullets[j] = bullets[bullets.length-1];//用最后一颗子弹替换越界子弹
bullets = Arrays.copyOf(bullets, bullets.length-1);//缩容
}
}
}
private int score = 0;//玩家得分
/** 子弹和敌人发生碰撞*/
public void bulletBangAction(){//每10ms走一次、
for (int i = 0; i < bullets.length; i++) {//遍历所有子弹
Bullet b = bullets[i];//获取每个子弹
for (int j = 0; j < enemies.length; j++) {//遍历所有敌人
FiyingObject f = enemies[j];//获取所有敌人
if (b.islife()&&f.islife()&&f.isHit(b)) {//子弹和敌人都活着并且子弹和敌人发生碰撞
b.goDead();//子弹去死
f.goDead();//敌人去死
if (f instanceof EnemyScore) {//若被撞敌人能得分
EnemyScore es = (EnemyScore)f;//将被撞对象强转为得分接口
score +=es.getScore();//玩家得分
}
if (f instanceof EnemyAward) {//若被撞敌人能得奖励
EnemyAward ea = (EnemyAward)f;//将被撞对象强转为奖励借口
int type = ea.getAwardType();//或得奖励类型
switch(type){//根据奖励类型的不同来获取不同的奖励
case EnemyAward.FIRE://若奖励类型为FIRE火力值
h.addFire();//则获得火力值
break;
case EnemyAward.LIFE://若奖励类型为LIFE生命值
h.addLife();//则获得生命值
break;
}
}
}
}
}
}
/** 英雄机与敌人发生碰撞*/
public void heroBangAction(){//每10ms走一次
for (int i = 0; i < enemies.length; i++) {//遍历所有敌人
FiyingObject f = enemies[i];//获取每一个敌人
if (h.islife() && f.islife() && f.isHit(h)) {//英雄机和敌人都活着并且英雄机和敌人发生碰撞
f.goDead();//敌人去死
h.aubtractLife();//英雄机减生命值
h.clearFire();//英雄机清空火力值
}
}
}
/** 检测游戏结束*/
public void checkGameOverAction(){//每10ms走一次
if(h.getLife() <= 0){//英雄机的命数小于等于0,表示游戏结束
state = GAME_OVER;//将当前状态修改为GAME_OVER
}
}
/** 启动程序的执行*/
public void action(){
//侦听器对象
MouseAdapter m = new MouseAdapter(){
/** 重写mouseMoved()鼠标移动事件*/
public void mouseMoved(MouseEvent e){
if(state == RUNNING){
int x = e.getX();//获取鼠标的x坐标
int y = e.getY();//获取鼠标的y坐标
h.moveTo(x,y);
}
}
/** 重写mouseClicked()鼠标点击事件*/
public void mouseClicked(MouseEvent e){
switch(state){//根据当前状态做不同处理
case START://启动状态时
state = RUNNING;//修改为运行状态
break;
case GAME_OVER://游戏结束状态时
score = 0;//清理现场(所有数据还原)
h = new Hero();
s = new Sky();
enemies = new FiyingObject[0];
bullets = new Bullet[0];
state = START;//修改为启动状态
break;
}
}
/** 重写mouseExited()鼠标移出事件*/
public void mouseExited(MouseEvent e){
if(state == RUNNING){//运行状态时
state = PAUSE;//修改为暂停状态
}
}
/** 重写mouseEntered()鼠标移入事件*/
public void mouseEntered(MouseEvent e){
if(state == PAUSE){//暂停状态时
state = RUNNING;//修改为运行状态
}
}
};
this.addMouseListener(m);//处理鼠标操作事件
this.addMouseMotionListener(m);//处理鼠标滑动事件
//定时器对象
Timer timer = new Timer();
int intervel = 10;//定时间隔(每10ms走一次)
timer.schedule(new TimerTask(){//定时计划表
public void run() {//定时任务
if(state == RUNNING){
enterAction();//敌人(小敌机,大敌机,小蜜蜂)入场
shootAction();//子弹入场
stepAction();//飞行物移动
OutOfBoundsAction();//删除越界的敌人和子弹
bulletBangAction();//子弹与敌人的碰撞
heroBangAction();//英雄机与敌人的碰撞
checkGameOverAction();//检测游戏结束
}
repaint();//重画(重新调用paint()方法)
}
}, intervel, intervel);//第一个intervel:启动程序到第1次执行时间间隔,第二个intervel:第n次执行到第n+1次执行时间间隔
}
/** 重写paint()画 g:画笔*/
public void paint (Graphics g){//每10ms走一次
g.drawImage(s.getImage(),s.x,s.y,null);//画天空
g.drawImage(s.getImage(),s.x,s.getY1(),null);
g.drawImage(h.getImage(),h.x,h.y,null);
for (int i = 0; i < enemies.length; i++) {
FiyingObject f = enemies[i];
g.drawImage(f.getImage(),f.x,f.y,null);
}
for (int j = 0; j < bullets.length; j++) {
Bullet b = bullets[j];
g.drawImage(b.getImage(),b.x,b.y,null);
}
g.drawString("得分:"+score,10,25);//画得分
g.drawString("生命值:"+h.getLife(), 10, 45);//画生命值
switch(state){//在不同状态下话不同的图
case START://启动状态下时画启动图
g.drawImage(images.start,0,0,null);
break;
case PAUSE://暂停状态下时画暂停图
g.drawImage(images.pause,0,0,null);
break;
case GAME_OVER://游戏结束状态下时画游戏结束图
g.drawImage(images.gameover, 0, 0, null);
break;
}
}
/** 创建窗口*/
public static void main(String[] args) {//窗口创建
JFrame frame = new JFrame("飞机大战");//窗口名
World world = new World();
frame.add(world);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH,HEIGHT);//窗口大小
frame.setLocationRelativeTo(null);
frame.setVisible(true); //1)设置窗口可见2)尽快调用paint()方法
world.action();//启动程序的执行
/*
* 1)要将引用设计在main方法外面的原因:
* 若将引用设计在main中,则意味着引用只能在main中
* 而world类中后期我们会设计很多方法,方法中都要用到这些引用
* 为了在所有的方法中都能使用引用,所以要设计在main方法外面
* 2)创建action来测试的原因:
* 因为main方法是static的,所以main中是无法访问那一堆引用,
* 所以单独创建了非static的方法action来测试
* static方法中无法访问引用的原因,后期解决
* 3)在main中要先创建World对象在调用action()方法
* 因为main方法是static的,所以main中无法直接调用action()方法,
* 那就只能先创建world对象在调用了
* static方法中无法直接调用action的原因,后期解决
*/
}
}