Java飞机大战

FlyingObject,作为飞行物的父类,这里的飞行物指的就是敌机,小蜜蜂,子弹,英雄机

package com.tarena.shoot;
import java.awt.image.BufferedImage;

//飞行物类
public abstract class FlyingObject {
    protected BufferedImage image; //图片
    protected int width;           //宽
    protected int height;          //高
    protected int x;               //坐标
    protected int y;
    
    // 飞行物走一步
    public abstract void step();
    
    //检查是否出界,,返回true表示已越界
    public abstract boolean outOfBounds();
    
    public boolean shootBy(Bullet b) {
        return false;
    };
}

敌机类,图片取自ShootGame的入口类(在下面),打掉一个敌机得五分,敌机只能向下走
图片在主类 ShootGame中静态引入了

package com.tarena.shoot;
import java.util.Random;

//敌机
public class Airplane extends FlyingObject implements Enemy{
    private int speed = 2;  //走的步数
    
    public Airplane() {     // 全部都在构造方法中初始化数据
        image = ShootGame.airplane;
        width = image.getWidth();  //获取图片的宽
        height = image.getHeight(); //获取图片的高
        Random rand = new Random(); //随机数对象
        x = rand.nextInt(ShootGame.WIDTH-this.width+1);
        y = -this.height;
    }
    public int getScore() {
        return 5;  //打掉一个敌机得5分
    }
    
    // 重写
    public void step() {
        y+=speed;
    }
    
    // 重写是否越界函数---越界删除该对象
    public boolean outOfBounds() {
        if(y>ShootGame.HEIGHT) {
            return true;
        }else {
            return false;
        }
    }
    
    // 敌人被子弹射击----也就是检测图片与图片之间检测碰撞
    public boolean shootBy(Bullet bullet) {
        int x1 = this.x;
        int x2 = this.x + this.width;
        int y1 = this.y;
        int y2 = this.y + this.height;
        // x在 x1 和 x2 之间,y在y1和y2之间,既是碰撞了
        if(bullet.x<x2 && bullet.x>x1 && bullet.y<y2 && bullet.y>y1) {
            return true;
        }else {
            return false;
        }
    }
}

Bee,小蜜蜂类,也是敌人的一种,和上面敌机不同的是,小蜜蜂还会横着走,碰到边界反弹,打掉一个小蜜蜂还会获得奖励,也就是继承的接口 Award

package com.tarena.shoot;

import java.util.Random;

public class Bee extends FlyingObject implements Award{
    private int xSpeed = 1; //X坐标速度
    private int ySpeed = 2; //Y坐标速度
    private int awardType;  //奖励的类型 0/1
    
    public Bee() {
        image = ShootGame.bee;
        width = image.getWidth();
        height = image.getHeight();
        Random rand = new Random();
        x = rand.nextInt(ShootGame.WIDTH - this.width+1);
        y = -this.height;
        awardType = rand.nextInt(2); //0到1之间
        
        int xSymbol = rand.nextInt(2);
        if(xSymbol == 0) {
            xSpeed = -xSpeed;
        }
    }
    public int getType() {
        return awardType;   //返回奖励类型 0/1   在ShootGame 中根据0、1判断奖励的类型分别调用方法
    }
    
    // 重写
    public void step() {
        y+=ySpeed;
        x+=xSpeed;
        if(x>ShootGame.WIDTH-this.width || x<=0) {
            xSpeed = -xSpeed;
        }
    }
    
    // 重写是否越界函数
    public boolean outOfBounds() {
        if(y>ShootGame.HEIGHT) {
            return true;
        }else {
            return false;
        }
    }
    
    // 敌人被子弹射击
    public boolean shootBy(Bullet bullet) {
        int x1 = this.x;
        int x2 = this.x + this.width;
        int y1 = this.y;
        int y2 = this.y + this.height;
        // x在 x1 和 x2 之间,y在y1和y2之间,既是碰撞了
        if(bullet.x<x2 && bullet.x>x1 && bullet.y<y2 && bullet.y>y1) {
            return true;
        }else {
            return false;
        }
    }
    
} 

Award,,奖励,被小蜜蜂继承

package com.tarena.shoot;

public interface Award {
    public int DOUBLE_FILE = 0; //火力值
    public int LIFE = 1;        //命
//  获取奖励
    public int getType();
}

Enemy类,作为敌人(敌机+小蜜蜂)的公共接口,得分

package com.tarena.shoot;


public interface Enemy {
//  得分
    public int getScore();
}

主角英雄机,Hero,有动态效果,两张图片来回切换

package com.tarena.shoot;
import java.awt.image.BufferedImage;

//英雄机
public class Hero extends FlyingObject {
    private int life;   //命
    private int doubleFire=0;   //火力值
    private BufferedImage[] images; //两张图片切换
    private int index; //协助图片切换
    
    public Hero() {
        image = ShootGame.hero0;
        width = image.getWidth();  //获取图片的宽
        height = image.getHeight(); //获取图片的高
        x = 150;  //x:初始值
        y = 400;  //y:初始值
        life = 3; //默认3条命
        images = new BufferedImage[] {ShootGame.hero0,ShootGame.hero1};
        // 初始化images,两张图片
        index = 0; //协助切换
    }
    
    // 重写
    public void step() {
        index++;
        image = images[index/10%images.length];  //每100个毫秒切换一次
    }
    // 英雄机发射子弹
    public Bullet[] shoot() {
        int xStep = this.width/4;
        int yStep = 20;
        if(doubleFire > 0) {
            Bullet[] bs = new Bullet[2];
            bs[0] = new Bullet(this.x+1*xStep,this.y-yStep);
            bs[1] = new Bullet(this.x+3*xStep,this.y-yStep);
            doubleFire -=2; //发射一次双倍火力减2 
            return bs;
        }else {
            Bullet[] bs = new Bullet[1];
            bs[0] = new Bullet(this.x+2*xStep,this.y-yStep);
            return bs;
        }
        
    }
    // 英雄机随着鼠标移动
    public void moveTo(int x, int y) {
        this.x = x - this.width/2;  //使得英雄机中心对准在鼠标
        this.y = y - this.height/2;
    }
    
    // 重写是否越界函数
    public boolean outOfBounds() {
        return false;  //永不越界
    }
    
    // 加命
    public void addLife() {
        life++;
    }
    // 返回命
    public int getLife() {
        return life;
    }
    
    public void reduceLife() {
        life--;
    }
    
    // 加火力
    public void addDoubleFire() {
        doubleFire += 40;
    }
    public void zeroDoubleFire() {
        doubleFire = 0;
    }
    
    // 英雄机撞敌人,,true表示碰撞
    public boolean hit(FlyingObject obj) {
        int x1 = this.x - this.width/2;
        int x2 = this.x + this.width/2;
        int y1 = this.y - this.height/2;
        int y2 = this.y + this.height/2;
        if(obj.x<x2 && obj.x>x1 && obj.y<y2 && obj.y>y1) {
            return true;
        }else {
            return false;
        }
        
    }
}

Bullet类,子弹类,也是飞行物的一种,它的x,y都是跟随英雄机

package com.tarena.shoot;

import java.util.Random;

public class Bullet extends FlyingObject{
    private int speed = 3;
    
    public Bullet(int x,int y) {
        image = ShootGame.bullet;
        width = image.getWidth();  //获取图片的宽
        height = image.getHeight(); //获取图片的高
        Random rand = new Random(); //随机数对象
        this.x = x;  //x跟随英雄机
        this.y = y;  //y跟随英雄机
    }
    
    // 重写
    public void step() {
        y-=speed;
    }
    
    // 重写是否越界函数
    public boolean outOfBounds() {
        if(y<-this.height) {
            return true;
        }else {
            return false;
        }
    }
}

主类ShootGame,整个入口类,在这其中引入了所有的静态资源(图片),整个程序是用JFrame和JPanel来写的
检测鼠标事件,设定了一个定时器,画面是由定时器不断调用repaint方法刷新重画的

package com.tarena.shoot;
import java.awt.Component;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;

import java.util.Timer;
import java.util.TimerTask;

import java.util.Arrays;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import java.awt.Color;
import java.awt.Font;

//主程序类
public class ShootGame extends JPanel {
    public static final int WIDTH = 400;  //窗口宽
    public static final int HEIGHT = 654; //窗口高
    
    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;  //当前状态
    
    public static BufferedImage background;
    public static BufferedImage start;
    public static BufferedImage pause;
    public static BufferedImage gameover;
    public static BufferedImage airplane;
    public static BufferedImage bee;
    public static BufferedImage bullet;
    public static BufferedImage hero0;
    public static BufferedImage hero1;
    
    private Hero hero = new Hero();
    private FlyingObject[] flyings = {}; //敌人数组
    private Bullet[] bullets = {}; //子弹数组
    
    static { //初始化静态资源(图片)
        try {
            background = ImageIO.read(ShootGame.class.getResource("background.png"));
            start= ImageIO.read(ShootGame.class.getResource("start.png"));
            pause = ImageIO.read(ShootGame.class.getResource("pause.png"));
            gameover = ImageIO.read(ShootGame.class.getResource("gameover.png"));
            airplane = ImageIO.read(ShootGame.class.getResource("airplane.png"));
            bee = ImageIO.read(ShootGame.class.getResource("bee.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"));
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    // 随机生成敌人对象
    public FlyingObject nextOne() {
        Random rand = new Random();
        int type = rand.nextInt(2);
        if(type == 0) {
            return new Bee();
        }else {
            return new Airplane();
        }
    }
    
    
    // 敌人入场
    int flyEnteredIndex = 0;
    public void enterAction() {
        //生成敌人对象,将对象添加到flyings数组中
        flyEnteredIndex++;
        if(flyEnteredIndex%40==0) {
            FlyingObject one = nextOne();
            flyings = Arrays.copyOf(flyings, flyings.length+1);
            flyings[flyings.length-1] = one;
        }
    }
    // 飞行物走一步
    public void stepAction() {
        hero.step();
        for(int i=0; i<flyings.length; i++) {
            flyings[i].step();
        }
        for(int i=0; i<bullets.length; i++) {
            bullets[i].step();
        }
    }
    // 子弹入场---英雄机发射子弹
    int shootIndex = 0;
    public void shootAction() {
        shootIndex++;
        if(shootIndex%30 == 0) {
            Bullet[] bs = hero.shoot();
//          System.arraycopy(bs,0, bullets, bullets.length-bs.length, bs.length);
            bullets = Arrays.copyOf(bullets, bullets.length+bs.length);
            for(int i=0; i<bs.length; i++) {
                bullets[bullets.length-bs.length+i] = bs[i];
            }
        }
    }
    
    // 删除越界的敌人,,敌机,小蜜蜂,子弹
    public void outOfBoundsAction() {
        // 删除越界的敌机,小蜜蜂
        int index = 0;
        FlyingObject[] flyingLives = new FlyingObject[flyings.length];
        for(int i=0; i<flyings.length; i++) {
            if(!flyings[i].outOfBounds()) {
                flyingLives[index] = flyings[i];
                index++;
            }
        }
        flyings = Arrays.copyOf(flyingLives, index);
        
        // 删除越界的子弹
        index = 0;
        Bullet[] bulletLives = new Bullet[bullets.length];
        for(int i=0; i<bullets.length; i++) {
            if(!bullets[i].outOfBounds()) {
                bulletLives[index] = bullets[i];
                index++;
            }
        }
        bullets = Arrays.copyOf(bulletLives, index);
    }
    
    // 所有子弹与所有敌人(敌机+小蜜蜂)的碰撞
    public void bangAction() {
        for(int a=0; a<bullets.length; a++) {
            bang(a);
        }
    }
    // 一个子弹与所有敌人的碰撞
    int score = 0;
    public void bang(int a) {
        Bullet b = bullets[a];
        int bangPoint = -1;
        for(int i=0; i<flyings.length; i++) {
            if(flyings[i].shootBy(b)) {
                bangPoint = i;
                break;
            }
        }
        if(bangPoint != -1) {
            FlyingObject obj = flyings[bangPoint];
            if(obj instanceof Enemy) {
                Enemy one = (Enemy)obj;
                score += one.getScore();
            }
            if(obj instanceof Award) {
                Award two = (Award)obj;
                int type = two.getType();
                switch(type) {
                case Award.DOUBLE_FILE:
                    hero.addDoubleFire();
                    break;
                case Award.LIFE:
                    hero.addLife();
                    break;
                }
            }
            FlyingObject l = flyings[flyings.length-1];  //最后一个敌人(敌机,小蜜蜂)
            flyings[bangPoint] = l;                      //放到被撞物体的位置上
            flyings = Arrays.copyOf(flyings, flyings.length-1); //直接缩容
            
            Bullet ll = bullets[bullets.length-1];  //最后一个子弹
            bullets[a] = ll;                        //放到被撞子弹的位置上
            bullets = Arrays.copyOf(bullets, bullets.length-1); //直接缩容
        }
    }
    
    // 检查游戏是否结束
    public void checkGameOverAction() {
        for(int i=0; i<flyings.length; i++) {
            if(hero.hit(flyings[i])) {
                //碰撞
                hero.reduceLife();
                hero.zeroDoubleFire();
                if(hero.getLife() <= 0) {
                    state = GAME_OVER;
                }
                FlyingObject l = flyings[flyings.length-1];  //最后一个敌人(敌机,小蜜蜂)
                flyings[i] = l;                              //放到被撞物体的位置上
                flyings = Arrays.copyOf(flyings, flyings.length-1); //直接缩容
            }
        }
    }
    int num=0;
    public void action() {
        MouseAdapter l = new MouseAdapter() {
            // 鼠标移动事件
            public void mouseMoved(MouseEvent e) {
                if(state == RUNNING) {
                    hero.moveTo(e.getX(), e.getY());
                }
            }
            // 鼠标点击事件
            public void mouseClicked(MouseEvent e) {
                switch(state) { //根据不同的状态做不同的处理
                case START:  //启动状态时变为运行状态
                    state = RUNNING;
                    break;
                case GAME_OVER: //游戏结束状态时变为启动状态
                    score = 0;  //清空数据
                    hero = new Hero();
                    flyings = new FlyingObject[0];
                    bullets = new Bullet[0];
                    state = START;
                    break;
                }
            }
            //鼠标移入事件
            public void mouseEntered(MouseEvent e) {
                if(state == PAUSE) {
                    state = RUNNING;
                }
            }
            
            //鼠标移出事件
            public void mouseExited(MouseEvent e) {
                if(state == RUNNING) {
                    state = PAUSE;
                }
            }
        };
        this.addMouseListener(l); //处理鼠标操作事件
        this.addMouseMotionListener(l); //处理鼠标滑动事件
        
        Timer timer = new Timer(); //创建定时器对象
        int interval = 10; //时间间隔 (以毫秒为单位)
        timer.schedule(new TimerTask() {
            public void run() {
                if(state == RUNNING) {
                    enterAction();
                    stepAction();
                    shootAction();
                    outOfBoundsAction(); //删除越界的敌人
                    bangAction(); //子弹和敌人碰撞
                }
                checkGameOverAction();  //英雄机和敌人的碰撞
                repaint();
            }
        }, interval,interval);
    }

    
//  重写paint()  g:画笔
    public void paint(Graphics g) {
        g.drawImage(background,0, 0, null);
        paintHero(g);
        paintFlyingObjects(g);
        paintBullets(g);
        paintScoreandLife(g);
        paintState(g); //画状态
    }
    //画英雄机对象
    public void paintHero(Graphics g) {
        g.drawImage(hero.image, hero.x, hero.y,null);
    }
    //画敌人对象
    public void paintFlyingObjects(Graphics g) {
        for(int i=0; i<flyings.length; i++) {
            g.drawImage(flyings[i].image,flyings[i].x,flyings[i].y,null);           
        }
    }
    //画子弹对象
    public void paintBullets(Graphics g) {
        for(int i=0; i<bullets.length; i++) {
            g.drawImage(bullets[i].image, bullets[i].x, bullets[i].y, null);
        }
    }
    
    //画分和得命
    public void paintScoreandLife(Graphics g) {
        g.setColor(new Color(0xFF0000)); //设置画笔红色
        g.setFont(new Font(Font.SANS_SERIF, Font.BOLD,24)); //设置字体样式
        g.drawString("SCORE: "+score, 10, 25);
        g.drawString("LIFE: "+hero.getLife(), 10, 45);
    }
    
    public 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;
        }
    }
    
    public static void main(String[] args) {
        JFrame frame = new JFrame("Fly"); //窗口
        ShootGame game = new ShootGame(); //面板
        frame.add(game); //将面板添加到窗口上
        
        frame.setSize(WIDTH, HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true); //1.设置窗口可见,2.尽快调用paint()方法
        
        game.action(); //启动程序的执行
    }
}

看看效果图啊,玩起来也是不错的。。。。

这个是开始界面,鼠标点击事件触发开始游戏。。


pic1.png

游戏界面,计分,得命


pic2.png

鼠标移出框外时触发 mouseExited 事件,暂停
pic3.png

游戏结束


pic4.png

编程经验:

先看功能是否是某个对象的行为
    若是对象的行为,则写在相应的类中
    若不是对象的行为,则写在ShooGame中

整个代码:链接:https://pan.baidu.com/s/1LBlG2y3QsYvfmpDy9U4ajw 密码:kdmp
(JDK版本1.7,编辑器Eclipse)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 早餐:紫薯227g、胡萝卜30g、奇异果38g、鸡蛋一个、黑咖150ml。 总计 355Kcal 加...
    仅知阅读 150评论 0 0
  • 我这活着时的贱肉 总有蚊子喜欢一口 要是死了,还有土虫 会将你安慰成成骷髅
    吴生善阅读 158评论 0 1
  • 细节思辨法即是一种通过对细小之处的思考从而得出结论的思维方法。许多时候,我们在观察一个现象或者着手一件大事的时候,...
    未__央阅读 606评论 0 0