飞机大战(案例详解)

前言

飞机大战是一个非常经典的案例,因为它包含了多种新手需要掌握的概念,是一个非常契合面向对象思想的入门练习案例

程序分析:

在此游戏*有六个对象:
小敌机Airplane,大敌机BigAirplane,小蜜蜂Bee,天空Sky,英雄机Hero,子弹Bullet

其次我们还需要三个类:
超类Flyer,图片类Images,测试类World

还需:
英雄机2张,小敌机,大敌机,小蜜蜂,子弹,天空各1张,爆炸图4张,游戏开始,暂停,游戏结束各1张,共14张图片放入与图片类Images同包中
飞机大战(案例详解)

超类Flyer:

此类是用来封装所有对象共有的行为及属性的
不管是写什么程序,都建议遵循两点:数据私有化,行为公开化

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

public abstract class Flyer {
	//所有对象都有三种状态:活着的,死了的,及删除的
	//这里之所以选择用常量表示状态是因为首先状态是一个不需要去修改的值
	//其次状态需要反复使用所以结合这两个特点,我选择了使用常量表示
	//state是用来表示当前状态的,每个对象都有一个实时的状态,此状态是会改变的,且初始状态都是活着的
    public static final int LIVE = 0;//活着的
    public static final int DEAD = 1;//死了的
    public static final int REMOVE = 2;//删除的
    protected int state = LIVE;//当前状态(默认状态为活着的)

   每个对象都是一张图片,既然是图片那么就一定有宽高,其次因为每个对象都是会随时移动的 即为都有x,y坐标
    protected int width;//宽
    protected int height;//高
    protected int x;//左右移动(x坐标)
    protected int y;//上下移动(y坐标)

    /**
     * 飞行物移动(抽象)
     * 每个飞行物都是会移动的,但是移动方式不同
     * 所以这里就将共有的行为抽到了超类中
     * 但是设置成了抽象方法,实现了多态的效果
     */
    public abstract void step();

    /**
     * 获取图片(抽象)
     * 所有对象都是图片但图片不相同所以抽象化了
     */
    public abstract BufferedImage getImage();

    /**
     * 判断对象是否是活着的
     */
    public boolean isLive(){
        return state == LIVE;
    }
    /**
     * 判断对象是否是死了的
     */
    public boolean isDead(){
        return  state == DEAD;
    }
    /**
     * 判断对象是否删除了
     */
    public boolean isRemove(){
        return state == REMOVE;
    }

    /**
     * 判断对象(大敌机,小敌机,小蜜蜂)是否越界
     * 当敌人越界我们就需要删除它否则程序越执行越卡,会出现内存泄露的问题,此方法就是为后续删除越界对象做铺垫的
     * @return
     */
    public boolean isOutOfBounds(){
        return y >= World.HEIGHT;
    }
    /**
     * 给小/大敌机,小蜜蜂提供的
     * 因为三种飞行物的宽,高不同所以不能写死。
     * 若三种飞行物的宽,高相同,那么就可以将宽,高写死
     */
    public Flyer(int width,int height){
        Random rand = new Random();
        this.width = width;
        this.height = height;
        x = rand.nextInt(World.WIDTH-width);//x:0到负的width长度的之间的随机数
        y = -height;//y:负的height高度
    }

    /**
     * 给天空,子弹,英雄机提供的
     * 因为英雄机,子弹,天空的宽,高,x,y都是不同的,所以数据不能写死,需要传参
     */
    public Flyer(int width,int height,int x,int y){
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
    }

    /**
     *检测碰撞
     * this:敌人(小敌机/小蜜蜂/大敌机)
     * other:子弹/英雄机
     *@return
     */
    public boolean isHit(Flyer 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:英雄机/子弹的x
        int y = other.y;//y:英雄机/子弹的y
        /*
          x在x1与x2之间 并且 y在y1与y2之间,即为撞上了
         */
        return x>x1 && x<=x2 && y>=y1 && y<=y2;
    }

    /**
     * 飞行物死亡
     */
    public void goDead(){
        state = DEAD;//将当前状态修改为死了的
    }
}

图片工具类Images:

此类用来获取每个对象对应的图片

import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
/**
 * 图片工具类
 */
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 {//初始化静态图片
        sky = readImage("background01.png");//天空
        bullet = readImage("bullet.png");//子弹
        heros = new BufferedImage[2];//英雄机图片数组
        heros[0] = readImage("hero0.png");//英雄机图片1
        heros[1] = readImage("hero1.png");//英雄机图片2
        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("bee01.png");//小蜜蜂图片读取

        /**爆炸图迭代读取*/
        for (int i=1;i<5;i++){//遍历/迭代赋值
            airs[i] = readImage("bom"+i+".png");//小敌机图片数组其余元素赋值爆炸图
            bairs[i] = readImage("bom"+i+".png");//大敌机图片数组其余元素赋值爆炸图
            bees[i] = readImage("bom"+i+".png");//小蜜蜂图片数组其余元素赋值爆炸图
        }
        start = readImage("start.png");//启动状态图
        pause = readImage("pause.png");//暂停状态图
        gameover = readImage("gameover.png");//游戏结束状态图
    }

    /**
     * 读取图片
     *      此处的fileName:图片文件名
     *
     *      try.....catch:异常的一种处理方法
     */
    public static BufferedImage readImage(String fileName){
        try{
            BufferedImage img = ImageIO.read(Flyer.class.getResource(fileName)); //读取与Flyer在同一个包中的图片
            return img;
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException();
        }
    }
}

世界窗口类/测试类 World:

此类用来集合所有类进行排序及具体的操作,和程序的最终运行

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.nio.Buffer;
//定时器
import java.util.Timer;
//定时器任务
import java.util.TimerTask;
//打开随机类
import java.util.Random;
//扩容类
import java.util.Arrays;
/**
 * 世界测试类(整个游戏窗口)
 */
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 Sky s = new Sky();
    private Hero h = new Hero();
    private Flyer[] enemies ={};
    private Bullet[] bt ={};

    /**
     * 生成敌人对象(小敌机,大敌机,小蜜蜂)
     */
    public Flyer nextOne(){
        Random rand = new Random();
        int type = rand.nextInt(20);//0-19之间的随机数
        if (type < 5){
            return new Bee();//返回小蜜蜂
        }else if (type < 13){
            return new Airplane();//返回小敌机
        }else{
            return new BigAirplane();//返回大敌机
        }
    }

    private  int enterIndex = 0;
    /**
     * 敌人(大敌机,小敌机,小蜜蜂)入场
     */
    public void enterAction() {//每10毫秒走一次
        enterIndex++;
        if (enterIndex%40 == 0 ){//四百毫秒走一次
            Flyer  fl = nextOne();//获取敌人对象
            enemies = Arrays.copyOf(enemies,enemies.length+1);//扩容(每产生一个敌人数组就扩容1)
            enemies[enemies.length-1] = fl;//将生成的敌人fl放置enemies数组的末尾
        }
    }

    int shootIndex = 0;
    /**
     * 子弹入场
     */
    public void shootAction(){//10毫秒走一次
        shootIndex++;
        if (shootIndex%30 == 0){//每300毫秒走一次
            Bullet[] bs = h.shoot();//获取子弹数组对象
            bt = Arrays.copyOf(bt,bt.length+bs.length);//扩容子弹数组(每入场一个子弹就加一个元素)
            System.arraycopy(bs,0,bt,bt.length-bs.length,bs.length);//数组的追加
        }
    }

    /**
     * 让除去英雄机外的所有对象(小敌机,大敌机,小蜜蜂,子弹,天空)移动
      */
    public void setpAction() {//每10毫秒走一次
        s.step();//天空移动
        for (int i=0;i<enemies.length;i++) {//遍历所有敌人
            enemies[i].step();//敌人移动
        }
        for (int i=0;i<bt.length;i++){//遍历所有子弹
            bt[i].step();//子弹移动
        }
    }

    /**
     * 重写outOfBoundsAction(方法)
      */
    public void outOfBoundsAction() {//每10毫秒走一次
        for (int i=0;i<enemies.length;i++){//遍历所有敌人
            if (enemies[i].isOutOfBounds() || enemies[i].isRemove()){
                enemies[i] = enemies[enemies.length-1];//最后一个敌人和越界敌人替换
                enemies = Arrays.copyOf(enemies,enemies.length-1);//缩容
            }
        }
        for (int i=0;i<bt.length;i++){//迭代所有子弹
            if (bt[i].isOutOfBounds() || bt[i].isRemove()){
                bt[i] = bt[bt.length-1];//用最后一个子弹替换出界的子弹
                bt = Arrays.copyOf(bt,bt.length-1);//缩容
            }
        }
    }
    private int score = 0;//玩家的得分
    /**
     * 子弹与敌人的碰撞
     */
    public void bulletBangAction() {//每10毫秒走一次
        for (int i=0;i<bt.length;i++){//遍历所有子弹
            Bullet b = bt[i];//获取每一个子弹
            for (int j=0;j<enemies.length;j++){//迭代每一个敌人
                Flyer f = enemies[j];//获取每一个敌人
                if (b.isLive() && f.isLive() && f.isHit(b)){//若子弹活着的,敌人活着的,并且两个对象相撞
                    b.goDead();//子弹当前状态修改为死亡
                    f.goDead();//敌人当前状态修改为死亡
                    if (f instanceof EnemyScore) {//判断死亡的敌人类型能否强转为得分接口类型
                        EnemyScore es = (EnemyScore) f;//将死亡敌人向下造型
                        score += es.getScore();//调用具体的敌人对象的得分接口的getScore()加分方法
                    }
                    if (f instanceof EnemyAward){//判断死亡的敌人类型能否强转为奖励值接口类型
                        EnemyAward ea = (EnemyAward) f;//将死亡敌人强转为奖励值接口类型
                        int type = ea.getAwardType();//将具体的奖励值赋值给type
                        switch (type){
                            case EnemyAward.FIRE://火力值
                                h.addFier();//返回增加火力值
                                break;
                            case EnemyAward.LIFE://生命值
                                h.addLife();//返回增加生命值
                                break;
                        }
                    }
                }
            }
        }
    }

    /**
     * 英雄机与敌人的碰撞
     */
    private void heroBangAction() {//每10毫秒走一次
        for (int i=0;i<enemies.length;i++){//迭代所有敌人
            Flyer f = enemies[i];//获取每个敌人
            if (f.isLive() && h.isLive() && f.isHit(h)){//判断碰撞
                f.goDead();//敌人死亡
                h.subtractLife();//英雄机减生命值
                h.clearFier();//英雄机清空火力值
            }
        }
    }

    /**
     * 检测游戏结束
     */
    private void checkGameOverAction() {//每10毫秒走一次
        if (h.getLife() <= 0) {//若英雄机生命值为0或小于0
            state = GAME_OVER;//将状态修改为GAME_OVER游戏结束状态
        }
    }
    /**
     * 启动程序的执行
     */
    public void action() {//测试代码
        MouseAdapter m = new MouseAdapter() {
            /**
             * 重写mouseMoved()鼠标移动事件
              * @param e
             */
            @Override
            public void mouseMoved(MouseEvent e) {
                if (state == RUNNING){//仅在运行状态下执行
                    int x = e.getX();//获取鼠标的x坐标
                    int y = e.getY();//获取鼠标的y坐标
                    h.moveTo(x,y);//接收鼠标具体坐标
                }
            }

            /**
             * 重写mouseClicked() 鼠标点击事件
             * @param e
             */
            public void mouseClicked(MouseEvent e){
                switch (state){//根据当前状态做不同的处理
                    case START://启动状态时
                        state = RUNNING;//鼠标点击后改成运行状态
                        break;
                    case GAME_OVER://游戏结束状态时
                    /*清理战场(将所有数据初始化)*/
                        score = 0;//总分归零
                        s = new Sky();//天空初始化所有属性
                        h = new Hero();//英雄机初始化所有属性
                        enemies = new Flyer[0];//敌人初始化所有属性
                        bt = new Bullet[0];//子弹初始化所有属性
                        
                        state = START;//鼠标点击后修改为启动状态
                        break;
                }
            }

            /**
             * 鼠标移出窗口事件
             * @param e
             */
            public void mouseExited(MouseEvent e){
                 if (state == RUNNING){//若状态为运行
                     state = PAUSE;//则将当前状态修改为暂停
                 }
            }

            /**
             * 鼠标的进入窗口事件
             * @param e
             */
            public void mouseEntered(MouseEvent e){
                if (state == PAUSE){//若当前状态为暂停
                    state = RUNNING;//则将当前状态修改为运行
                }
            }
        };
        this.addMouseListener(m);
        this.addMouseMotionListener(m);
        Timer timer = new Timer();//定时器对象
        int interval = 10;//定时的间隔(此间隔是以毫秒为单位)
        timer.schedule(new TimerTask() {
            @Override
            public void run() {//定时干的事(每10毫秒自动执行)
                if (state == RUNNING){//只在运行状态下执行
                    enterAction();//敌人(大敌机,小敌机,小蜜蜂)入场
                    shootAction();//子弹入场
                    setpAction();//飞行物移动
                    outOfBoundsAction();//删除越界的敌人
                    bulletBangAction();//子弹与敌人的碰撞
                    heroBangAction();//英雄机与敌人的碰撞
                    checkGameOverAction();//检测游戏结束
                }
                repaint();//重新调用paint()方法(重画)
            }
        }, interval, interval);//定时计划表
    }

    /**
     * 重写paint方法,在窗口中画图片
     * @param g:画笔
     */
    public void paint(Graphics g){//每10毫秒走一次
        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++){//遍历所有敌人
            Flyer f = enemies[i];//获取每一个敌人
            g.drawImage(f.getImage(),f.x,f.y,null);//画敌人
        }
        for (int i = 0; i<bt.length; i++){//遍历所有子弹
            Bullet b = bt[i];//获取所有子弹
            g.drawImage(b.getImage(),b.x,b.y,null);//画子弹
        }
        g.drawString("SCORE:"+score,10,25);//在窗口右上角画分数
        g.drawString("HP:"+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;
        }
    }

    /**
     * 主执行方法
     * @param args
     */
    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中使用,其他方法都不能访问,
 *      为了能在其他方法中也能访问这些引用,所以将引用设计在main外
 *
 * 2.问:为什么要单独创建action方法来测试?
 *   答:因为main方法时static的,在main方法中是无法访问引用的,
 *      所以需要单独创建非static的方法来测试
 *
 * 3.问:为什么在main中要先创建world对象,然后再调用action()方法?
 *   答:因为main方法是static的,再main中是无法调用action()方法的
 *      所以要先创建world对象,然后再调用action()方法
 */

小敌机类Airplane:

此类存储小敌机特有的属性及行为:
移动速度,分值,及图片的切换
继承超类,且实现得分接口

package cn.tedu.shoot;

import java.awt.image.BufferedImage;

/**
 * 小敌机
 */
public class Airplane extends Flyer implements EnemyScore{
    //    移动速度
   private int speed;

   public  Airplane(){
        super(66,89);
        speed = 2;//小敌机的下落速度
   }
    /**重写step方法(移动)*/
   public void  step(){
        y += speed;//y+表示向下
   }
    int index = 1;
    /**
     * 重写getImage()获取对象图片
     * @return
     */
    public BufferedImage getImage() {
       if (isLive()){//若活着 则返回airs[0]图片
           return Images.airs[0];
       }else if (isDead()){//若死了 则返回airs[1~4]图片
           BufferedImage img = Images.airs[index++];//获取爆破图
           if (index == Images.airs.length){//若index到了5 则表示到了最后一张
               state = REMOVE;//将当前状态修改为REMOVE删除的
           }
           return img;//返回爆炸图
           /*
             index = 1
             10M  isLive返回true 则 return返回airs[0]图片
             20M  isLive返回false 则 执行isDead返回true  img = airs[1]  index = 2                   返回airs[1]图片
             30M  isLive返回false 则 执行isDead返回true  img = airs[2]  index = 3                   返回airs[2]图片
             40M  isLive返回false 则 执行isDead返回true  img = airs[3]  index = 4                   返回airs[3]图片
             50M  isLive返回false 则 执行isDead返回true  img = airs[4]  index = 5 state修改为REMOVE  返回airs[4]图片
             60M  isLive返回false 则 执行isDead返回false return返回null空值(不返回图片)
             */
       }
       return null;
    }

    /**
     * 重写getScore()方法
     * @return:分值
     */
    public int getScore(){
        return 1;
    }
}

大敌机类BigAirplane:

大敌机与小敌机几乎无差别
同样要继承超类,且实现得分接口

package cn.tedu.shoot;

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

/**
 * 大敌机
 */
public class BigAirplane extends Flyer implements EnemyScore{
//    移动速度
   private int speed;

    public BigAirplane(){//初始化默认属性
        super(203,211);//图片宽,高
       speed = 2;//移动速度
    }

    /**重写step方法(移动)*/
    public void step(){
        y += speed;//y+表示直线向下移动
    }

    int index = 1;
    @Override
    public BufferedImage getImage() {
        if (isLive()){//若活着 则返回airs[0]图片
            return Images.bairs[0];
        }else if (isDead()){//若死了 则返回airs[1~4]图片
            BufferedImage img = Images.bairs[index++];//获取爆破图
            if (index == Images.bairs.length){//若index到了5 则表示到了最后一张
                state = REMOVE;//将当前状态修改为REMOVE删除的
            }
            return img;
        }
        return null;
    }

    /**
     * 重写getScore()方法
     * @return:分值
     */
    public int getScore(){
        return 3;
    }
}

小蜜蜂类Bee:

此类虽也可以算作敌人类,但是与小/大敌机有所不同,它是实现奖励值接口

package cn.tedu.shoot;

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

/**
 * 小蜜蜂
 */
public class Bee extends Flyer implements EnemyAward{
    //    x坐标移动速度,y坐标移动速度,
    private int xSpeed;//x坐标移动速度
    private int ySpeed;//y坐标移动速度
    private int awardType;//奖励类型

    public Bee(){//初始化属性
        super(48,50);//图片宽,高
        Random rand = new Random();
        awardType = rand.nextInt(2);//随机奖励值类型0~2之间(不包括2)0表示火力值,1表示生命值
        xSpeed = 1;//平行移动
        ySpeed = 2;//垂直移动
    }
    /**重写step方法(移动)*/
    public void step() {
        y += ySpeed;//y+:向下移动
        x += xSpeed;//x+:随机向左或是向右移动
        if (x <= 0 || x >= World.WIDTH - width) {
            xSpeed *= -1;//到达边界后反方向移动(正负为负,负负为正)
        }
    }

    int index = 1;
        public BufferedImage getImage() {
        if (isLive()){//若活着 则返回airs[0]图片
            return Images.bees[0];//返回小蜜蜂图
        }else if (isDead()){//若死了 则返回airs[1~4]图片
            BufferedImage img = Images.bees[index++];//获取爆破图
            if (index == Images.bees.length){//若index到了5 则表示到了最后一张
                state = REMOVE;//将当前状态修改为REMOVE删除的
            }
            return img;//返回爆炸图
        }
        return null;
    }

    /**
     * 重写getAwardType()方法
     * @return
     */
    public int getAwardType(){
        return awardType;//返回奖励类型
    }
}

天空类Sky:

这里有一点需要强调,就是为了实现天空图片向下移动后会出现移动过的位置出现图片丢失的情况,就使用了两张图上下拼接起来,当第某张天空图完全移出窗口的时候会让它重新出现在窗口上方继续向下移动

package cn.tedu.shoot;

import java.awt.image.BufferedImage;

/**
 * 天空
 */
public class Sky extends Flyer{
    //    移动速度,y1
    private int y1;//第二张图片的y坐标
    private int speed;//移动速度

    public Sky(){//设置初始值(默认值)
    //此处的宽高用常量是因为天空的宽高和窗口是一致的,x轴和y轴为若不为0就和窗口不匹配了
        super(World.WIDTH,World.HEIGHT,0,0);//初始化图片坐标及宽,高
        speed = 1;//初始化移动速度
        y1 = -World.HEIGHT;//第二张图片设置在第一张图片上方
    }
    /**重写step方法(移动)*/
    public void step(){
        y += speed;//第一张图向下移动
        y1 += speed;//第二张图向下移动
        if (y >= World.HEIGHT){//若y>=窗口的高
            y = -World.HEIGHT;//将移动出去的第一张天空挪到窗口上方
        }
        if (y1 >= World.HEIGHT){//若第二张天空挪出窗口
            y1 = -World.HEIGHT;//将第二张天空挪到窗口上方
        }
    }
    /**重写getImage()获取对象图片*/
    @Override
    public BufferedImage getImage() {//10毫秒走一次
        return Images.sky;//返回天空图片即可
    }

    /**
     * 获取y1坐标
     */
    public int getY1(){
        return y1;//返回y1
    }
}

英雄机类Hero:

package cn.tedu.shoot;

import java.awt.image.BufferedImage;
/**
 * 英雄机
 */
public class Hero extends Flyer {
    //    命数,火力值
    private int life;//命数
    private int fire;//火力

    /**
     * 初始化英雄机坐标机具体数据
      */
    public Hero() {
        super(97,139,140,400);//宽,高,及初始坐标
        fire = 0;//初始火力值   0:单倍火力
        life = 3;//初始生命值
    }
    /**重写step方法(移动)*/
    public void step(){//每10毫秒走一次
    //因为英雄机是跟随鼠标移动的,而鼠标是在窗口上的所以这里就没有写具体的方法,而是在窗口类中去用鼠标的具体坐标计算出英雄机的移动位置
    }

    int index = 0;//下标
    /**重写getImage()获取对象图片*/
    @Override
    public BufferedImage getImage() {//每10毫秒走一次
        return Images.heros[index++ % Images.heros.length];//heros[0]和heros[1]来回切换
        /*过程
                             index = 0
            10M 返回heros[0]  index = 1
            20M 返回heros[1]  index = 2
            30M 返回heros[0]  index = 3
            40M 返回heros[1]  index = 4
            50M 返回heros[0]  index = 5
            60M 返回heros[1]  index = 6
            ...........
         */
    }
    /**
     * 英雄机发射子弹(生成子弹对象)
     */
    public Bullet[] shoot(){
        int xStep = this.width/4;//子弹x坐标
        int yStep = 5;//子弹y坐标
        System.out.println(this.x+"\t"+this.y);
        if (fire>0){//双倍火力
            Bullet[] bs = new Bullet[3];//2发子弹
            bs[0] = new Bullet(this.x+1*xStep,this.y-yStep);//子弹坐标1
            bs[1] = new Bullet(this.x+3*xStep,this.y-yStep);//子弹坐标2
            bs[2] = new Bullet(this.x+2*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);//x:英雄机的x+2/4英雄机的宽,y:英雄机的y-
            return bs;
        }
    }

    /**
     * 英雄机移动
     */
    public void moveTo(int x,int y){//形参列表:鼠标的x坐标,y坐标
        this.x = x - this.width/2;//英雄机的x = 鼠标的x减1/2英雄机的宽
        this.y = y - this.height/2;//英雄机的y = 鼠标的y减1/2英雄机的高
    }
    /**
     * 英雄机增生命值
     */
    public void addLife(){
        life++;//生命值+1
    }

    /**
     * 获取英雄机生命值
     * @return
     */
    public int getLife(){
        return life;//返回生命值
    }

    /**
     *  英雄机减少生命值
     */
    public void subtractLife(){
        life--;//生命值减1
    }
    /**
     * 英雄机增火力值
     */
    public void  addFier(){
        fire += 40;//火力值+40
    }

    /**
     * 清空火力值
     */
    public void clearFier(){
        fire = 0;//火力值归零
    }
}

子弹类Bullet:

package cn.tedu.shoot;

import java.awt.image.BufferedImage;

/**
 * 子弹
 */
public class Bullet extends Flyer {
    //    移动速度
    private int speed;
    public Bullet(int x,int y) {//子弹有多个,每个子弹的初始坐标都不同,所以要写活
        super(8,20,x,y);
        speed = 3;//初始移动速度
    }
    /**重写step方法(移动)*/
    public void step(){
       y -= speed;//y-:表示直线向上移动
    }

    /**
     * 重写getImage()获取对象图片
     * @return
     */
    @Override
    public BufferedImage getImage() {//10毫秒走一次
        if (isLive()){//若活着则返回bullet图片
            return Images.bullet;
        }else if (isDead()){//若死了则将state修改为REMOVE
            state = REMOVE;
        }
            return null;//死了的和删除的都返回null空值
        /**
         * 若活着 则返回bullet图片
         * 若死了 则修改REMOVE 再返回空值
         * 若删除 则返回空值
         */
    }

    /**
     * 判断子弹是否越界
     * @return
     */
    public boolean isOutOfBounds(){
        return y <= -height;若子弹的y轴坐标小于自己的高则说明移动到了窗口外部
    }
}

奖励值接口 EnemyAward:

package cn.tedu.shoot;

/**
 * 奖励值接口
 */
public interface EnemyAward {
    public int FIRE = 0;//火力
    public int LIFE = 1;//生命值
    /**
     * 获取奖励值类型
     * @return
     */
    int getAwardType();
}

得分接口 EnemyScore:

package cn.tedu.shoot;
/*得分接口*/
public interface EnemyScore {
    /*得分*/
    public int getScore();
}

上一篇:java生成GIF图片


下一篇:基于spark-streaming实时推荐系统