Java Swing 经典小游戏《飞机大战》———— (三)敌机 敌机发射 与 种类生成

(一)效果展示

在这里插入图片描述
这里因为作者提前做好了前面的部分,左上角的得分暂且忽略

(二)代码实现

1.添加敌机

老套路,先加载敌机的造型

Utils.java

import java.awt.*;

public class Utils {
    public static Image BgSky = Toolkit.getDefaultToolkit().getImage("src/Images/Bg_sky.png");
    public static Image[] Plane = {
        Toolkit.getDefaultToolkit().getImage("src/Images/Plane/1.png"),
        Toolkit.getDefaultToolkit().getImage("src/Images/Plane/2.png"),
        Toolkit.getDefaultToolkit().getImage("src/Images/Plane/3.png"),
    };
    public static Image[] Bullet = {
        Toolkit.getDefaultToolkit().getImage("src/Images/Bullet/1.png"),
        Toolkit.getDefaultToolkit().getImage("src/Images/Bullet/2.png")
    };
    public static Image[]  Enamy = {
        Toolkit.getDefaultToolkit().getImage("src/Images/Enamy/1.png"),
        Toolkit.getDefaultToolkit().getImage("src/Images/Enamy/2.png"),
        Toolkit.getDefaultToolkit().getImage("src/Images/Enamy/3.png")
    };
}

然后还记得上篇文章我们创建的Plan类吗?创建Enamy类继承它
跟子弹同样的道理,直接加上移动功能
这里为了不让敌机一排一排像板刷一样的出现,我们加上一个随机小范围调整速度让机群错落有致

Enamy.java

import java.awt.*;
import java.util.List;

public class Enamy extends Plane{
    int hp;
   	/*
   		boss 飞机是横向移动的 所以区分 x,y speed 更加方便
    */
    int speedx,speedy; 
    /*
    	跟子弹同理
    */
    boolean delete = false;

    Enamy(int x,int y,int level) {
        this.rect = new Rectangle(x,y,64,64);
        this.img = Utils.Enamy[level];
        this.state = level + 10; // 标记是敌机的某种型号 + 10
        if (level == 0 || level == 1) {
            this.hp = (level + 1) * 2; // 根据等级确定血量
            this.speedy = (level + 1) * (3 + (int)Math.round(Math.random() * (1 - 0)) + 0);
            this.speedx = 0;
        } else {
            this.lstFire = -15;
            this.hp = 30;
            this.rect.setLocation(x,30);
            this.speedy = 0;
            this.speedx = 5;
        }
    }
	
    void paintSelf(Graphics g) {
        if (this.rect.y >= 700 || this.hp <= 0) this.delete = true; 
        else {
            if (this.rect.x <= 20 || this.rect.x >= 450) this.speedx *= -1;
            this.rect.translate(this.speedx,this.speedy);
        }
        super.paintSelf(g);
    }
}

后来发现hpEnamyPlayer共有的应该放进Plane中大家可以自行修改一下。
敌机移动的代码就实现了,接下来还要实例化它,也就是基因有了,还要把他生出来。

GameWin.java

在游戏主循环中生成敌机
这里 小怪 经常出现,boss 极少出现且间隔超过15秒,所以我们给游戏加上一条时间线(实际上就是一个计数变量TimeStamp)
另外用Math.random()控制敌机种类的出现概率

import java.awt.*;

import javax.swing.*;
import javax.swing.event.MouseInputAdapter;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class GameWin extends JFrame {
    int width = 500;
    int height = 700;
    Image offScreenImage;

    Bg bg = new Bg();
    Player player = new Player(this);
    List<Bullet> bullets = new ArrayList<>();
    // 同子弹
    List<Enamy> enamies = new ArrayList<>();
    int TimeStamp = 0,lstBoss = -100,Bossnum = 0,lstFire = -15;

    void launch() {
        this.setVisible(true);
        this.setSize(width,height);
        this.setLocationRelativeTo(null);
        this.setTitle("飞机大战");
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        this.addKeyListener(new MyKeyListener());
        this.addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseClicked(java.awt.event.MouseEvent e) {
                // TODO Auto-generated method stub
                if (GameState == 0) {
                    GameState = 1;
                }
                super.mouseClicked(e);
            }
        });;

        while (true) {
            TimeStamp ++; // 时间线
            // 个人习惯 用 {} 包裹一个整体 增强模块化逻辑
            {
                int num;
                if (enamies.size() < 10 && enamies.size() >= 3) {
                    num = (int)(Math.random() * ((10 - enamies.size()) - 1)) + 1;
                } else if(enamies.size() < 3) {
                    num = (int)(Math.random() * ((10 - enamies.size()) - 3)) + 3;
                } else {
                    num = 0;
                }
                for(int i = 1;i <= num;i ++) {
                    double rd = Math.random();
                    int level;
                    if (rd <= 0.7) level = 0;
                    else if(rd <= 0.95 || (TimeStamp - lstBoss) <= 1500 || Bossnum != 0) level = 1;
                    else {
                        level = 2;
                        lstBoss = TimeStamp;
                        Bossnum ++;
                    }
                    this.enamies.add(new Enamy((int)(Math.random() * (450 - 20)) + 20,(int)(Math.random() * (25 - 0)) + 0,level));
                }
            }

            repaint();

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    class MyKeyListener implements KeyListener {
        @Override
        public void keyPressed(KeyEvent e) {
            // TODO Auto-generated method stub
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                player.movedir = -1;
            } else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
                player.movedir = 1;
            } else if(e.getKeyCode() == KeyEvent.VK_SPACE) {
                player.Fire();
            }
        }

        @Override
        public void keyTyped(KeyEvent e) {
            // TODO Auto-generated method stub
            
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub
            if (e.getKeyCode() == KeyEvent.VK_LEFT) player.movedir = 0;
            else if(e.getKeyCode() == KeyEvent.VK_RIGHT) player.movedir = 0;
        }
    }

    @Override
    public void paint(Graphics g) {
        // TODO Auto-generated method stub
        offScreenImage = this.createImage(width,height);
        Graphics gImage = offScreenImage.getGraphics();
        
        bg.paintSelf(gImage);
        player.paintSelf(gImage);
        for(Iterator<Bullet> it = bullets.iterator();it.hasNext();) {
            Bullet bl = it.next();
            if (bl.delete) it.remove();
            else bl.paintSelf(gImage);
        }
        for(Iterator<Enamy> it = enamies.iterator();it.hasNext();) {
            Enamy en = it.next();
            if (en.delete) {
                if (en.state == 12) Bossnum --; // 控制最多有一个boss出现
                it.remove();
            }
            else en.paintSelf(gImage);
        }
        gImage.setFont(new Font("黑体",Font.BOLD,25));
        gImage.setColor(Color.red);
        

        g.drawImage(offScreenImage,0,0,null);
    }
    
    public static void main(String[] args) throws Exception {
        GameWin gameWin = new GameWin();
        gameWin.launch();
    }
}

敌机的问题就解决了

2.Boss升级

稍等,Boss还没有发射子弹,问题还没有结束
类似Player设置Fire,但是我们还需要将GameWin直接传参进去吗?
个人理解,这样做法不够好,占用更多的空间, 那么我们需要的其实只是bullets列表
这里炮弹的形态,以及等级可以自选

void Fire(List<Bullet> bl) {
        bl.add(new Bullet(rect.x,rect.y,1, 1));
        bl.add(new Bullet(rect.x + rect.width - 5,rect.y,1, 1));
    }

接下来就是发射了,许多小伙伴可能觉得按空格键和方向键不太方便,于是和Boss的子弹发射一起,我们做一个AutoFireE键启动

class MyKeyListener implements KeyListener {
        @Override
        public void keyPressed(KeyEvent e) {
            // TODO Auto-generated method stub
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                player.movedir = -1;
            } else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
                player.movedir = 1;
            } else if(e.getKeyCode() == KeyEvent.VK_SPACE) {
                player.Fire();
            } else if(e.getKeyCode() == KeyEvent.VK_E) {
                autofire = !autofire; // 设置自动开火模式
            }
        }

        @Override
        public void keyTyped(KeyEvent e) {
            // TODO Auto-generated method stub
            
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub
            if (e.getKeyCode() == KeyEvent.VK_LEFT) player.movedir = 0;
            else if(e.getKeyCode() == KeyEvent.VK_RIGHT) player.movedir = 0;
        }
    }

然后在主循环中按TimeStamp与上一次lstFire的间隔频率发射,添加

{
	if (this.autofire && this.TimeStamp - this.lstFire >= 15){ // 150ms 一发
	  this.lstFire = this.TimeStamp;
	  player.Fire();
	}
	for(Enamy en: enamies) {
	  if (en.state == 12 && this.TimeStamp - en.lstFire >= 15){
	      en.lstFire = this.TimeStamp;
	      en.Fire(bullets);
	  }
	}
}

运行,发现Boss可以开火了,今天的任务也就完成了

  • 实现了添加敌机飞机
  • 实现了不同种类的敌机错落有致
  • 实现了Boss子弹发射
  • 实现了游戏时间线
  • 子弹碰撞检测 (由于结构变化,下期更新)

下期预告
子弹碰撞检测
游戏得分统计与玩家升级
游戏状态(输赢)
最后 撰写不易,做人,一定要善良; 点赞,好人,有好福
关注我,收藏 点赞 评论文章 更新速度更快哦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值