如何用VUE和Canvas实现雷霆战机打字类小游戏

本日就来实现一个雷霆战机打字游戏,玩法很简朴,每一个“仇人”都是一些英文单词,键盘正确打出单词的字母,飞机就会发射一个个子弹没落“仇人”,每次需要击毙当前“仇人”后才气击毙下一个,一个比手速和单词纯熟度的游戏

首先来看看最终结果图:

如何用VUE和Canvas实现雷霆战机打字类小游戏

emmmmmmmmmmmmm,界面UI做的很简朴,先实现根基成果,再思量高峻上的UI吧。

首先依旧是来阐明界面构成:

(1)牢靠在画面底部中间的飞机;

(2)从画面上方随机发生的仇人(单词);

(3)从飞机头部发射出去,直奔仇人而去的子弹;

(4)游戏竣事后的分数显示。

这次的游戏和之前的比,举动的部门貌似更多且更巨大了。在flappy bird中,固然管道是举动的,可是小鸟的x坐标和管道的隔断、宽度始终稳定,较量容易计较界线;在弹球消砖块游戏中,木板和砖块都是相对简朴可能牢靠的坐标,只用鉴定弹球的界线和砖块的触会晤积就行。在雷霆战机消单词游戏中,无论是降落的方针单词,照旧飞出去的子弹,都有着各自的举动轨迹,可是子弹又要追寻着方针而去,所以存在着一个及时计较轨道的操纵。

万丈高楼平地起,说了那么多,那就从最简朴的开始着手吧!

1、牢靠在画面底部中间的飞机

这个很简朴没啥好说的,这里默认飞机宽度高度为40像素单元,然后将飞机画在画面的底部中间:

drawPlane() { let _this = this; _this.ctx.save(); _this.ctx.drawImage( _this.planeImg, _this.clientWidth / 2 - 20, _this.clientHeight - 20 - 40, 40, 40 ); _this.ctx.restore(); },

2、从画面上方随机发生的仇人

这里默认配置每次在画面中最多只呈现3个单词靶子,靶子的y轴移动速度为1.3,靶子的半径巨细为10:

const _MAX_TARGET = 3; // 画面中一次最多呈现的方针 const _TARGET_CONFIG = { // 靶子的牢靠参数 speed: 1.3, radius: 10 };

然后我们一开始要随机在词库数组里取出_MAX_TARGET个不反复的单词,并把剩下的词放进轮回词库this.wordsPool中去:

generateWord(number) { // 从池子里随机挑选一个词,不与已显示的词反复 let arr = []; for (let i = 0; i < number; i++) { let random = Math.floor(Math.random() * this.wordsPool.length); arr.push(this.wordsPool[random]); this.wordsPool.splice(random, 1); } return arr; }, generateTarget() { // 随机生成方针 let _this = this; let length = _this.targetArr.length; if (length < _MAX_TARGET) { let txtArr = _this.generateWord(_MAX_TARGET - length); for (let i = 0; i < _MAX_TARGET - length; i++) { _this.targetArr.push({ x: _this.getRandomInt( _TARGET_CONFIG.radius, _this.clientWidth - _TARGET_CONFIG.radius ), y: _TARGET_CONFIG.radius * 2, txt: txtArr[i], typeIndex: -1, hitIndex: -1, dx: (_TARGET_CONFIG.speed * Math.random().toFixed(1)) / 2, dy: _TARGET_CONFIG.speed * Math.random().toFixed(1), rotate: 0 }); } } }

可以看出,this.targetArr是存放方针工具的数组:

一个初始的方针有随机漫衍在画面宽度的x;

y轴值为直径;

txt记录靶子代表的单词;

typeIndex记录“轰炸”这个单词时正在敲击的字符索引下标(用来疏散已敲击字符和未敲击字符);

hitIndex记录“轰炸”这个单词时子弹轰炸的索引下标(因为子弹真正轰炸掉方针是在打完单词之后,究竟子弹有航行时间,所以通过hitIndex来判定子弹什么时候被击碎消失);

dx是靶子每帧在x轴偏移间隔;

dy是靶子每帧在y轴偏移间隔;

rotate配置靶子自转角度。

好了,生成了3个靶子,我们就让靶子先从上往下动起来吧:

drawTarget() { // 逐帧画方针 let _this = this; _this.targetArr.forEach((item, index) => { _this.ctx.save(); _this.ctx.translate(item.x, item.y); //配置旋转的中心点 _this.ctx.beginPath(); _this.ctx.font = "14px Arial"; if ( index === _this.currentIndex || item.typeIndex === item.txt.length - 1 ) { _this.drawText( item.txt.substring(0, item.typeIndex + 1), -item.txt.length * 3, _TARGET_CONFIG.radius * 2, "gray" ); let width = _this.ctx.measureText( item.txt.substring(0, item.typeIndex + 1) ).width; // 获取已敲击文字宽度 _this.drawText( item.txt.substring(item.typeIndex + 1, item.txt.length), -item.txt.length * 3 + width, _TARGET_CONFIG.radius * 2, "red" ); } else { _this.drawText( item.txt, -item.txt.length * 3, _TARGET_CONFIG.radius * 2, "yellow" ); } _this.ctx.closePath(); _this.ctx.rotate((item.rotate * Math.PI) / 180); _this.ctx.drawImage( _this.targetImg, -1 * _TARGET_CONFIG.radius, -1 * _TARGET_CONFIG.radius, _TARGET_CONFIG.radius * 2, _TARGET_CONFIG.radius * 2 ); _this.ctx.restore(); item.y += item.dy; item.x += item.dx; if (item.x < 0 || item.x > _this.clientWidth) { item.dx *= -1; } if (item.y > _this.clientHeight - _TARGET_CONFIG.radius * 2) { // 遇到底部了 _this.gameOver = true; } // 旋转 item.rotate++; }); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsjffs.html