《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

一. 前言

上一篇中主要分享的是 位移这个功能的实现,简单的说就是通过 requestAnimationFrame 这个window对象上提供的原生能力,将坐标改变这个过程与屏幕帧率吻合实现了动画化,这种动画化的效果不管是从性能上,还是从动画的流畅度上都远好于 setTimeoutsetInterval,当然也包括本文中提到的我方飞机,其实现过程也是依靠 requestAnimationFrame 实现的位移;
本文主要分享的内容为我方飞机的实现,耐心看完,或许你会所有收获~

二. 阅读对象与难度

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

  • Vue中的基础知识,包括v-for等常规用法;
  • 我方飞机的操控;
  • requestAnimationFrame 等等

具体内容可以参考以下的思维导图:
在这里插入图片描述

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

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

四. 我方飞机模型的实现

4.1 分析分析

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

  1. 首先要实现的就是我方飞机的样式,当然也包括我方飞机被摧毁时的动画;
  2. 我方飞机初始化时候的坐标;
  3. 由于我们这个阶段是仅仅在web网页上进行控制的,因此,在操控上实现的是传统网页游戏那种WASD来控制我方飞机的上下左右移动;

大致上这三个功能在本文这个阶段是最主要的,再来估计一下如果要实现这几个功能可能要用到什么实现逻辑
第一个,我方飞机样式
飞机样式,这一块其实包含两部分:

  • 第一部分,我方飞机,其实就是DOM元素,加上背景图,这一点应该是毋庸置疑的;
  • 第二部分,爆炸动画,当敌机撞到我们控制的飞机时,我方飞机会被摧毁,被摧毁时会有一个爆炸的效果动画,既然是动画那肯定是老传统了,通过 CSS3animation 实现;

第二个,出生坐标
既然是坐标,那坐标肯定是有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;
    }
  });

通过绑定 keydownkeyup 开控制开关的开启与关闭;

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这几个按键,当这几个按键被按下时,触发对应的位移效果;

但看飞机模型的代码量,也不算复杂,耐心看看还可以明白的,接下来就是碰撞检测了,这一块稍微涉及到了一些计算,当然,问题不大~
都已经看到这里了,点个赞吧,点个关注吧,谢谢

猜你喜欢

转载自blog.csdn.net/zy21131437/article/details/127477230