Java Swing 经典小游戏《飞机大战》———— (四)碰撞检测 游戏状态与得分 玩家升级

前期回顾

Java Swing 经典小游戏《飞机大战》———— (一)获取素材,创建窗口,添加滚动背景,双缓冲
Java Swing 经典小游戏《飞机大战》———— (二)玩家 移动 与 子弹
Java Swing 经典小游戏《飞机大战》———— (三)敌机 敌机发射 与 种类生成
Java Swing 经典小游戏《飞机大战》———— (四)碰撞检测 游戏状态与得分 玩家升级

(一)效果展示

在这里插入图片描述
以上边框是作者为了调试碰撞检测添加实际上没有

(二)代码实现

1.碰撞检测

首先来做碰撞检测部分
先介绍一下碰撞检测,我们可以用一个矩形来表示物体的位置,如:
在这里插入图片描述
然后对物体碰撞的检测其实也就是看矩形的交集
但是这样就会出现一点问题,如这样:
在这里插入图片描述
碰撞了,又好像没有碰撞
原因是矩形碰撞了,但是实际上的飞机图像并没有碰撞(也就是玩家并不会知道撞机了)
这怎么办呢?
个人思考如下:(如果大家有更好的办法可以告诉我)
观察到这样子的情况是小部分,而且其碰撞的面积也很小,因此我们给他一个阈值
具体如下:
在这里插入图片描述
我们计算橙色部分的面积,然后看他在两个矩形中的占比
那么如何衡量呢?取平均值,和还是差?
都不是,个人认为应该取最大值,毕竟有一个已经快完全撞上了,肯定应该先考虑他嘛
所以来实现一下
在GameWin的主循环里检测

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;
    int score = 0;
    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;
    boolean autofire = false;
	
	//直接用java.awt中的API计算
    int GetArea(Rectangle r1,Rectangle r2) {
        Rectangle insect = r1.intersection(r2); // 获取相交的矩形
        return insect.height * insect.width;
    }

    boolean collision(Rectangle a,Rectangle b) {
        if (!a.intersects(b)) return false; //先检测有无交集
        int area = GetArea(a, b);
        int areaa = a.width * a.height;
        int areab = b.width * b.height;
        if (Math.max(area * 1.0 / areaa,area * 1.0 / areab) <= 0.5) return false; // 如果超过一半 就返回碰撞
        return true;
    }

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

        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));
                    }
                }
                
                {
                    for(Bullet bl: bullets) { // 以子弹为外层,因为只要被一个物体碰到了就会消失,后面可以break掉
                        if (bl.move_dir == 1) { // 如果是乡下发射的
                            if (collision(player.rect,bl.rect) && !bl.delete) { // 与玩家的飞机做检测
                                bl.delete = true;
                                player.hp -= bl.level + 1;
                                break;
                            }
                        } else {
                            for(Enamy en: enamies) {
                            	// 看有没有击落敌机
                                if (collision(en.rect,bl.rect) && !bl.delete && !en.delete) {
                                    en.hp -= bl.level + 1;
                                    bl.delete = true;
                                    break;
                                }
                            }
                        }
                    }
                    // 敌机是否碰到了玩家
                    for(Enamy en: enamies) {
                        if (collision(en.rect,this.player.rect) && !en.delete) {
                        	// 执行一些操作
                        }
                    }
                } 
                {
                    if (this.autofire && this.TimeStamp - this.lstFire >= 15){
                        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);
                        }
                    }
                }
            }

            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 (GameState != 1) return ;
            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 (GameState != 1) return ;
            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 --;
                if (en.rect.y < 700) this.score += Utils.EnamyScore[en.state - 10];
                it.remove();
            }
            else en.paintSelf(gImage);
        }
        

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

2.游戏状态

写代码的时候发现有敌机或子弹碰到玩家,我们不知到应该干什么了
其实这个时候游戏就失败了,我们应该切换页面了
新建一个GameState变量标记游戏状态
然后再主循环与paint函数中检测
GameWin.java

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;
    int score = 0;
    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;
    /*
     * 0: 等待开始
     * 1: 游戏进行中
     * 2: 游戏结束 玩家胜利
     * 3:游戏结束 玩家失败
     */
    int GameState = 0;
    boolean autofire = false;

    int GetArea(Rectangle r1,Rectangle r2) {
        Rectangle insect = r1.intersection(r2);
        return insect.height * insect.width;
    }

    boolean collision(Rectangle a,Rectangle b) {
        if (!a.intersects(b)) return false;
        int area = GetArea(a, b);
        int areaa = a.width * a.height;
        int areab = b.width * b.height;
        if (Math.max(area * 1.0 / areaa,area * 1.0 / areab) <= 0.5) return false;
        return true;
    }

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

        this.addKeyListener(new MyKeyListener());

        while (true) {
            switch(GameState) {
                case 1:
                    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));
                        }
                    }
                    
                    {
                        for(Bullet bl: bullets) {
                            if (bl.move_dir == 1) {
                                if (collision(player.rect,bl.rect) && !bl.delete) {
                                    bl.delete = true;
                                    player.hp -= bl.level + 1;
                                    break;
                                }
                            } else {
                                for(Enamy en: enamies) {
                                    if (collision(en.rect,bl.rect) && !bl.delete && !en.delete) {
                                        en.hp -= bl.level + 1;
                                        bl.delete = true;
                                        break;
                                    }
                                }
                            }
                        }
                        for(Enamy en: enamies) {
                            if (collision(en.rect,this.player.rect) && !en.delete) {
                                GameState = 3;
                            }
                        }
                    } 
                    {
                        if (this.autofire && this.TimeStamp - this.lstFire >= 15){
                            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);
                            }
                        }
                    }
                    break;
                case 2:
                    break;
                case 3:
                    break;
            }

            if (player.hp <= 0) GameState = 3; // 别忘了这个,玩家可能被子弹射死哦

            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 (GameState != 1) return ;
            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 (GameState != 1) return ;
            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();

        switch(GameState) {
            case 0:
                break;
            case 1:
                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 --;
                        if (en.rect.y < 700) this.score += Utils.EnamyScore[en.state - 10];
                        it.remove();
                    }
                    else en.paintSelf(gImage);
                }
                gImage.setFont(new Font("黑体",Font.BOLD,25));
                gImage.setColor(Color.red);
                break;
            case 2:
                break;
            case 3:
                break;
        }
        

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

3.游戏得分

还记得我们之前创建了3种状态的玩家飞机吗?现在我们只能一直玩0级当然是不行的
于是我们需要有一个衡量升级的标准————得分
先规定一下得分规则

击落飞机得分
小怪10
中怪30
Boss50

还有升级规则

分数等级
0~1000级
100~6001级
600~10003级
1000以上胜利

在代码中实现一下
GameWin.java

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;
    int score = 0;
    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;
    /*
     * 1: 游戏进行中
     * 2: 游戏结束 玩家胜利
     * 3:游戏结束 玩家失败
     */
    int GameState = 0;
    boolean autofire = false;

    int GetArea(Rectangle r1,Rectangle r2) {
        Rectangle insect = r1.intersection(r2);
        return insect.height * insect.width;
    }

    boolean collision(Rectangle a,Rectangle b) {
        if (!a.intersects(b)) return false;
        int area = GetArea(a, b);
        int areaa = a.width * a.height;
        int areab = b.width * b.height;
        if (Math.max(area * 1.0 / areaa,area * 1.0 / areab) <= 0.5) return false;
        return true;
    }

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

        this.addKeyListener(new MyKeyListener());

        while (true) {
            switch(GameState) {
                case 1:
                    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));
                        }
                    }
                    
                    {
                        for(Bullet bl: bullets) {
                            if (bl.move_dir == 1) {
                                if (collision(player.rect,bl.rect) && !bl.delete) {
                                    bl.delete = true;
                                    player.hp -= bl.level + 1;
                                    break;
                                }
                            } else {
                                for(Enamy en: enamies) {
                                    if (collision(en.rect,bl.rect) && !bl.delete && !en.delete) {
                                        en.hp -= bl.level + 1;
                                        bl.delete = true;
                                        break;
                                    }
                                }
                            }
                        }
                        for(Enamy en: enamies) {
                            if (collision(en.rect,this.player.rect) && !en.delete) {
                                GameState = 3;
                            }
                        }
                    } 
                    {
                        if (this.autofire && this.TimeStamp - this.lstFire >= 15){
                            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);
                            }
                        }
                    }
                    break;
                case 2:
                    break;
                case 3:
                    break;
            }

            if (player.hp <= 0) GameState = 3;
            else if(this.score >= 100 && this.score <= 600 && this.player.state != 1) this.player.Upper();
            else if(this.score > 600 && this.score < 1000 && this.player.state != 2) this.player.Upper();
            else if(this.score >= 1000) {
                GameState = 2;
            }

            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 (GameState != 1) return ;
            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 (GameState != 1) return ;
            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();

        switch(GameState) {
            case 0:
                break;
            case 1:
                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 --;
                        if (en.rect.y < 700) this.score += Utils.EnamyScore[en.state - 10]; // 注意这里要 -10 作者RE了很多次才发现
                        it.remove();
                    }
                    else en.paintSelf(gImage);
                }
                gImage.setFont(new Font("黑体",Font.BOLD,25));
                gImage.setColor(Color.red);
                gImage.drawString("得分:" + this.score, 10,60); // 把得分绘制出来
                break;
            case 2:
                break;
            case 3:
                break;
        }
        

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

然后再在Player.java中实现Upper()

import java.awt.*;

public class Player extends Plane{
    
    /*
     * 0 不动 1 向右 -1 向左
     */
    int movedir;

    GameWin frame;

    Player(GameWin frame) {
        this.frame = frame;
        this.state = 0;
        this.hp = 10;
        this.movedir = 0;
        this.img = Utils.Plane[state];
        this.rect =  new Rectangle(225,625,50,50);
    }

    void Upper() {
    	if (this.state == 2) return ; // 好习惯 ++
        this.state = this.state + 1; // 升级
        this.hp += 5 * this.state; // 给点血量补给
        this.img = Utils.Plane[state]; // 重新加载造型
    }

    void Fire() {
        if (this.state == 0) {
            this.frame.bullets.add(new Bullet(rect.x + rect.width / 2,rect.y,-1, 0));
        } else if (this.state == 1) {
            this.frame.bullets.add(new Bullet(rect.x,rect.y,-1, 0));
            this.frame.bullets.add(new Bullet(rect.x + rect.width - 5,rect.y,-1, 0));
        } else {
            this.frame.bullets.add(new Bullet(rect.x,rect.y,-1, 1));
            this.frame.bullets.add(new Bullet(rect.x + rect.width - 5,rect.y,-1, 1));
        }
    }

    @Override
    void paintSelf(Graphics g) {
        // TODO Auto-generated method stub
        int x = this.rect.x;
        int y = this.rect.y;
        x = Math.min(Math.max(15,x + this.movedir * 2 * ((this.state + 1) / 2 + 1)),440);
        this.rect.setLocation(x,y);
        super.paintSelf(g);
    }
}
  • 实现了碰撞检测
  • 实现了游戏状态标记
  • 实现了玩家造型的升级

下期预告
添加游戏胜利/失败以及说明游戏规则的开始界面 完成游戏
最后 撰写不易,做人,一定要善良; 点赞,好人,有好福
关注我,收藏 点赞 评论文章 更新速度更快哦

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值