用Java实现简易的飞机大战---附源码和图片

先看演示

Java简易飞机大战

源码链接:(内置图片)

此项目源码链接【百度网盘】提取码:1234icon-default.png?t=M276https://pan.baidu.com/s/1EoBdg_47nYgLWgB6Zee9gg

一、基本介绍

1、创作背景:老师教完多线程后留的作业,主要是熟悉多线程,然后自己感兴趣就多多了一些功能,这个项目中没有用到接口相关的知识(创作的时候忘记了),感兴趣的可以自己改进

2、本人背景:大二在读学生,没有很深的java功底,如果有哪里需要改进的话还请指教

3、游戏大概内容:可选择是否双人游玩,随着分数的增高会增加玩家的攻击力和攻速,还有敌人的生命值和生成速度,有暂停功能

4、游戏需求分析:玩家可以灵活的操作飞机并发射子弹,敌人行进的速度不能相同(行进路线全是直行)

二、部分源码介绍

·1、主类

主要功能:创建一个JFrame和一个继承了JPanel类的GameJPanel对象,设置窗口的主要属性、添加按键监听

package playplane;


import javax.swing.*;

public class theWarOfPlane {

    public static final int WIDTH = 480;    //定义常量 窗口的宽
    public static final int HEIGHT = 750;   //定义常量 窗口的高

    public static void main(String[] args) {
        //定义主窗口和面板
        JFrame mainWindow = new JFrame("飞机大战/playplane");
        playplane.Global.gameJPanel=new GameJPanel();

        //给主窗口添加按键监听
        mainWindow.addKeyListener(new KeyListen());

        //加载这个工具类
        Global g=new Global();

        //布局为空才可以设置各组件和图片的位置
        playplane.Global.gameJPanel.setLayout(null);
        mainWindow.add(Global.gameJPanel);

        //主窗口的基础设置
        mainWindow.setSize(WIDTH, HEIGHT);                                      //设置窗口的长宽
        mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);     //设置窗口的关闭
        mainWindow.setLocationRelativeTo(null);                                 //窗口居中
        mainWindow.setVisible(true);                                            //窗口显示出来


    }


}

2、所有飞行物类的父类

主要功能:规定所有飞行物的标准,例如:玩家的飞机、敌人的飞机和子弹这些类都继承这个类,并拥有一些飞行物必须有的属性

部分属性介绍:

1、坐标:这个对象所处的位置(图片的左上角)

2、图片:存有图片的对象

3、宽高:为了后面计算两个图片是否相碰,所有初始化时由程序员输入这个图片的宽和高

4、生命:这个对象的生命值,子弹也有生命值,虽然目前生命值都为1,但是后期扩展这个游戏时可能添加特殊子弹

5、速度:图片移动的速度

6、阵营:方便识别友方、敌方、中立,例如:敌机碰到右方子弹才会消失

package playplane;



import java.awt.image.BufferedImage;

/**
 * 所有飞行物的父类
 */
public abstract class FlyingObject {
    //坐标
    private int x;
    private int y;
    //图片
    private BufferedImage img;
    //宽高
    private int width;
    private int height;
    //生命
    private int life;
    //速度
    private int speed;
    //阵营
    Camp camp;

    FlyingObject(int x, int y, BufferedImage img, int life, int speed, playplane.Camp camp) {
        this.img = img;
        //获取图片的宽和高用于判断子弹是否击中飞机
        this.width = img.getWidth();
        this.height = img.getHeight();
        this.x = x;
        this.y = y;
        this.life = life;
        this.speed=speed;
        this.camp=camp;
    }
    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }
    //移动
    public abstract void move();

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public BufferedImage getImg() {
        return img;
    }

    public void setImg(BufferedImage img) {
        this.img = img;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getLife() {
        return life;
    }

    public void setLife(int life) {
        this.life = life;
    }
}

3、子弹类

主要功能:生成子弹,在生成时指定这个子弹各方面的数值

属性介绍:

1、子弹方向:子弹生成时就决定它要行驶的方向,方向一开始有8种,后来为了增加游戏性,又加了两种,这里的wsad分别代表前后左右(玩过4399游戏的都懂)

方法介绍:

1、move():这是子弹的移动,有线程会不断的调用这个方法来移动子弹,仔细看会发现每次移动的距离就是它的速度

2、Injured():子弹受伤时调用这个方法,传入的num参数是收到的伤害数值(当时想的是也许后期有超级子弹呢,子弹生命比敌机还厚时,一个子弹就可以击毁多架飞机,所以子弹也可以受伤),Global类的介绍在下面

package playplane;



import java.awt.image.BufferedImage;

/**
 * 子弹类
 */
public class Bullet extends FlyingObject {

    //子弹方向
    BulletDirection direction;

    Bullet(int x, int y, BufferedImage img, int life, int speed, Camp camp, BulletDirection direction) {
        super(x, y, img, life, speed, camp);
        this.direction = direction;
    }

    @Override
    public void move() {
        //子弹移动的8种方向
        switch (direction) {
            case W -> setY(getY() - getSpeed());
            case S -> setY(getY() + getSpeed());
            case A -> setX(getX() - getSpeed());
            case D -> setX(getX() + getSpeed());
            case WA -> {
                setX(getX() - getSpeed() / 2);
                setY(getY() - getSpeed());
            }
            case WD -> {
                setX(getX() + getSpeed() / 2);
                setY(getY() - getSpeed());
            }
            case SA -> {
                setX(getX() - getSpeed() / 2);
                setY(getY() + getSpeed());
            }
            case SD -> {
                setX(getX() + getSpeed() / 2);
                setY(getY() + getSpeed());
            }
            case WWA -> {
                setX(getX() - getSpeed() / 3);
                setY(getY() - getSpeed());
            }
            case WWD -> {
                setX(getX() + getSpeed() / 3);
                setY(getY() - getSpeed());
            }
        }
    }

    /**
     * 收到伤害调用此方法
     */
    public void Injured(int num) {
        //当前生命减去伤害
        setLife(getLife() - num);
        //如果生命小于0
        if (getLife() <= 0) {
            //删除这个对象的存在
            Global.allFlying.remove(this);
        }
    }
}

4、玩家一的类

基本介绍:这里我把2个玩家写了2个类,其实没必要,写之前没有想清楚,所以大家写项目前一定先构思清楚

主要功能:规定玩家的一些基本属性,初始化玩家时,直接创建对象就行

属性介绍:

1、射击模式:根据分数的不断增长,射击模式会变化,例如:发射多行子弹

方法介绍:

1、move():方法开始会判断是否达到地图边界,如果达到了,就调用Global中的方法将玩家一的WSAD都变为false,这样就不会移动了。Global.W类型是boolean,有专门的按键监听,按下时这个会变为true,松开按键时会变为false,有线程会不断的执行move()这个方法,这样就可以实现8个方向的灵活移动了,如果用Scanner的话是实现不了灵活移动的(会有卡顿和延迟)。仔细看move()方法可以看见有8个方向的判断。这里我把2个玩家的方向属性(W、S、A、D)都放在了Global里,其实这样不好,当时没想清楚,这些可以是类中的属性,大家写的时候可以把这些都写进类里。

2、shoot():会有线程不断的调用这个方法,方法中判断玩家一的射击模式然后在它的指定方向上生成子弹,生成时就是new一个对象出来,可以规定属性。ShootModel是自己写的枚举类型

3、Injred():受伤时调用这个方法

package playplane;



import java.awt.image.BufferedImage;

public class HeroPlane1 extends FlyingObject {

    //射击模式
    private playplane.ShootModel shoot;

    //创建玩家飞机时,需要指定初始坐标,图片和生命
    HeroPlane1(int x, int y, BufferedImage img, int life, int speed, playplane.Camp camp) {
        super(x, y, img, life,speed,camp);
        //射击等级初始为1
        shoot= playplane.ShootModel.SHOOT1;
    }

    //受伤
    /**
     * 收到伤害调用此方法
     */
    public void Injured(int num){
        //当前生命减去伤害
        setLife(getLife()-num);
        //如果生命小于0
        if(getLife()<=0){
            //删除这个对象的存在
            playplane.Global.heroPlane1=null;
        }
    }

    //每个飞行物都有自己独特的移动方法
    @Override
    public void move(){
        if(playplane.Global.heroPlane1.getY()<=10)
            playplane.Global.W=false;
        if(playplane.Global.heroPlane1.getX()<=-20)
            playplane.Global.A=false;
        if(playplane.Global.heroPlane1.getX()>=400)
            playplane.Global.D=false;
        if(playplane.Global.heroPlane1.getY()>=600)
            playplane.Global.S=false;
        //上
        if(playplane.Global.W&&!playplane.Global.A&&!playplane.Global.S&&!playplane.Global.D){
            setY(getY()-getSpeed());
        }
        //下
        if(!playplane.Global.W&&!playplane.Global.A&& playplane.Global.S&&!playplane.Global.D){
            setY(getY()+getSpeed());
        }
        //左
        if(!playplane.Global.W&& playplane.Global.A&&!playplane.Global.S&&!playplane.Global.D){
            setX(getX()-getSpeed());
        }
        //右
        if(!playplane.Global.W&&!playplane.Global.A&&!playplane.Global.S&& playplane.Global.D){
            setX(getX()+getSpeed());
        }
        //左上
        if(playplane.Global.W&& playplane.Global.A&&!playplane.Global.S&&!playplane.Global.D){
            setX(getX()-getSpeed());
            setY(getY()-getSpeed());
        }
        //右上
        if(playplane.Global.W&&!playplane.Global.A&&!playplane.Global.S&& playplane.Global.D){
            setX(getX()+getSpeed());
            setY(getY()-getSpeed());
        }
        //左下
        if(!playplane.Global.W&& playplane.Global.A&& playplane.Global.S&&!playplane.Global.D){
            setX(getX()-getSpeed());
            setY(getY()+getSpeed());
        }
        //右下
        if(!playplane.Global.W&&!playplane.Global.A&& playplane.Global.S&& playplane.Global.D){
            setX(getX()+getSpeed());
            setY(getY()+getSpeed());
        }
    }

    //需要生成子弹时调用这个方法
    public void shoot(){
        switch(shoot){
            case SHOOT1:
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+45,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.W));
                break;
            case SHOOT2:
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+50,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.W));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+40,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.W));
                break;
            case SHOOT3:
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+45,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.W));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+40,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.WA));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+50,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.WD));
                break;
            case SHOOT4:
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+50,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.W));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+40,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.W));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+40,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.WA));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+50,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.WD));
                break;
            case SHOOT5:
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+50,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.W));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+40,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.W));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+40,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.WA));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+50,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.WD));
                playplane.Global.allFlying.add(new playplane.Bullet(getX()+40,getY()+10, playplane.Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.WWA));
                playplane.Global.allFlying.add(new Bullet(getX()+50,getY()+10, Global.bullet,1,10, playplane.Camp.FRIEND1, playplane.BulletDirection.WWD));

        }
    }

    public playplane.ShootModel getShoot() {
        return shoot;
    }

    public void setShoot(playplane.ShootModel shoot) {
        this.shoot = shoot;
    }


}

5、按键监听(重磅)

主要功能:这个按键监听添加在JFrame上,负责游戏的全局监听。会跟根据Global.state(游戏状态:准备、暂停、开始、结束).。这两个方法分别是按键按下时的监听和松开时的监听

准备状态:按下空格键将state转变为开始状态,然后调用GameJpanel.action()后线程就开始运行

开始状态:按下i时游戏变为暂停状态,所有线程在运行的同时都在不停的判断游戏是否时暂停状态,如果是的话就用wait()暂停线程。玩家一的方向是WSAD,玩家二是wsad(上面我也说过,这个我写的并不好,还望大家引以为戒)。玩家的攻击键分别是J和1

暂停状态:按o唤醒线程继续开始游戏。开始游戏时记得将方向键和射击键的判断变为false,不然可能一结束暂停飞机就乱跑,用的notifyAll()幻想所有线程

结束状态:所有玩家都阵亡了,按p会重新调用GameJpanel.action()初始化游戏,游戏就可以重新开始了

package playplane;


import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class KeyListen extends KeyAdapter {
    @Override
    public void keyPressed(KeyEvent e) {
        //如果游戏是准备状态
        if (playplane.Global.state == playplane.GameState.READY) {
            if (e.getKeyChar() == ' ') {
                GameJPanel.action();
                //游戏状态转为【开始】
                playplane.Global.state = playplane.GameState.START;
            }
        }
        //游戏是暂停状态
        if (playplane.Global.state == playplane.GameState.PAUSE) {
            if (e.getKeyChar() == 'o') {
                synchronized (playplane.Global.object) {
                    //方向键和攻击键状态清除
                    playplane.Global.clearDirection();
                    //唤醒所有线程
                    playplane.Global.object.notifyAll();
                    playplane.Global.state = playplane.GameState.START;
                }
            }
        }
        //如果游戏已经开始
        if (playplane.Global.state == playplane.GameState.START) {
            if (e.getKeyChar() == 'i') {
                playplane.Global.state= playplane.GameState.PAUSE;
            }
            switch (e.getKeyChar()) {
                case 'w' -> playplane.Global.W = true;
                case 's' -> playplane.Global.S = true;
                case 'a' -> playplane.Global.A = true;
                case 'd' -> playplane.Global.D = true;
                case 'j' -> playplane.Global.shoot1 = true;
                case '1' -> playplane.Global.shoot2 = true;
            }
            switch (e.getKeyCode()) {
                case KeyEvent.VK_LEFT:
                    playplane.Global.a = true;
                    break;
                case KeyEvent.VK_RIGHT:
                    playplane.Global.d = true;
                    break;
                case KeyEvent.VK_UP:
                    playplane.Global.w = true;
                    break;
                case KeyEvent.VK_DOWN:
                    playplane.Global.s = true;
                    break;
            }

        }
        if (playplane.Global.state == playplane.GameState.GAMEOVER) {
            if (e.getKeyChar() == 'p') {
                GameJPanel.action();
                playplane.Global.state = playplane.GameState.START;
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        //如果游戏已经开始
        if (playplane.Global.state == GameState.START) {
            switch (e.getKeyChar()) {
                case 'w' -> playplane.Global.W = false;
                case 's' -> playplane.Global.S = false;
                case 'a' -> playplane.Global.A = false;
                case 'd' -> playplane.Global.D = false;
                case 'j' -> playplane.Global.shoot1 = false;
                case '1' -> playplane.Global.shoot2 = false;
            }

            switch (e.getKeyCode()) {
                case KeyEvent.VK_LEFT:
                    playplane.Global.a = false;
                    break;
                case KeyEvent.VK_RIGHT:
                    playplane.Global.d = false;
                    break;
                case KeyEvent.VK_UP:
                    playplane.Global.w = false;
                    break;
                case KeyEvent.VK_DOWN:
                    Global.s = false;
                    break;
            }

        }

    }
}

6、GameJPanel(游戏的主面板)

主要功能:这个类继承了JPanle类,主要为了重写paint()方法,重写后调用repaint()方法就会重绘面板了

方法介绍:

1、action()和chushihua():action()中会调用chushihua(),chushihua()主要负责重新初始化一些游戏属性,例如存飞行物的allFlying(ArrayLIst类型),游戏分数,敌人生成间隔,敌人初始生命值,玩家的攻速和攻击力都进行初始化。action()中会重新new一个玩家出来和新的线程。

2、paint():里面的内容就是面板上绘画的内容。根据游戏内容会绘画上不同的界面例如暂停和结束都会显示指定的界面。因为绘画的东西太多所以写在了不同的方法里,因为都继承了FlyingObject类所以绘画的时候有具体的X和Y坐标

package playplane;


import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;

public class GameJPanel extends JPanel {
    static void chushihua() {
        //飞行物品刷新
        playplane.Global.allFlying = new ArrayList<>();
        //清除之前的方向操作
        playplane.Global.clearDirection();
        //分数清零
        playplane.Global.score = 0;
        //敌人属性初始化
        if (playplane.Global.doublePerson)
            playplane.Global.productEnemyTime = 800;
        else
            playplane.Global.productEnemyTime = 1200;
        playplane.Global.enemySmallLife = 4;
        playplane.Global.enemyBigLife = 6;
        //玩家射速攻击力初始化
        playplane.Global.playerShootTIme1 = 250;
        playplane.Global.playerShootTIme2 = 250;
        playplane.Global.playerDamage1 = 1;
        playplane.Global.playerDamage2 = 1;
    }

    /**
     * 调用此方法,游戏开始
     */
    public static void action() {
        //所有属性初始话
        chushihua();
        //创建玩家飞机
        playplane.Global.heroPlane1 = new HeroPlane1(80, 480, playplane.Global.playerPlane1, 3, 5, playplane.Camp.FRIEND1);
        //如果有第二个玩家
        if (playplane.Global.doublePerson) {
            playplane.Global.heroPlane2 = new HeroPlane2(250, 480, playplane.Global.playerPlane2, 3, 5, playplane.Camp.FRIEND2);
            //生成敌人时间间隔从1200->800
            playplane.Global.productEnemyTime = 800;
        }

        playplane.Global.t = new Time();
        playplane.Global.t.start();

        playplane.Global.m = new MainThread();
        playplane.Global.m.start();

        playplane.Global.shootThread1 = new ShootThread1();
        playplane.Global.shootThread1.start();
        playplane.Global.shootThread2 = new ShootThread2();
        playplane.Global.shootThread2.start();

        playplane.Global.e = new EnemyProduct();
        playplane.Global.e.start();


    }

    @Override
    public void paint(Graphics g) {
        if (Global.state == GameState.GAMEOVER) {
            paintOver(g);
            return;
        }
        //如果游戏暂停
        if (Global.state == GameState.PAUSE) {
            paintPause(g);
            return;
        }
        //清空之前绘图
        super.paint(g);
        //绘制背景
        paintbackground(g);
        //绘制玩家飞机
        paintHeroPlane(g);
        //绘制除玩家飞机外的所有飞行物
        paintAllFlying(g);

        //设置字体格式
        Font font = new Font("幼圆", 1, 20);
        g.setFont(font);
        //左上角显示信息
        g.drawString("时间:" + Global.timeText, 10, 20);
        g.drawString("分数:" + Global.score, 10, 40);
        if (Global.heroPlane1 != null) {
            g.drawString("玩家一生命:" + playplane.Global.heroPlane1.getLife(), 10, 60);
            g.drawString("玩家一攻速:" + 1000 / playplane.Global.playerShootTIme1, 310, 20);
            g.drawString("玩家一攻击力:" + playplane.Global.playerDamage1, 310, 40);
        }
        if (playplane.Global.heroPlane2 != null) {
            g.drawString("玩家二生命:" + playplane.Global.heroPlane2.getLife(), 10, 80);
            g.drawString("玩家二攻速:" + 1000 / playplane.Global.playerShootTIme2, 310, 60);
            g.drawString("玩家二攻击力:" + playplane.Global.playerDamage2, 310, 80);
        }
        if (playplane.Global.state == GameState.START) {
            g.drawString("小飞机生命值:" + playplane.Global.enemySmallLife, 300, 100);
            g.drawString("大飞机生命值:" + playplane.Global.enemyBigLife, 300, 120);
        }


    }

    //绘制背景
    void paintbackground(Graphics g) {
        g.drawImage(playplane.Global.background, 0, 0, this);
    }

    //绘制暂停界面
    void paintPause(Graphics g) {
        g.drawImage(playplane.Global.backgroundPause, 0, 0, this);
    }

    //绘制游戏结束界面
    void paintOver(Graphics g) {
        g.drawImage(playplane.Global.backgroundOver, 0, 0, this);
    }

    //绘制玩家飞机
    void paintHeroPlane(Graphics g) {
        if (playplane.Global.heroPlane1 != null) {
            g.drawImage(playplane.Global.heroPlane1.getImg(), playplane.Global.heroPlane1.getX(), playplane.Global.heroPlane1.getY(), this);
        }
        if (playplane.Global.heroPlane2 != null) {
            g.drawImage(playplane.Global.heroPlane2.getImg(), playplane.Global.heroPlane2.getX(), playplane.Global.heroPlane2.getY(), this);
        }
    }

    //绘制所有除玩家外的飞行物
    void paintAllFlying(Graphics g) {
        if (playplane.Global.allFlying.size() != 0) {
            for (int i = 0; i < playplane.Global.allFlying.size(); i++) {
                FlyingObject f = Global.allFlying.get(i);
                if (f != null)
                    g.drawImage(f.getImg(), f.getX(), f.getY(), this);
            }
        }
    }

}

7、Global(工具类)

主要功能:提供两个图片是否相交的方法,加载图片到内存中,提供全局变量,从配置文件中读取信息

static代码块:负责加载图片到内存中,加载配置文件中的信息

package playplane;


import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Objects;
import java.util.ResourceBundle;

/**
 * 工具类
 * 存储所有全局变量,方便各个类之间使用
 */
public class Global {

    //玩家飞机
    static HeroPlane1 heroPlane1 = null;
    static HeroPlane2 heroPlane2 = null;

    //游玩时间
    static String time = "";

    //游戏主面板
    static GameJPanel gameJPanel;

    //分数
    static int score = 0;

    //显示时间和分数的文本框
    static String timeText = "--:--";

    //各种图片
    static BufferedImage playerPlane1;
    static BufferedImage playerPlane2;
    static BufferedImage enemyPlaneSmall;
    static BufferedImage enemyPlaneBig;
    static BufferedImage bullet;
    static BufferedImage background;
    static BufferedImage backgroundPause;
    static BufferedImage backgroundOver;


    //游戏是否已开始
    static GameState state = GameState.READY;

    //存储所有敌对飞行物
    static ArrayList<FlyingObject> allFlying = new ArrayList<>();

    //玩家发射子弹的间隔,默认是250
    static int playerShootTIme1 = 250;
    static int playerShootTIme2 = 250;
    //玩家初始伤害
    static int playerDamage1 = 1;
    static int playerDamage2 = 1;

    //是否是双人游戏
    static boolean doublePerson = true;

    static final Object object = new Object();

    //敌人生成间隔
    static int productEnemyTime = 1200;

    //敌人生命值
    static int enemySmallLife = 4;
    static int enemyBigLife = 6;

    //各种线程
    static Time t;
    static MainThread m;
    static ShootThread1 shootThread1;
    static ShootThread2 shootThread2;
    static EnemyProduct e;

    //玩家一的方向键状态是否按下
    static boolean W = false;
    static boolean S = false;
    static boolean A = false;
    static boolean D = false;
    static boolean w = false;
    static boolean s = false;
    static boolean a = false;
    static boolean d = false;
    //玩家是否处于发射状态
    static boolean shoot1 = false;
    static boolean shoot2 = false;

    //音乐文件
    static File file1 = new File("../music_plane/flight.wav");
    static File file2 = new File("../music_plane/菜单长.wav");
    static File file3 = new File("../music_plane/菜单短.wav");


    {
        //加载所有图片到内存中
        try {
            InputStream i1 = this.getClass().getResourceAsStream("img_plane/Hero1.png");
            InputStream i2 = this.getClass().getResourceAsStream("img_plane/Hero2.png");
            InputStream i3 = this.getClass().getResourceAsStream("img_plane/airplane.png");
            InputStream i4 = this.getClass().getResourceAsStream("img_plane/bigplane.png");
            InputStream i5 = this.getClass().getResourceAsStream("img_plane/bullet.png");
            InputStream i6 = this.getClass().getResourceAsStream("img_plane/background.png");
            InputStream i7 = this.getClass().getResourceAsStream("img_plane/pause.png");
            InputStream i8 = this.getClass().getResourceAsStream("img_plane/gameover.png");
            if (i1 != null)
                playerPlane1 = ImageIO.read(i1);
            if (i2 != null)
                playerPlane2 = ImageIO.read(i2);
            if (i3 != null)
                enemyPlaneSmall = ImageIO.read(i3);
            if (i4 != null)
                enemyPlaneBig = ImageIO.read(i4);
            if (i5 != null)
                bullet = ImageIO.read(i5);
            if (i6 != null)
                background = ImageIO.read(i6);
            if (i7 != null)
                backgroundPause = ImageIO.read(i7);
            if (i8 != null)
                backgroundOver = ImageIO.read(i8);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //使用资源绑定器绑定属性配置文件
        ResourceBundle bundle = ResourceBundle.getBundle("playplane_basic_information");
        //读取配置文件中的信息
        String str1 = bundle.getString("whether_Doueble_Player");
        doublePerson = Boolean.parseBoolean(str1);
    }

    /**
     * 清楚之前按键的操作
     */
    static void clearDirection() {
        W = false;
        S = false;
        A = false;
        D = false;
        shoot1 = false;
        shoot2 = false;
        w = false;
        s = false;
        a = false;
        d = false;
    }

    /**
     * 判断两个飞行物是否相交
     * 相交返回true
     *
     * @param f1
     * @param f2
     * @return
     */
    static boolean pictureIntersection(FlyingObject f1, FlyingObject f2) {
        int x01, x02, y01, y02, x11, x12, y11, y12;

        x01 = f1.getX();
        x02 = f1.getX() + f1.getWidth();
        y01 = f1.getY();
        y02 = f1.getY() + f1.getHeight();

        x11 = f2.getX();
        x12 = f2.getX() + f2.getWidth();
        y11 = f2.getY();
        y12 = f2.getY() + f2.getHeight();

        int zx = Math.abs(x01 + x02 - x11 - x12);
        int x = Math.abs(x01 - x02) + Math.abs(x11 - x12);
        int zy = Math.abs(y01 + y02 - y11 - y12);
        int y = Math.abs(y01 - y02) + Math.abs(y11 - y12);
        if (zx <= x && zy <= y)
            return true;
        return false;
    }

}

结尾

我把所有线程都写在一个java文件中了,所以这里就不展示了,想看的话可以下载原文件。

此文章仅供学习分享,禁止未授权转载!

本人也觉得项目的结构不够好,请大家引以为戒,在写项目前先构思好

package cn.feike.shoot; import java.awt.Graphics; import java.awt.image.BufferedImage; public abstract class FlyingObject { protected double x;//物体的x坐标 protected double y;//物体的y坐标 protected double width;//物体的宽 protected double heigth;//物体的高 protected BufferedImage image;//当前正在显示的图片 protected int index = 0;//图片数组下标序号,子类中使用 protected double step;//飞行物每次(1/24秒)移动的距离 protected int life;//命 protected int state;//飞行物的状态 public static final int ACTIVE=0;//活着状态 public static final int DEAD=1;//死亡状态 public static final int REMOVE=2;//回收状态 //默认构造器 public FlyingObject() { life = 1; state = ACTIVE; } //有参构造器 public FlyingObject(double width,double heigth){ this();//调用无参数的构造器,必须写在第一行. this.x = (int)(Math.random()*(480-width)); this.y = -heigth; this.width = width; this.heigth = heigth; step = Math.random()*3+0.8;//初始化step为[0.8,3.8)之间的数 } //重写toString方法 public String toString() { return x+","+y+","+width+","+heigth+","+image; } //重写paint,方便子类对象的使用 public void paint(Graphics g) { g.drawImage(image, (int)x, (int)y, null);//绘制图片 } //飞行物移动的move方法 /** * 重构了move,方法实现播放销毁动画功能 */ public void move(){ if(state == ACTIVE){ y += step; return ; } if(state == DEAD){ //从子类对象中获取下一张照片 BufferedImage img = nextImage(); if(img == null){ state = REMOVE;//没有照片则回收 }else{ image = img;//否则把子类的图片传给image } //越界则销毁 if(y>=825){ state = REMOVE; } } } /** * 子类中必须有的方法,返回下一个要播放的照片引用, * 如果返回null表示没有可播放的照片了. */ protected abstract BufferedImage nextImage(); /** * 飞行物被打了一下 */ public void hit(){ if(life>0){ life--; } if(life==0){ state = DEAD; } } /** * 碰撞检测的方法 * 检测物体的位置是否在碰撞的范围内. * (子弹是否在飞行物的碰撞范围内) */ public boolean duang(FlyingObject obj){ //this(x,y,w,h) //obj(x,y,w,h) double x1 = this.x - obj.width; double x2 = this.x + this.width; double y1 = this.y - obj.width; double y2 = this.y + this.heigth; return x1<obj.x&&obj;.x<x2&&y1;<obj.y&&obj;.y<y2; } /** 重构FlyingObject,添加了状态检查方法 */ /** 检查飞行物死了吗 */ public boolean isDead(){ return state == DEAD; } /** 检查飞行物是否活动的 */ public boolean isActive(){ return state == ACTIVE; } /** 检查飞行是否可以被删除*/ public boolean canRemove(){ return state == REMOVE; } /** 飞行物添加"去死"方法*/ public void goDead(){ if(isActive()){ state = DEAD; } } }
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值