java:飞机大战

飞机大战

author:晚安早睡

1.项目分析

1.1项目概述

本项目旨在开发一款经典的飞机大战游戏,玩家将通过鼠标控制一架战斗机在空中与敌方飞机进行战斗。游戏的核心玩法包括射击敌人、躲避障碍物以及收集奖励。击败小boss和大boss则获得游戏的胜利

1.2需求分析

1.游戏未开始的画面

​ 游戏未开始时设定为静止画面,画面上有静态的游戏背景图片,boss,我方飞机,在我方飞机和敌方boss之间有一个爆炸的画面,画面中间有一行文字“鼠标右键开始游戏”。

2.游戏中的画面

​ 游戏开始后我方飞机有鼠标移动控制飞机移动,同时不断发射子弹,在窗口的左上方绘制得分面板,打败敌方飞机后会获得不同的分数。在窗口的上方不断产生两种敌方小飞机,一种能发射子弹,一种不能发射子弹,能发射子弹的敌方飞机产生的频率要慢于不能产生子弹的敌方小飞机,产生子弹的敌方小飞机血量高于不能产生子弹的小飞机。在一定的时间后窗口会出现两个小boss,一个从屏幕的左边出现,一个从屏幕的上方出现,都能发射子弹,但子弹都能被我方飞机子弹抵消,上方出现的小boss子弹有一定的追踪功能,小boss都有一定的血量,在死后会掉落物品,我方飞机碰到物品后子弹会升级,伤害提高。在其中任意一个小boss死后,且到达一定时间,会出现一个警告画面,随后敌方大boss会从窗口上方出现,到达一定距离开始水平移动,且在大boss出现后,会在屏幕上方绘制大boss血条。

3.游戏结束画面

​ 大boss死后,游戏获得胜利,在窗口中绘制游戏胜利的文字。

4.游戏暂停

​ 在窗口中添加键盘监听,按下空格游戏就会进入暂停状态,在窗口中绘制游戏暂停的文字。

2.初始化界面

使用init()方法设置界面,设置窗口宽为512px,高为768px,设置窗口不可放缩

public void init(){
        this.setTitle("飞机大战");
        this.setVisible(true);
        this.setSize(WIDTH,HEIGHT);
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
//        设置窗口放缩
        this.setResizable(flase);
//        加载程序图标
        try {
            Image imageicon= ImageIO.read(new File("images/UI图标.png"));
            this.setIconImage(imageicon);
        }catch (IOException e){
            e.printStackTrace();
        }
}

3.获取游戏中的图片

创建一个GameUtil类用来存放游戏中的图片资源,其中使用ToolKit类中的静态方法getDeafaultToolKit返回ToolKit的子类实例化对象,然后再调用静态方法getImage获取图片资源

public class GameUtils {
    //    获取背景图片
        public static Image bgImg=Toolkit.getDefaultToolkit().getImage("images/bg1.png");
    //    获取boss图片
        public static Image bossImg=Toolkit.getDefaultToolkit().getImage("images/boss1.png");
    //    获取爆炸图片
        public static Image explodeImg=Toolkit.getDefaultToolkit().getImage("images/explod1.png");
    //    获取我方飞机图片
        public static Image myPlaneImg=Toolkit.getDefaultToolkit().getImage("images/myplane1.png");
    //    获取我方子弹图片
        public static Image shell1Img=Toolkit.getDefaultToolkit().getImage("images/shell1.png");
        public static Image shell2Img=Toolkit.getDefaultToolkit().getImage("images/shell2.png");
        public static Image shell3Img=Toolkit.getDefaultToolkit().getImage("images/shell3.png");
    //    获取小飞机图片
        public static Image enemySmallPlane1=Toolkit.getDefaultToolkit().getImage("images/enemysmallplane1.png");
    //    获取大飞机图片
        public static Image enemyBigPlane1=Toolkit.getDefaultToolkit().getImage("images/enemybigplane1.png");
    //    获取敌方大飞机子弹
        public static Image enemyBulletBigPlane1=Toolkit.getDefaultToolkit().getImage("images/enemybulletbigplane.png");
    //    获取小boss图片
        public static Image littleBoss1=Toolkit.getDefaultToolkit().getImage("images/littleboss1.png");
        public static Image littleBoss2=Toolkit.getDefaultToolkit().getImage("images/littleboss2.png");
    //    获取小boss子弹图片
        public static Image littleBoss1Bullet=Toolkit.getDefaultToolkit().getImage("images/littleboss1bullet.png");
        public static Image littleBoss2Bullet=Toolkit.getDefaultToolkit().getImage("images/littleboss2bullet.png");
    //    获取补给的图片
        public static Image giftImg=Toolkit.getDefaultToolkit().getImage("images/gift1.png");
    //    获取大boss子弹图片
        public static Image boss1BulletImg=Toolkit.getDefaultToolkit().getImage("images/boss1bullet.png");
    //    获取警示图片
        public static Image warningImg=Toolkit.getDefaultToolkit().getImage("images/warning.gif");

    }

2.1添加游戏中重复出现的元素

将以下代码添加到GameUtil这个类中,这段代码中声明了一些静态的列表,之所以不使用数组存储,是因为列表的长度会动态变化,符合游戏元素的个数不断变化的特性,声明成静态的,方便通过类名直接调用,其中包括敌方小飞机,敌方子弹,我方子弹这样一些会重复出现的游戏元素,创建gameObjList存放所有元素的对象,将创建的所有游戏元素列表都添加到gameObjList这个列表中,这里为什么已经创建了每种游戏元素列表,又重复创建一个列表将元素添加到这个列表呢,后面可以通过循环该列表中的每一个元素,进行绘制,而不用分别对每个游戏元素列表进行遍历绘制,是为了绘制游戏元素的时候减少代码的书写。

//    创建我方子弹集合
    public static List<Shell1Obj> shell1ObjList=new ArrayList<>();
    public static List<Shell2Obj> shell2ObjList=new ArrayList<>();
    public static List<Shell3Obj> shell3ObjList=new ArrayList<>();
//    创建所有元素的集合
    public static List<GameObj> gameObjList=new ArrayList<>();
//    创建敌方小飞机的集合
    public static List<EnemySmallObj> enemySmallObjList=new ArrayList<>();
//    创建敌方大飞机的集合
    public static List<EnemyBigObj> enemyBigObjList=new ArrayList<>();
//    创建敌方大飞机子弹集合
    public static List<EnemyBigBulletObj> enemyBigBulletObjList =new ArrayList<>();
//    创建敌方小boss子弹集合
    public static List<LittleBoss1BulletObj> littleBoss1BulletObjList =new ArrayList<>();
    public static List<LittleBoss2BulletObj> littleBoss2BulletObjList=new ArrayList<>();
//    创建爆炸集合
    public static List<ExplodeObj> explodeObjList=new ArrayList<>();
//    创建补给集合
    public static List<GiftObj> giftObjList=new ArrayList<>();
//    创建boss1子弹集合
    public static List<Boss1BulletObj> boss1BulletObjList=new ArrayList<>();
//    创建警示集合
    public static List<WarningObj> warningObjList=new ArrayList<>();

//    移除物体对象结合
    public static List<GameObj> removeList=new ArrayList<>();

2.2封装一个绘制文字的方法

在GameUtil类中声明drawWord方法用来绘制文字,减少相同代码的重复。

//    这个方法用来绘制文字
    public static void drawWord(Graphics gImage,String string,Color color,int size,int x,int y){
        gImage.setColor(color);
        gImage.setFont(new Font("楷体",Font.BOLD,size));
        gImage.drawString(string,x,y);
    }

4.创建游戏中的所有元素的父类

创建父类GameObj,其中包含游戏元素的必须属性,包括图片,宽,高,速度,坐标(单位是像素),窗口类(方便后续添加监听),一些构造方法方便子类对象初始化,一个paintSelf()方法用于绘制自身, 还有一个getRec()方法返回的是Rectangle类的实例化对象,该方法用来检测游戏中元素的碰撞

//所有游戏元素的父类
public class GameObj {
//    物体对象的图片
    Image img;
//    物体的宽
    int width;
//    物体的高
    int height;
//    物体的水平位置
    int x;
//    物体的垂直位置
    int y;
//    物体的速度
    int speed;
//    窗口类
    GameWin frame;

    public GameObj() {
    }

    public GameObj(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public GameObj(Image img, int x, int y, int speed) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.speed = speed;
    }

    public GameObj(Image img, int width, int height, int x, int y) {
        this.img = img;
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
    }

    public GameObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        this.img = img;
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
        this.speed = speed;
        this.frame = frame;
    }

    public Image getImg() {
        return img;
    }

    public void setImg(Image 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 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 int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public GameWin getFrame() {
        return frame;
    }

    public void setFrame(GameWin frame) {
        this.frame = frame;
    }
    public void paintSelf(Graphics g){
        g.drawImage(img,x,y,null);
    }
    public Rectangle getRec(){
        return new Rectangle(x,y,width,height);
    }
}

3.1创建PlaneObj类

继承GameObj这个父类(如没有说明以下的每一个类都继承GameObj这个父类),飞机的初始化需要有宽,高,速度,位置,需要重写paintSelf()用于绘制飞机的移动和getRec()方法用于检测飞机的碰撞

public class PlaneObj extends GameObj{
    public PlaneObj() {
        super();
    }
    public PlaneObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }
    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
       
}

3.2创建BgObj类

加载背景图片的时候,图片有宽,高,位置,所有可以创建一个背景对象类用来初始化背景图片

public class Bgobj extends GameObj{
    public Bgobj() {
        super();
    }

    public Bgobj(Image img, int x, int y, int speed) {
        super(img, x, y, speed);
    }

    public Bgobj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.3创建Explodebj类

爆炸图片是一组图片组成的动图,所以定义一个Image类型的数组,使用for循环将每一帧图片加载到Image数组中,在paintSelf()方法中使用一个变量explodeCount来确定数组中的图片是否都被绘制

public class ExplodeObj extends GameObj{
//    定义一个image的静态数组存放一串爆炸图片
    static Image[] explodePic=new Image[27];
//    定义变量记录图片加载的次数
    int explodeCount=0;
    static {
        for (int i = 0; i < explodePic.length; i++) {
            explodePic[i]=Toolkit.getDefaultToolkit().getImage("images/explode/"+i+".jpg");

        }
    }

    public ExplodeObj(int x, int y) {

        super(x, y);
    }

    @Override
    public void paintSelf(Graphics g) {
        if(explodeCount<27){
            super.img=explodePic[explodeCount];
            super.paintSelf(g);
            explodeCount++;
        }
    }
}

3.4创建Shell1Obl类

声明游戏中的我方飞机一级子弹对象,包括宽,高,速度,位置,用来创建我方飞机的一级子弹,在paintSelf()方法中通过改变子弹的y坐标来实现子弹的移动

public class Shell1Obj extends GameObj{
    public Shell1Obj() {
        super();
    }

    public Shell1Obj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
		public class Shell1Obj extends GameObj{
    public Shell1Obj() {
        super();
    }

    public Shell1Obj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
//        通过改变y值移动子弹
        y-=speed;
    }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}


    }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.5创建Shell2Obj类

声明游戏中的我方飞机二级子弹对象,包括宽,高,速度,位置,用来创建我方飞机的二级子弹,在paintSelf()方法中通过改变子弹的y坐标来实现子弹的移动

public class Shell2Obj extends GameObj{
    public Shell2Obj() {
        super();
    }

    public Shell2Obj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        y-=speed;
    }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.6创建Shell3Obj类

声明游戏中的我方飞机三级子弹对象,包括宽,高,速度,位置,用来创建我方飞机的三级子弹,在paintSelf()方法中通过改变子弹的y坐标来实现子弹的移动

public class Shell3Obj extends GameObj{
    public Shell3Obj() {
        super();
    }

    public Shell3Obj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        y-=speed;
    }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.7创建EnemySmallObj类

创建游戏中的小飞机对象,包括宽,高,速度,位置,用来初始化小飞机,通过改变y坐标的值实现小飞机的移动

public class EnemySmallObj extends GameObj{
    public EnemySmallObj() {
        super();
    }

    public EnemySmallObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
  		y+=speed;
    }  

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.8创建EnemyBigObj类

创建游戏中的大飞机对象,包括宽,高,速度,位置,用来初始化大飞机

public class EnemyBigObj extends GameObj{
 
    public EnemyBigObj() {
        super();
    }

    public EnemyBigObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.9创建EnemyBigBulletObj类

创建游戏中的大飞机子弹对象,包括宽,高,速度,位置,用来创建大飞机的子弹,在paintSelf()方法中通过改变子弹的y坐标来实现子弹的移动

public class EnemyBigBulletObj extends GameObj{
    public EnemyBigBulletObj() {
        super();
    }

    public EnemyBigBulletObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        y+=speed;
    }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.10创建LittleBoss1类

声明游戏中的小boss1对象,包括宽,高,速度,位置,声明一个静态变量blood用来存放小boss1的血量,用来初始化小boss1,在paintSelf()方法中通过改变小boss1的x坐标来实现小boss1的移动,其中加入if判断语句在适当的时候对速度取反,是使小boss1在y坐标不变的情况下,水平来回移动

public class LittleBoss1 extends GameObj{
//      boss1的血量
    public static int blood=10;
    public LittleBoss1() {
        super();
    }

    public LittleBoss1(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        x+=speed;
        if(x>400){
            speed=-speed
        }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.11创建LittleBoss1BulletObj类

创建游戏中的小boss1子弹对象,包括宽,高,速度,位置,用来创建小boss1的子弹,在paintSelf()方法中通过改变子弹的y坐标来实现子弹的移动

public class LittleBoss1BulletObj extends GameObj{
    public LittleBoss1BulletObj() {
        super();
    }

    public LittleBoss1BulletObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        this.y+=speed;


    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.12创建LittleBoss2类

声明游戏中的小boss2对象,包括宽,高,速度,位置,声明一个静态变量blood用来存放小boss2的血量,用来初始化小boss2,在paintSelf()方法中通过改变小boss2的x和y坐标来实现小boss2的移动,其中加入if判断语句在适当的时候对速度取反,使小boss2垂直,水平移动

public class LittleBoss2 extends GameObj{
    public static int blood=10;
    public LittleBoss2() {
        super();
    }

    public LittleBoss2(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        y+=speed;
        if(y>300){
            x+=speed;
            if(x>350||x<10){
                speed=-speed;
            }
        }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.13创建LittleBoss2BulletObj类

创建游戏中的小boss2子弹对象,包括宽,高,速度,位置,用来创建小boss2的子弹,在paintSelf()方法中通过改变子弹的y坐标来实现子弹的移动,加入判断当子弹出现在窗口上时,通过我方飞机和子弹的横坐标相减确定速度的正负值,然后再除以一个合适的数值适当减弱速度的大小,避免子弹速度过快,降低游戏难度

public class LittleBoss2BulletObj extends GameObj{
    public LittleBoss2BulletObj() {
        super();
    }

    public LittleBoss2BulletObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
//        实现子弹追踪
        if(y>0){
            this.y+=speed;
            this.x-=(this.x- GameUtils.gameObjList.get(GameWin.planeIndex).getX())/60;
        }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.14创建GiftObj类

初始化没有设置掉落物品的宽,高,在碰撞检测的时候会不能进行,所以在paintSelf()方法中使用getWidth()和getHeight()方法获取了物品的宽,高,为后面实现碰撞检测做准备

public class GiftObj extends GameObj{
    public GiftObj() {
        super();
    }


    public GiftObj(int x, int y) {
        super(x, y);
    }

    public GiftObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.img= GameUtils.giftImg;
        super.width=GameUtils.giftImg.getWidth(null);
        super.height=GameUtils.giftImg.getHeight(null);

        super.paintSelf(g);
    }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.15创建Boss1Obj类

声明游戏中的大boss1对象,包括宽,高,速度,位置,声明一个静态变量blood用来存放大boss1的血量,用来初始化大boss1,在paintSelf()方法中通过改变大boss1的x,y坐标来实现大boss1的移动,其中加入if判断语句在适当的时候对速度取反,是使大boss1在y坐标大于一个设定数值的情况下,水平来回移动

public class Boss1Obj extends GameObj{
//    记录boss1血量
    public static int blood=50;

    public Boss1Obj() {
        super();
    }

    public Boss1Obj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        if(y>80){
            y+=0;
            x+=speed;
            if(x<=0||x>GameWin.WIDTH - GameUtils.bossImg.getWidth(null)){
                speed=-speed;
            }
        }else if (y<80){
            y+=3;
        }


    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.16创建Boss1BulletObj类

创建游戏中的大boss1子弹对象,包括宽,高,速度,位置,用来创建大boss1的子弹,在paintSelf()方法中通过改变子弹的y坐标来实现子弹的移动,因为游戏设定大boss1的这一种子弹不能被我方子弹碰撞消失,所以不用实现碰撞检测的功能

public class Boss1BulletObj extends GameObj{
    public Boss1BulletObj() {
        super();
    }

    public Boss1BulletObj(Image img, int width, int height, int x, int y, int speed, GameWin frame) {
        super(img, width, height, x, y, speed, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        y+=speed;
    }

    @Override
    public Rectangle getRec() {
        return super.getRec();
    }
}

3.17创建WarningObj类

加载警告图片的时候,图片有宽,高,位置,所有可以创建一个警告对象类用来加载警告背景图片

public class WarningObj extends GameObj{
    public WarningObj(int x, int y) {
        super(x, y);
    }

    public WarningObj() {
    }

    public WarningObj(Image img, int width, int height, int x, int y) {
        super(img, width, height, x, y);
    }
    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
}

5.碰撞检测

以下代码是在PlaneObj类中paintSelf()方法中用于检测我方飞机与各种游戏元素的碰撞,包括但不限于敌方飞机,子弹,boss,及其掉落物的碰撞检测,下述代码只列举了我方飞机与各种游戏元素的碰撞,实际上还有别的游戏元素需要进行游戏的碰撞检测,这里不在每个都列举,我简述一下游戏元素碰撞检测的原理。在每个游戏对象类中都有一个getRec()方法用来返回Rectangle的实例化对象,再调用该对象的静态方法intersects()用来返回一个boolean类型的变量判断两个游戏元素是否相交。

//        进行碰撞检测
//        我方飞机和敌方小飞机碰撞
        for (EnemySmallObj enemySmallObj : GameUtils.enemySmallObjList) {
            if(this.getRec().intersects((enemySmallObj.getRec()))){
//                碰撞爆炸动画
                ExplodeObj explodeObj=new ExplodeObj(x,y);
                GameUtils.explodeObjList.add(explodeObj);
                GameUtils.removeList.add(explodeObj);

                enemySmallObj.setX(-100);
                enemySmallObj.setY(-100);
                this.x=-200;
                this.y=-200;
                GameUtils.removeList.add(enemySmallObj);
                GameUtils.removeList.add(this);
                GameWin.state=3;
            }
        }
//        我方飞机和敌方大飞机碰撞
        for (EnemyBigObj enemyBigObj : GameUtils.enemyBigObjList) {
            if(this.getRec().intersects((enemyBigObj.getRec()))){
//                碰撞爆炸动画
                ExplodeObj explodeObj=new ExplodeObj(x,y);
                GameUtils.explodeObjList.add(explodeObj);
                GameUtils.removeList.add(explodeObj);

                enemyBigObj.setX(-100);
                enemyBigObj.setY(-100);
                this.x=-200;
                this.y=-200;
                GameUtils.removeList.add(enemyBigObj);
                GameUtils.removeList.add(this);
                GameWin.state=3;

            }
        }
//        我方飞机和敌方子弹碰撞
        for (EnemyBigBulletObj enemyBigBulletObj : GameUtils.enemyBigBulletObjList) {
            if(this.getRec().intersects((enemyBigBulletObj.getRec()))){
//                碰撞爆炸动画
                ExplodeObj explodeObj=new ExplodeObj(x,y);
                GameUtils.explodeObjList.add(explodeObj);
                GameUtils.removeList.add(explodeObj);

                enemyBigBulletObj.setX(-100);
                enemyBigBulletObj.setY(-100);
                this.x=-200;
                this.y=-200;
                GameUtils.removeList.add(enemyBigBulletObj);
                GameUtils.removeList.add(this);
                GameWin.state=3;

            }
        }
//        我方飞机和敌方小boss1的碰撞检测
        if(this.getRec().intersects(littleBoss1.getRec())){
//                碰撞爆炸动画
            ExplodeObj explodeObj=new ExplodeObj(x,y);
            GameUtils.explodeObjList.add(explodeObj);
            GameUtils.removeList.add(explodeObj);
            this.x=-100;
            this.y=-100;
            GameUtils.removeList.add(this);
            GameWin.state=3;

        }
//        我方飞机和敌方小boss2的碰撞检测
        if(this.getRec().intersects(littleBoss2.getRec())){
//                碰撞爆炸动画
            ExplodeObj explodeObj=new ExplodeObj(x,y);
            GameUtils.explodeObjList.add(explodeObj);
            GameUtils.removeList.add(explodeObj);
            this.x=-100;
            this.y=-100;
            GameUtils.removeList.add(this);
            GameWin.state=3;

        }
//        我方飞机和敌方小boss1子弹的碰撞检测
        for (LittleBoss1BulletObj littleBoss1BulletObj :GameUtils.littleBoss1BulletObjList) {
            if(this.getRec().intersects(littleBoss1BulletObj.getRec())){
//                碰撞爆炸动画
                ExplodeObj explodeObj=new ExplodeObj(x,y);
                GameUtils.explodeObjList.add(explodeObj);
                GameUtils.removeList.add(explodeObj);
                littleBoss1BulletObj.x=-200;
                littleBoss1BulletObj.y=-200;
                GameUtils.removeList.add(littleBoss1BulletObj);
                this.x=-100;
                this.y=-100;
                GameUtils.removeList.add(this);
                GameWin.state=3;

            }
        }
//        我方飞机和敌方小boss2子弹的碰撞检测
        for (LittleBoss2BulletObj littleBoss2BulletObj :GameUtils.littleBoss2BulletObjList) {
            if(this.getRec().intersects(littleBoss2BulletObj.getRec())){
//                碰撞爆炸动画
                ExplodeObj explodeObj=new ExplodeObj(x,y);
                GameUtils.explodeObjList.add(explodeObj);
                GameUtils.removeList.add(explodeObj);
                littleBoss2BulletObj.x=-200;
                littleBoss2BulletObj.y=-200;
                GameUtils.removeList.add(littleBoss2BulletObj);
                this.x=-100;
                this.y=-100;
                GameUtils.removeList.add(this);
                GameWin.state=3;

            }
        }
//        我方飞机和大boss1的碰撞检测
        if(this.getRec().intersects(boss1Obj.getRec())){
//                碰撞爆炸动画
            ExplodeObj explodeObj=new ExplodeObj(x,y);
            GameUtils.explodeObjList.add(explodeObj);
            GameUtils.removeList.add(explodeObj);
            this.setX(-100);
            this.setY(-100);
            GameUtils.removeList.add(this);
            GameWin.state=3;

        }
//        我方飞机和大boss1子弹的碰撞检测
        for (Boss1BulletObj bossBulletObj: GameUtils.boss1BulletObjList) {
            if(this.getRec().intersects(bossBulletObj.getRec())){
//                碰撞爆炸动画
                ExplodeObj explodeObj=new ExplodeObj(x,y);
                GameUtils.explodeObjList.add(explodeObj);
                GameUtils.removeList.add(explodeObj);

                this.setX(-100);
                this.setY(-100);
                GameUtils.removeList.add(this);
                bossBulletObj.setX(-200);
                bossBulletObj.setY(-200);
                GameUtils.removeList.add(bossBulletObj);
                GameWin.state=3;

            }
        }
//    我方飞机拾取补给品
        for (GiftObj giftObj : GameUtils.giftObjList) {
            if(this.getRec().intersects(giftObj.getRec())){
                giftObj.setX(-100);
                giftObj.setY(-100);
                GameUtils.removeList.add(giftObj);
                times++;
            }
        }
    }

6.游戏入口类

GameWin类继承JFrame类,作为游戏的入口,其中包含前面提到过初始化窗口的方法init(),重写了父类paint()方法,这个方法用于窗口中各种游戏元素的绘制。

在init()方法中添加鼠标监听和键盘监听,用于控制游戏的状态游戏中我方飞机的移动。在其中写了一个while死循环用于不断重绘画面,其中还写了线程休眠用于控制画面重绘的速度。

//        将所有游戏元素放入集合中
        GameUtils.gameObjList.add(bgobj);
        GameUtils.gameObjList.add(planeObj);
        //拿到gamelist列表中的飞机索引值
        planeIndex=GameUtils.gameObjList.indexOf(planeObj);

//        添加键盘监听事件
        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                if(e.getKeyCode()==KeyEvent.VK_SPACE){
                    if(state==1){
                        state=2;//游戏暂停
                    } else if (state == 2) {
                        state=1;
                    }
                }
            }
        });
//        添加鼠标监听事件
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if(e.getButton()==MouseEvent.BUTTON1&&state==0){
                    state=1;//游戏开始
                }
            }
        });
        while (true){
            if (state==1){
                createObj();
            }
            repaint();
            try {
                Thread.sleep(15);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

声明了createObj()方法,该方法中通过一个设置好的变量count来实现游戏中元素的创建频率,核心逻辑是,count这个变量在paint()方法中每次加一,通过对count变量取模的方式来判断是否在gameObjList列表中添加游戏元素

//    用于批量创建物体
    void createObj(){
//        我方飞机子弹产生的速度
        if(count%10==0){
            if(PlaneObj.times==0){//一级子弹
                GameUtils.shell1ObjList.add(new Shell1Obj(GameUtils.shell1Img,25,62, planeObj.getX()+28, planeObj.getY()-62, 5,this));
//    添加到所有元素集合中的对象是子弹集合中最新的一个,并不是整个子弹集合
                GameUtils.gameObjList.add(GameUtils.shell1ObjList.get(GameUtils.shell1ObjList.size()-1));
            }
            if(PlaneObj.times==1){//二级子弹
                GameUtils.shell2ObjList.add(new Shell2Obj(GameUtils.shell2Img,35,65,planeObj.getX()+28, planeObj.getY()-62, 5,this));
                GameUtils.gameObjList.add(GameUtils.shell2ObjList.get(GameUtils.shell2ObjList.size()-1));
            }
            if(PlaneObj.times==2){//三级子弹
                GameUtils.shell3ObjList.add(new Shell3Obj(GameUtils.shell3Img,GameUtils.shell3Img.getWidth(null),GameUtils.shell3Img.getHeight(null),planeObj.getX()+28, planeObj.getY()-62, 5,this));
                GameUtils.gameObjList.add(GameUtils.shell3ObjList.get(GameUtils.shell3ObjList.size()-1));
            }
        }
//        敌方小飞机产生的速度
        if(count%15==0){
            GameUtils.enemySmallObjList.add(new EnemySmallObj(GameUtils.enemySmallPlane1,63,45,(int)((Math.random()*8)*63),-45,5,this));
            GameUtils.gameObjList.add(GameUtils.enemySmallObjList.get(GameUtils.enemySmallObjList.size()-1));
        }


        if(count%50==0){//敌方大飞机子弹产生速度

            if(count%200==0){//敌方大飞机的产生速度
                GameUtils.enemyBigObjList.add(new EnemyBigObj(GameUtils.enemyBigPlane1,100,76,(int)((Math.random()*5)*100),-76,1,this));
                GameUtils.gameObjList.add(GameUtils.enemyBigObjList.get(GameUtils.enemyBigObjList.size()-1));
            }
            //创建敌方大飞机子弹
            if(!GameUtils.enemyBigObjList.isEmpty()){
                int x=(GameUtils.enemyBigObjList.get(GameUtils.enemyBigObjList.size()-1)).getX();
                int y=(GameUtils.enemyBigObjList.get(GameUtils.enemyBigObjList.size()-1)).getY();
                GameUtils.enemyBigBulletObjList.add(new EnemyBigBulletObj(GameUtils.enemyBulletBigPlane1,11,35,x+45,y+76,5,this) );
                GameUtils.gameObjList.add(GameUtils.enemyBigBulletObjList.get(GameUtils.enemyBigBulletObjList.size()-1));
            }
        }
//        添加一号小boss
        if(count==600&&(!GameUtils.gameObjList.contains(littleBoss1))){
            GameUtils.gameObjList.add(littleBoss1);
        }
//        添加二号小boss
        if(count==800&&(!GameUtils.gameObjList.contains(littleBoss2))){
            GameUtils.gameObjList.add(littleBoss2);
        }
//        一号小boss子弹
        if(count%60==0){
            GameUtils.littleBoss1BulletObjList.add(new LittleBoss1BulletObj(GameUtils.littleBoss1Bullet,50,47,littleBoss1.getX()+60,littleBoss1.getY()+130,4,this));
            GameUtils.gameObjList.add(GameUtils.littleBoss1BulletObjList.get(GameUtils.littleBoss1BulletObjList.size()-1));
        }
//        二号小boss子弹
        if(count%60==0){
            GameUtils.littleBoss2BulletObjList.add(new LittleBoss2BulletObj(GameUtils.littleBoss2Bullet,50,50,littleBoss2.getX()+60, littleBoss2.getY()+130, 4,this));
            GameUtils.gameObjList.add(GameUtils.littleBoss2BulletObjList.get(GameUtils.littleBoss2BulletObjList.size()-1));
        }
//        大boss
        if(LittleBoss1.blood==0||LittleBoss2.blood==0){
            if(count==1300&&(!GameUtils.gameObjList.contains(boss1Obj))){
                GameUtils.gameObjList.add(boss1Obj);
            }
        }
//        创建大boss1的子弹
        if(GameUtils.gameObjList.contains(boss1Obj)&&count%20==0){
//            添加三种子弹
//            使用一号小boss的子弹
            GameUtils.littleBoss1BulletObjList.add(new LittleBoss1BulletObj(GameUtils.littleBoss1Bullet,50,47,boss1Obj.getX()+104,boss1Obj.getY()+117,4,this));
            GameUtils.gameObjList.add(GameUtils.littleBoss1BulletObjList.get(GameUtils.littleBoss1BulletObjList.size()-1));
//            使用二号小boss的子弹
            if(count%40==0){
                GameUtils.littleBoss2BulletObjList.add(new LittleBoss2BulletObj(GameUtils.littleBoss2Bullet,50,50,boss1Obj.getX()+104, boss1Obj.getY()+117, 4,this));
                GameUtils.gameObjList.add(GameUtils.littleBoss2BulletObjList.get(GameUtils.littleBoss2BulletObjList.size()-1));
            }
            if(count%40==0){
//            boss1的子弹-第三种子弹
                GameUtils.boss1BulletObjList.add(new Boss1BulletObj(GameUtils.boss1BulletImg,GameUtils.boss1BulletImg.getWidth(null),GameUtils.boss1BulletImg.getHeight(null),boss1Obj.getX()+104,boss1Obj.getY()+117,4,this));
                GameUtils.gameObjList.add(GameUtils.boss1BulletObjList.get(GameUtils.boss1BulletObjList.size()-1));
            }

        }
        if(count==1250&&(!GameUtils.gameObjList.contains(warningObj))){
            GameUtils.gameObjList.add(warningObj);
        }
        if(count==1300){
            GameUtils.removeList.add(warningObj);
        }
    }

paint()方法中通过判断一个变量state的数值来判断游戏状态,以此来控制游戏画面,在起初我遇到了游戏画面闪动的问题,这是因为repaint()方法每次被调用的时候都会清除上一帧画面,然后再在窗口中绘制下一帧画面。为了解决闪动问题,通过查阅质料,使用了双缓冲技术,该技术是在内存中先画出下一帧图片,当调用repaint()方法时,将窗口中的图片使用内存中的图片替换,从而解决画面的闪动。

public void paint(Graphics g) {
//        初始化双缓存图片对象
        if(offScreenImage==null){
            offScreenImage=createImage(WIDTH,HEIGHT);
        }
//        获取当前图片的画笔
        Graphics gImage=offScreenImage.getGraphics();
        gImage.fillRect(0,0,WIDTH,HEIGHT);

        if(state==0){
            gImage.drawImage(GameUtils.bgImg,0,0,null);
            gImage.drawImage(GameUtils.bossImg,200,100,null);
            gImage.drawImage(GameUtils.myPlaneImg,300,600,null);
            gImage.drawImage(GameUtils.explodeImg,300,250,null);
            gImage.setColor(Color.YELLOW);
            gImage.setFont(new Font("楷体",Font.BOLD,40));
            gImage.drawString("鼠标左键开始游戏",100,400);
        }
        if(state==1){
//            将爆炸集合添加到所有元素集合中
            GameUtils.gameObjList.addAll(GameUtils.explodeObjList);
//            不在单独绘制某个游戏元素,将每个游戏元素放入了所有元素集合中
            for (int i = 0; i < GameUtils.gameObjList.size() - 1; i++) {
                GameUtils.gameObjList.get(i).paintSelf(gImage);
            }
//            将要移除的元素从所有元素的集合中删去
            GameUtils.gameObjList.removeAll(GameUtils.removeList);
        }
        if(state==2){
            gImage.drawImage(GameUtils.bgImg,0,0,null);
            GameUtils.drawWord(gImage,"游戏暂停",Color.YELLOW,40,80,250);
        }
        if(state==3){
            gImage.drawImage(GameUtils.bgImg,0,0,null);
            GameUtils.drawWord(gImage,"游戏失败",Color.YELLOW,40,80,250);
        }
        if(state==4){
            gImage.drawImage(GameUtils.bgImg,0,0,null);
            GameUtils.drawWord(gImage,"恭喜你,游戏通关",Color.YELLOW,40,80,250);
        }
        GameUtils.drawWord(gImage,score+"分",Color.yellow,20,30,50);
        g.drawImage(offScreenImage,0,0,null);
        count++;
    }
    gImage.drawString("鼠标左键开始游戏",100,400);
    }
    if(state==1){

// 将爆炸集合添加到所有元素集合中
GameUtils.gameObjList.addAll(GameUtils.explodeObjList);
// 不在单独绘制某个游戏元素,将每个游戏元素放入了所有元素集合中
for (int i = 0; i < GameUtils.gameObjList.size() - 1; i++) {
GameUtils.gameObjList.get(i).paintSelf(gImage);
}
// 将要移除的元素从所有元素的集合中删去
GameUtils.gameObjList.removeAll(GameUtils.removeList);
}
if(state2){
gImage.drawImage(GameUtils.bgImg,0,0,null);
GameUtils.drawWord(gImage,“游戏暂停”,Color.YELLOW,40,80,250);
}
if(state
3){
gImage.drawImage(GameUtils.bgImg,0,0,null);
GameUtils.drawWord(gImage,“游戏失败”,Color.YELLOW,40,80,250);
}
if(state==4){
gImage.drawImage(GameUtils.bgImg,0,0,null);
GameUtils.drawWord(gImage,“恭喜你,游戏通关”,Color.YELLOW,40,80,250);
}
GameUtils.drawWord(gImage,score+“分”,Color.yellow,20,30,50);
g.drawImage(offScreenImage,0,0,null);
count++;
}


  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值