• 《uni-app》一个非canvas的飞机对战小游戏实现-碰撞检测的实现


    在这里插入图片描述

    这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言~博主看到后会去代替大家踩坑的~接下来的几篇都是uni-app的小实战,有助于我们更好的去学习uni-app~
    主页: oliver尹的主页
    格言: 跌倒了爬起来就好~
    准备篇https://oliver.blog.csdn.net/article/details/127185461
    启动页实现https://oliver.blog.csdn.net/article/details/127217681
    敌机模型实现https://oliver.blog.csdn.net/article/details/127332264
    requestAnimationFrame详解https://oliver.blog.csdn.net/article/details/127377916
    我方飞机实现https://oliver.blog.csdn.net/article/details/127477230
    子弹模型实现https://oliver.blog.csdn.net/article/details/127702195

    一. 前言

    上一篇中主要分享的是子弹这个模型功能的实现,包括了:子弹的样式,子弹的创建坐标,子弹的位移操作等等 明显的是,到这一步我们的游戏主体还没有完成,因为子弹发射出去了,当与敌机交叉的时候子弹会穿过敌机,这不明显是不大合理的,子弹应该要摧毁敌机才行~
    因此,本文将实现的是 碰撞检测 相关的内容,通过碰撞检测可以将我方飞机,子弹与敌机相互关联起来,形成一个相对完整的小游戏,耐心看完,或许你会所有收获~

    二. 阅读对象与难度

    本文难度属于:中级,本文中主要实现的我方飞机发射子弹后子弹与敌方飞机的碰撞检测相关内容
    具体内容可以参考以下的思维导图:
    在这里插入图片描述

    三. 项目地址以及最终效果

    文本代码已上传CSDN上的gitCode,有兴趣的小伙伴可以直接clone,项目地址:https://gitcode.net/zy21131437/planegameuni
    如果有小伙伴愿意点个星,那就非常感谢了~最终效果图如下:
    在这里插入图片描述

    四. 碰撞的实现

    4.1 分析分析

    通过效果图,我们可以大致知道要实现的内容一共有两个:

    • 第一个,子弹与敌机的碰撞检测;
    • 第二个,敌机的爆炸动画被触发,包括大飞机爆炸动画和小飞机的爆炸动画;

    这两个都是需要我们去实现的,只有实现了这两个后飞机大战这个小游戏才算是勉强可以正常玩耍了~

    4.2 碰撞检测

    在开始之前不妨思考一下,这个碰撞检测应该在什么时候执行?这明显是一个很关键的问题,不同的人有不同的解决方案,以本文为例,本文中的碰撞检测的执行时机在于敌机的每一次的 requestAnimationFrame 循环内,也就是说,每一次敌机在进行位移动画的时候都会去检测一遍有没有子弹与当前的敌机进行了碰撞,一旦进行碰撞那么,就要触发对应的后续,比如爆炸动画以及缓存中移除该飞机;
    执行时机我们知道了,那么碰撞这个概念该怎么去实现呢?我们先看一段代码,代码并不复杂

    export function isCollision(target, enemy) {
    	return !(
    		target.x + target.width < enemy.x ||
    		enemy.x + enemy.width < target.x ||
    		target.y + target.height < enemy.y ||
    		enemy.y + enemy.height < target.y
    	);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这一段代码就是用来做碰撞检测的,代码量上是不是非常简单,但细看一眼又觉得不大好理解,觉得很绕,其实仔细想想所谓碰撞检测不就是做了一个判断吗,判断两个DOM是否存在重叠,如果存在重叠那么就是碰撞了,需要我们后续处理,下面我们就仔细看一下这段代码,一行一行看:
    首先第一行,定义了一个名为 isCollision 的函数,这个函数接收两个参数,很明显,这两个参数就是用来进行检测的两方~
    第二行,第二行是返回,并且这个返回针对运算结果执行了!这个取反的操作;
    从第三行开始就是两个DOM的碰撞判断了,先看第三行,第三行是一个判断

    target.x + target.width < enemy.x
    
    • 1

    这段判断的意思是说,target的x轴的坐标加上target的宽度小于enemy在X轴上的坐标,什么意思呢?看个示意图:
    在这里插入图片描述

    简单的说,假设target的坐标加上其本身的宽度还小于enemy在X轴上的值的话,那target一定在enemy的左侧位置
    再看第四行:

    enemy.x + enemy.width < target.x
    
    • 1

    同理,这段判断的意思是说,enemy的x轴的坐标加上enemy的宽度小于target在X轴上的坐标,这就代表着target一定是在enemy的右侧,如果不大清楚可以继续看个示意图
    在这里插入图片描述
    enemy一定在target的左侧,换句话说,target一定会在enemy的右侧
    第五行:

    target.y + target.height < enemy.y
    
    • 1

    target的Y坐标加上target的高度小于enemy的Y坐标,直接看示意图
    在这里插入图片描述
    如果target的Y坐标加上高度依然小于enemy的Y坐标,这就代表target一定是在enemy的上方位置
    第六行:

    enemy.y + enemy.height < target.y
    
    • 1

    enemy.y的Y坐标加上enemy.y的高度小于target的Y坐标,直接看示意图
    在这里插入图片描述
    如果enemy的Y坐标加上高度依然小于target的Y坐标,这就代表target一定是在enemy的下方位置;

    到这里我们不难发现,从第五行到第九行就是一个判断的过程,判断了两个DOM在上、下、左、右这四个方向上的是否是不重合的,最后做了个取反,如果值为true,那么就代表这两者是重合、交叉的部分,也就是发生碰撞了
    因此,我们只需要在敌机执行 requestAnimationFrame 的时候做一个检测,检测是否存在敌机与子弹发生碰撞,添加一个 isCollision 方法

    this.moveTimer = () => {
      //敌机移动
      this.move();
    
      // 碰撞检测
      this.isCollision();
    
      // 重绘,无限循环
      requestAnimationFrame(this.moveTimer);
    };
    this.moveTimer();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    碰撞检测代码如下

    isCollision() {
      if (this.bulletData.length === 0) return;
    
      this.bulletData.forEach(el => {
    
        const collision = isCollision(el, this.data);
        if (collision) {
          new Promise(resolve => {
            this.data.isExplosion = true;
    
            // 删除子弹
            this.$emit('removeBullet', el.id);
    
            setTimeout(() => resolve(), 450);
          }).then(res => {
            this.remove();
          });
        }
      });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    检测到如果与任意一个子弹发生碰撞,那么立即将 isExplosion 设置为true,且将子弹的ID上传到父级,让其删除子弹~

    4.3 爆炸动画

    敌机的爆炸动画其实我们在之前已经分享过了,这边再简单分享一下,将其和子弹碰撞串起来,核心的点在于上一篇中的 isExplosion ,当 isExplosion 被设置成true的时候,也就是这一段

    this.data.isExplosion = true;
    
    • 1

    一旦data的isExplosion为true,那么我们在computed中设置的样式会立即发生对应的变化

    	computed: {
    		getEnemyClass() {
    			const classStyle = [`enemy_${this.data.type}`];
    			const explosion = {};
    			explosion[`enemy_${this.data.type}_effect`] = this.data.isExplosion;
    			classStyle.push(explosion);
    			return classStyle;
    		}
    	},
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    原来返回的是:

    [`enemy_1`]
    
    • 1

    会变成返回

    [`enemy_1`,{enemy_1_effect:true}]
    
    • 1

    这就代表enemy_1_effect这个类名上添加的animation动画会被立刻触发,其爆炸效果也就会立刻显示;
    可能有小伙伴会问,这里的延迟是怎么实现的,我们知道爆炸效果是有一个延迟的,爆炸效果持续了0.45秒,0.45秒后需要将敌机移除,这里的延迟是通过 promise+setTimeout 实现的

    new Promise(resolve => {
      this.data.isExplosion = true;
    
      // 删除子弹
      this.$emit('removeBullet', el.id);
    
      setTimeout(() => resolve(), 450);
    }).then(res => {
      this.remove();
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    通过 setTimeout 将移除这个功能延迟到0.45秒之后执行,这样就实现了县执行爆炸动画,当爆炸动画显示了0.45秒以后,执行 remove函数,将对应敌机删除掉;

    五. 阶段性展示

    到这一章节,我们已经实现的效果图如下:
    在这里插入图片描述

    六. 小结

    本文主要概述了碰撞以及爆炸效果的实现,主要包含:

    • 碰撞检测:我们在敌机每次渲染位移的时候多进行了一步碰撞检测,在检测中如果发现子弹与敌机存在DOM上的交叉,那么我们就认为两种进行碰撞了,一旦发生碰撞敌机就需要执行爆炸动画,之后移除敌机,而子弹则在一开始碰撞时就立刻进行移除~
    • 爆炸动画:爆炸动画其实在之前的对应模型实现中就已经实现了,这里只是给予其触发的一个提示,当这个提示出现时,敌机立刻执行爆炸动画~

    到这里,飞机大战算是简单的,基本的能玩了,有我方飞机,有敌机,有子弹,子弹能摧毁敌机,基本上所有主要条件算是凑齐了,有兴趣的小伙伴一定要亲自试试~还是非常有趣的;

  • 相关阅读:
    Json文件序列化读取
    javascript转ArrayBuffer为字符串
    项目管理中,项目干系人的角色和责任
    组织架构图怎么做?手把手教你绘制完美的组织架构图
    怎么找到appdata文件夹?
    马克·雷伯特访谈:机器人的未来及波士顿动力的创新之路
    【操作系统】esp栈指针
    02.线性回归算法
    树莓派Raspberrypi安装Kali Linux保姆教程(通过树莓派安装ARM Kali教程)
    YOLOv5 Head解耦
  • 原文地址:https://blog.csdn.net/zy21131437/article/details/127715943