这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言~博主看到后会去代替大家踩坑的~接下来的几篇都是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
《uni-app》一个非canvas的飞机对战小游戏实现-我方飞机实现
一. 前言
上一篇中主要分享的是 位移这个功能的实现,简单的说就是通过 requestAnimationFrame 这个window对象上提供的原生能力,将坐标改变这个过程与屏幕帧率吻合实现了动画化,这种动画化的效果不管是从性能上,还是从动画的流畅度上都远好于 setTimeout 和 setInterval ,当然也包括本文中提到的我方飞机,其实现过程也是依靠 requestAnimationFrame 实现的位移; 本文主要分享的内容为我方飞机的实现,耐心看完,或许你会所有收获~
二. 阅读对象与难度
本文难度属于:中级,本文中主要实现的我方飞机模型相关的操作,包括我方飞机坐标 位置初始化,爆炸动画,飞机位移等等,通过文本你可以大致了解到一下内容
- Vue中的基础知识,包括v-for等常规用法;
- 我方飞机的操控;
- requestAnimationFrame 等等
具体内容可以参考以下的思维导图:
三. 项目地址以及最终效果
文本代码已上传CSDN上的gitCode,有兴趣的小伙伴可以直接clone,项目地址:https://gitcode.net/zy21131437/planegameuni 如果有小伙伴愿意点个星,那就非常感谢了~最终效果图如下:
四. 我方飞机模型的实现
4.1 分析分析
根据上面的效果图,我们先分析一下要实现的功能:
- 首先要实现的就是我方飞机的样式,当然也包括我方飞机被摧毁时的动画;
- 我方飞机初始化时候的坐标;
- 由于我们这个阶段是仅仅在web网页上进行控制的,因此,在操控上实现的是传统网页游戏那种WASD来控制我方飞机的上下左右移动;
大致上这三个功能在本文这个阶段是最主要的,再来估计一下如果要实现这几个功能可能要用到什么实现逻辑 第一个,我方飞机样式 飞机样式,这一块其实包含两部分:
- 第一部分,我方飞机,其实就是DOM元素,加上背景图,这一点应该是毋庸置疑的;
- 第二部分,爆炸动画,当敌机撞到我们控制的飞机时,我方飞机会被摧毁,被摧毁时会有一个爆炸的效果动画,既然是动画那肯定是老传统了,通过 CSS3 的
animation 实现;
第二个,出生坐标 既然是坐标,那坐标肯定是有x和y的,从效果上看,我方飞机处于的位置在于屏幕的下方且处于中间位置; 第三个,位移 重头戏来了,通过之前我们知道移动是靠的 requestAnimationFrame 实现的,那操控呢,就是就是键盘事件,当按下指定的按键后,触发对应的控制位移的方法,这样就实现了对应方向的位移;
4.2 我方飞机的样式
先来看看我放飞机的样式,它的的素材图如下: 可能看到这会有一点点疑惑,不对啊,怎么是这种图片,我们其实可以这么理解,先看一个示意图 正常情况下,默认显示最左边的我方飞机素材图,也就是 正常状态下的飞机,当这个飞机模型接收到被摧毁的信号时,触发爆炸动画,此时的我方飞机模型只需要通过 animation 将右侧隐藏的图片分步显示出来,形成一个动画,具体完整代码如下:
<template>
<view class="plane" :class="getClass"></view>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => {
return {};
}
},
},
computed: {
getClass() {
const classStyle = [`plane`];
const explosion = {};
explosion[`plane_animate`] = this.isExplosion;
classStyle.push(explosion);
return classStyle;
}
},
};
</script>
<style scoped lang="scss">
.plane {
position: fixed;
width: 98px;
height: 122px;
z-index: 3;
background: url(@/static/images/plane.png) no-repeat left top;
}
.plane_animate {
animation: plane_animate 0.5s steps(5) both infinite;
-webkit-animation: plane_animate 0.5s steps(5) both infinite;
}
@keyframes plane_animate {
0% {
background-position: 0 0;
}
100% {
background-position: -490px 0;
}
}
@-webkit-keyframes plane_animate {
0% {
background-position: 0 0;
}
100% {
background-position: -490px 0;
}
}
</style>
完整代码差不多就是如上,我们分布看一下,首先是template
<template>
<view :class="getClass"></view>
</template>
template部分非常简洁,通过一个名为getClass的计算属性获取完整的类名数组,我们细看一下这个计算属性
getClass() {
const classStyle = [`plane`];
const explosion = {};
explosion[`plane_animate`] = this.isExplosion;
classStyle.push(explosion);
return classStyle;
}
通这个属性中可以看到,最终返回出去的是一个数组,数组一共有两项,第一项是一个字符串,第二项是一个对象,举个例子吧,假设this.data.type的值是1,那么这个计算属性最终返回的结果是这个
["plane",{"plane_animate":this.data.isExplosion}]
我们放到css中去看一下,这个两个对应的样式
.plane {
position: fixed;
width: 98px;
height: 122px;
z-index: 3;
background: url(@/static/images/plane.png) no-repeat left top;
}
标准DOM样式,宽,高,背景图等等,很明显这一个用于设定DOM基本属性的,另外一个
.plane_animate {
animation: plane_animate 0.5s steps(5) both infinite;
-webkit-animation: plane_animate 0.5s steps(5) both infinite;
}
/* 飞机-爆炸效果 */
@keyframes plane_animate {
0% {
background-position: 0 0;
}
100% {
background-position: -490px 0;
}
}
@-webkit-keyframes plane_animate {
0% {
background-position: 0 0;
}
100% {
background-position: -490px 0;
}
}
这是一个动画,关于animation的用法具体可以参考之前的博文,启动页的那一篇,里面有详细的用法解释,简单的说,就是当 this.data.isExplosion 的值为true的时候,plane_animate这个类名将会被添加到这个DOM上,当类名被添加的同时会立即执行名为plane_animate的动画,该动画会在0.5秒内分成5步显示完,动画改变的背景图的坐标,将x轴从0变到了-490px,实现了动画效果; 具体效果图如下:
4.3 我方飞机生成的实现
飞机的创建流畅和之前敌机的创建流程其实差不多,大致的流程是,我方的文件是一个单独的.vue文件,里面有我方飞机模型的所有参数功能,当参数创建成功后存入飞机数组,通过v-for将飞机数组遍历,其它的类似于是否爆炸等参数的则是通过props传入的飞机组件,
因此,在父级,我们可以这么写
<template v-if="isStart">
<Plane v-for="plane in planeData" :data="plane" :params="config" :key="plane.id" @addBullet="addBullet" />
</template>
<script>
import Plane from '../view/plane/plane.vue';
export default {
data() {
return {
planeData: [],
};
},
components: { Plane },
methods: {
initPlane() {
const data = {
width: 98,
height: 122,
x: (this.config.winWdith - 98) / 2,
y: this.config.winHeight - 122,
id: `plane` + new Date().getTime(),
isExplosion: true
};
this.planeData.push(data);
}
}
};
</script>
子组件,也就是我方飞机组件则是如下,通过props将父组件中的飞机配置参数传入子组件
<script>
export default {
props: {
data: {
type: Object,
default: () => {
return {};
}
},
},
};
</script>
4.4 我方飞机坐标的实现
从效果图中我们其实可以看到,我方飞机y轴上飞机的生成位置处于屏幕的下半方,在x轴上处于
因此,实际上我们只需要固定X轴和Y轴的值即可~,先看X轴的代码
(this.config.winWdith - 98) / 2
在x轴上显示获取屏幕的宽度,减掉飞机的宽度再除以2,这就是飞机模型离左边距的距离,Y轴就更简单了
this.config.winHeight - 122
直接屏幕高度减掉一个固定值即可,接着就将参数传递到子组件接收
<template>
<view class="plane" :class="getClass" :style="{ left: data.x + 'px', top: data.y + 'px' }"></view>
</template>
在子组件中直接 设置left属性 以及 top属性 来控制我方飞机的的初始坐标方位;
4.5 我方飞机的操控与位移
我方飞机的操控是按键盘的WASD来控制飞机的前后左右移动,因此我们先看一下实现思路:
简单的说就是 飞机模型被加载的时候给当前页面添加一个监听事件,监听整个键盘的按键,当WASD这几个按键被按下的时候 打开对应的开关,因此循环执行飞机位移动画时得到位移方向确认,因此飞机即可向对应方向位移
4.5.1 初始化操作事件
操作事件的初始化,初始化一共分为两种,一种是按键按下时触发控制器为开,一种是按键松开时触发控制器为关,先是控制器为开的操作
document.addEventListener('keydown', e => {
switch (e.keyCode) {
//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;
}
});
通过绑定 keydown 和 keyup 开控制开关的开启与关闭;
4.5.2 位移函数
先上代码
move() {
if (this.keyTop && this.data.y > 0) {
this.data.y -= 5;
}
if (this.keyBottom && this.data.y + 122 < this.params.winHeight) {
this.data.y += 5;
}
if (this.keyLeft && this.data.x > 0) {
this.data.x -= 5;
}
if (this.keyRight && this.data.x + 98 < this.params.winWdith) {
this.data.x += 5;
}
},
initMove() {
this.moveTimer = () => {
this.move();
requestAnimationFrame(this.moveTimer);
};
this.moveTimer();
},
整个位移有两段,分别 实现位移动画 以及 位移动画的具体实现,一段一段看
initMove() {
this.moveTimer = () => {
this.move();
requestAnimationFrame(this.moveTimer);
};
this.moveTimer();
},
这一段很明显,在敌机模型中也被用到了,作用就是通过 requestAnimationFrame 实现了飞机位移动画,位移的具体实现则是通过move()这个函数实现的
move() {
if (this.keyTop && this.data.y > 0) {
this.data.y -= 5;
}
if (this.keyBottom && this.data.y + 122 < this.params.winHeight) {
this.data.y += 5;
}
if (this.keyLeft && this.data.x > 0) {
this.data.x -= 5;
}
if (this.keyRight && this.data.x + 98 < this.params.winWdith) {
this.data.x += 5;
}
}
这里有4个判断,用于判断当前哪个开关是处于开关,当对应的开关处于打开的状态时,那么飞机将在对应方向上实现位移:
- 第一个if,按键是W时执行,飞机在Y轴上的位移轨迹,每次在Y轴上减少5个像素,当然坐标不能小于0,小于0的话就到屏幕外了;
- 第二个if,按键是S时执行,飞机在Y轴上的位移轨迹,每次在Y轴上增加5个像素,坐标加飞机本身高度不能大于屏幕高度,大于的话就到屏幕外了;
- 第三个if,按键是A时执行,飞机在X轴上的位移轨迹,每次在X轴上减少5个像素,X轴上的坐标也不能小于0,小于的话就到屏幕外了;
- 第四个if,按键是D时执行,飞机在X轴上的位移轨迹,每次在X轴上增加5个像素,坐标加飞机本身宽度不能大于屏幕宽度,大于的话就到屏幕外了;
这就是每次执行 requestAnimationFrame 的内部动画逻辑;
五. 阶段性展示
到这一章节,我们已经实现的效果图如下:
六. 小结
本文主要概述了我方飞机的实现,主要包含:
- 飞机样式:其实就是设定好DOM,加入背景图,预设好爆炸的CSS动画;
- 飞机生成:创建我方飞机配置参数,加入缓存数组,通过v-for指令循环生成飞机;
- 飞机坐标:我方飞机的坐标位于屏幕的下方,是固定的;
- 飞机操作位移:位移通过
requestAnimationFrame 实现,并在加载我方飞机时 添加按键监听,监听WASD这几个按键,当这几个按键被按下时,触发对应的位移效果;
但看飞机模型的代码量,也不算复杂,耐心看看还可以明白的,接下来就是碰撞检测了,这一块稍微涉及到了一些计算,当然,问题不大~ 都已经看到这里了,点个赞吧,点个关注吧,谢谢
|