这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言~博主看到后会去代替大家踩坑的~接下来的几篇都是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 分析分析
根据上面的效果图,我们先分析一下要实现的功能:
- 首先要实现的是子弹的样式;
- 其次是子弹的初始化坐标,由于我方飞机的位置是不固定的,因此子弹的生成位置也将是不固定的;
- 最后时子弹创建后的自适应位移;
大致上这三个功能在本文这个阶段是最主要的,再来估计一下如果要实现这几个功能可能要用到什么实现逻辑
第一个,子弹样式
子弹样式这一块和我方飞机、敌机一样,都是由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