飞道的博客

《uni-app》一个非canvas的飞机对战小游戏实现-子弹模型的实现

344人阅读  评论(0)

这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言~博主看到后会去代替大家踩坑的~接下来的几篇都是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

一. 前言

上一篇中主要分享的是我方飞机这个模型功能的实现,包括了:飞机的样式,飞机的生成,飞机的创建坐标,飞机的位移操作等等,但是,很明显我们这个飞机还是不完善的,最大的功能遗漏就是子弹了,没有子弹,怎么能被称为是飞机大战呢,对不对~
本文将实现的是
子弹模型
相关,当然,我们这个子弹还是属于简单的那一种,后续复杂子弹的实现我们后续再说,耐心看完,或许你会所有收获~

二. 阅读对象与难度

本文难度属于:中级,本文中主要实现的我方飞机发射的子弹型相关的操作,包括子弹坐标位置初始化,子弹移动,子弹样式等等,通过文本你可以大致了解到一下内容

  • Vue中的基础知识,包括v-for等常规用法;
  • 子弹的模型的样式,创建,位移等等;

具体内容可以参考以下的思维导图:

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

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

四. 子弹模型的实现

4.1 分析分析

根据上面的效果图,我们先分析一下要实现的功能:

  1. 首先要实现的是子弹的样式;
  2. 其次是子弹的初始化坐标,由于我方飞机的位置是不固定的,因此子弹的生成位置也将是不固定的;
  3. 最后时子弹创建后的自适应位移;

大致上这三个功能在本文这个阶段是最主要的,再来估计一下如果要实现这几个功能可能要用到什么实现逻辑
第一个,子弹样式
子弹样式这一块和我方飞机、敌机一样,都是由DOM+CSS实现的,设定好宽高以及背景图即可;
第二个,创建坐标
既然是坐标,那坐标肯定是有x和y的,从效果上看,子弹创建的位置位于飞机头部的中间处,并且由于飞机是不固定位置的,因此子弹的位置也是不固定的
第三个,子弹位移
老规矩,位移还是靠的requestAnimationFrame实现的;

4.2 子弹的样式

先来看看子弹的样式,它的的素材图如下:

就是一个简单的图片,我们只需要将设定其宽高,并且背景图绑定图片即可:

<template>
	<view class="bullet"></view>
</template>

<style scoped lang="less">
.bullet {
     
	width: 7px;
	height: 18px;
	position: fixed;
	z-index: 2;
	background: url(@/static/images/bullet.png) no-repeat left top;
}

.bullet_effect {
     
	display: none;
}
</style>

 

4.3 子弹创建

在了解子弹创建之前,我们得明白子弹的创建时机,和我方飞机与敌机不同的是,子弹并不是随时创建的,它必须在我们触发某一个按钮,比如web上的是按下空格键后触发创建,大致流程如下

因此首先,我们需要添加一个事件,一个按下空格键时触发的事件

initEvent() {
  document.addEventListener('keydown', e => {
    switch (e.keyCode) {
      //空格
      case 32:
        this.launchBullet();
        break;
      //up键
      case 38:
        this.keyTop = true;
        break;
      //down键
      case 40:
        this.keyBottom = true;
        break;
      //left键
      case 37:
        this.keyLeft = true;
        break;
      //right键
      case 39:
        this.keyRight = true;
        break;
      default:
        console.log('无效案件,请使用上、下、左、右控制');
    }
  });
  document.addEventListener('keyup', e => {
    switch (e.keyCode) {
      //up键
      case 38:
        this.keyTop = false;
        break;
      //down键
      case 40:
        this.keyBottom = false;
        break;
      //left键
      case 37:
        this.keyLeft = false;
        break;
      //right键
      case 39:
        this.keyRight = false;
        break;
    }
  });
},

 

找到plane.vue中的按键监听,在 keydown 中额外新增一项,指定值为32时触发发射子弹的函数,这个32是空格键的内部识别码

launchBullet() {
  const params = {
    width: 7,
    height: 18,
    x: this.data.x + 49,
    y: this.data.y,
    id: `bullet` + new Date().getTime()
  };
  this.$emit('addBullet', params);
}

当按下空格键后,会触发这个名为 launchBullet的子弹创建函数,这个函数包括了子弹的宽,高,X轴,Y轴的坐标,以及唯一的标识码,可能有小伙伴问,不是说子弹的坐标不固定吗,为啥这边可以直接写死,并且还是加了一个49?这里解释一下,这里的this.data.x和this.data.y是当前飞机X轴,Y轴的坐标

子弹虽然是不固定的,但相对我方飞机来说,确又是固定的,它生成的位置永远在我方飞机头部中间处的位置,因此,子弹的X轴坐标只需要取得飞机的X轴加上飞机一半的宽度即可,高度就是等同于我方飞机Y轴的高度~
创建完成后通过emit将参数传递到父组件中,父组件接收参数之后将其push到子弹缓存列表中

<!-- 我方飞机 -->
<template>
  <Plane @addBullet="addBullet" />
</template>

<script>
import Bullet from '../view/bullet/bullet.vue';
export default {
     
	data() {
     
		return {
     
			bulletData: []
		};
	},
	components: {
     Bullet },
	onLoad() {
     
		this.init();
	},
	methods: {
     
		addBullet(param) {
     
			console.log('--- 触发子弹 ---');
			this.bulletData.push(param);
		},
	}
};
</script>

 

当参数存入缓存数组后,通过v-for循环动态生成子弹

<!-- 子弹 -->
<template v-if="isStart">
  <Bullet v-for="bullet in bulletData" :key="bullet.id" :data="bullet"  />
</template>

并通过props将初始位置赋值给子弹模型

<template>
	<view 
    class="bullet" 
    :style="{ left: data.x + 'px', top: data.y + 'px' }"
    ></view>
</template>

<script>
export default {
     
	props: {
     
		data: {
     
			type: Object,
			default: () => {
     
				return {
     };
			}
		}
	},
};
</script>

 

这样,当我们按下空格键的时候,子弹就会被创建在飞机的头部的中间位置,虽然此时的子弹还不会位移

4.4 子弹的位移

子弹的位移,实现上还是非常简单的,和我方飞机与敌机是相同的实现方式,都是利用的 requestAnimationFrame 实现,先看代码吧

<script>
export default {
     
	methods: {
     
		init() {
     
			this.moveTimer = () => {
     
				// 移动
				this.move();
				// 重绘,无限循环
				requestAnimationFrame(this.moveTimer);
			};
			this.moveTimer();
		},
		move() {
     
			if (this.data.y > 0) {
     
				// 子弹的加速度
				this.data.y += -5;
			} else {
     
				// this.isExplosion = true;
				this.remove();
			}
		},
		remove() {
     
			this.$emit('removeBullet', this.data.id);
		}
	},
	mounted() {
     
		this.init();
	}
};
</script>

 

当子弹创建后,会执行 mounted 中的init方法,这个方法会通过 requestAnimationFrame 无限重绘子弹位置,重绘的依据则是在 move() 这个函数中实现的;
接着看move这个函数,分为两段:

  • 第一段当子弹在Y轴上的坐标大于0时,不断改变它在Y轴上的坐标,每次减少5个像素,或者说子弹的加速度是5;
  • 第二段是移除子弹,我们知道子弹的存在并不是无限制的,比如当子弹没有击中敌机,并且子弹在Y轴的位置小于0,这就代表其实子弹已经超出屏幕了,这种情况下那这个子弹就没有存在的意义了,完全可以在DOM中移除掉,节省性能

    因此,当子弹在Y轴的坐标小于0时,触发remove函数,通过emit将子弹的ID给传递出去
this.$emit('removeBullet', this.data.id);

在父组件中接收到 removeBullet 这个事件后,遍历子弹缓存数组,移除对应id的子弹即可

	let index = 0;
	for (let i = 0; i < array.length; i++) {
		if (array[i].id === id) {
			index = i;
			break;
		}
	}
	array.splice(index, 1);

五. 阶段性展示

到这一章节,我们已经实现的效果图如下:

六. 小结

本文主要概述了子弹模型的实现,主要包含:

  • 子弹样式:其实就是设定好DOM,加入背景图形成了一个子弹的DOM;
  • 子弹生成:按下空格键时,创建好子弹的参数,包括生成的位置坐标,并将参数传递到子弹的模型中;
  • 子弹操作位移:位移通过requestAnimationFrame实现;

到这里,敌机,我方飞机,子弹模型都已经结束了,接下来就是碰撞检测了,这一块稍微涉及到了一些计算,当然,问题不大~


转载:https://blog.csdn.net/zy21131437/article/details/127702195
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场