如标题,这个游戏大家也玩过,随处可见,左右方向键控制财神移动,接住从天而降的金元宝等,时间一到,则游戏结束。先来看一下效果:
相比于之前的雷霆战机要打出四处飞的子弹,这次元素的运动轨迹就很单一了,垂直方向的珠宝和水平移动的财神爷,类似于之前的代码,这里就说一下关键步骤点吧:
1、键盘控制水平移动的财神爷
这个很简单,同理于《VUE+Canvas 实现桌面弹球消砖块小游戏》滑块的控制:
drawCaishen() { let _this = this ; _this.ctx.save(); _this.ctx.drawImage( _this.caishenImg, _this.caishen.x, _this.caishen.y, 120, 120 ); _this.ctx.restore(); }, moveCaishen() { this .caishen.x += this .caishen.dx; if ( this .caishen.x > this .clientWidth - 120) { this .caishen.x = this .clientWidth - 120; } else if ( this .caishen.x < 0) { this .caishen.x = 0; } } |
2、从天而降的珠宝
这个也很简单,但要注意的是,珠宝的初始x值不能随机取0~clientWidth了,因为这样很容易造成珠宝堆积在一起,影响了游戏的可玩性,所以珠宝最好是分散在不同的轨道上,这里我们把画布宽度分为5条轨道,初始珠宝的时候,我们就把珠宝分散在轨道上,并且y值随机在一定高度造成参差。而后新生成的珠宝都依据轨道分布来生成,避免珠宝挤在一起。
generateTreasure() { let _this = this ; if (_this.treasureArr.length < MaxNum) { let random = Math.floor(Math.random() * TreasureNames.length); let channel = _this.getRandomArbitrary(1, 5); _this.treasureArr.push({ x: _this.channelWidth * (1 / 2 + (channel - 1)) - 30, y: 0, name: TreasureNames[random], speed: _this.getRandomArbitrary(2, 4) }); } }, filterTreasure(item) { let _this = this ; if ( item.x <= _this.caishen.x + 110 && item.x >= _this.caishen.x && item.y > _this.caishen.y ) { // 判断和财神的触碰范围 _this.score += _this.treasureObj[item.name].score; return false ; } if (item.y >= _this.clientHeight) { return false ; } return true ; }, drawTreasure() { let _this = this ; _this.treasureArr = _this.treasureArr.filter(_this.filterTreasure); _this.treasureArr.forEach(item => { _this.ctx.drawImage( _this.treasureObj[item.name].src, item.x, item.y, 60, 60 ); item.y += item.speed; }); }, getRandomArbitrary(min, max) { return Math.random() * (max - min) + min; } |
这里用filter函数过滤掉应该消失的珠宝,如果用for+splice+i--的方法会造成抖动。
然后给予每个珠宝随机的运动速度,当珠宝进入财神爷的图片范围时则累加相应分数。
3、倒计时圆环
设置倒计时30s,那么在requestAnimationFrame的回调里计算当前时间与上次时间戳毫秒差值是否大于1000,实现秒的计算,然后取另一时间戳累加progress,实现圆环的平滑移动。
|
( function animloop() { _this.ctx.clearRect(0, 0, _this.clientWidth, _this.clientHeight); _this.loop(); animationId = window.requestAnimationFrame(animloop); if (_this.countDown === 0) { _this.gameOver = true ; window.cancelAnimationFrame(animationId); } if (Date.now() - _this.timeTag >= 1000) { _this.countDown--; _this.timeTag = Date.now(); } })(); |
至此,一个非常简单的财神爷接元宝的小游戏就完成了,当然可以为了增加难度,设置不间断地丢炸弹这一环节,原理同珠宝的运动是一样的。
下面还是附上全部代码,供大家参考学习:
?<template> <div class= "caishen" > <canvas id= "caishen" width= "1200" height= "750" ></canvas> <div class= "container" v- if = "gameOver" > <div class= "dialog" > <p class= "once-again" >恭喜!</p> <p class= "once-again" >本回合夺宝:{{ score }}分</p> </div> </div> </div> </template> <script> const TreasureNames = [ "yuanbao" , "tongqian" , "jintiao" , "shuijin_red" , "shuijin_blue" , "fudai" ]; let animationId = null ; let countDownInit = 0; const MaxNum = 5; export default { name: "CaiShen" , data() { return { score: 0, ctx: null , caishenImg: null , clientWidth: 0, clientHeight: 0, channelWidth: 0, caishen: { x: 0, y: 0, speed: 8, dx: 0 }, progress: 0, countDown: 30, timeTag: Date.now(), timeTag2: Date.now(), treasureArr: [], gameOver: false , treasureObj: { yuanbao: { score: 5, src: null }, tongqian: { score: 2, src: null }, jintiao: { score: 10, src: null }, shuijin_red: { score: 20, src: null }, shuijin_blue: { score: 15, src: null }, fudai: { score: 8, src: null } } }; }, mounted() { let _this = this ; let container = document.getElementById( "caishen" ); _this.ctx = container.getContext( "2d" ); _this.clientWidth = container.width; _this.clientHeight = container.height; _this.channelWidth = Math.floor(_this.clientWidth / 5); _this.caishenImg = new Image(); _this.caishenImg.src = require( "@/assets/img/caishen/财神爷.png" ); _this.initTreasures(); countDownInit = _this.countDown; _this.caishen.x = _this.clientWidth / 2 - 60; _this.caishen.y = _this.clientHeight - 120; document.onkeydown = function (e) { let key = window.event.keyCode; if (key === 37) { // 左键 _this.caishen.dx = -_this.caishen.speed; } else if (key === 39) { // 右键 _this.caishen.dx = _this.caishen.speed; } }; document.onkeyup = function (e) { _this.caishen.dx = 0; }; _this.caishenImg.onload = function () { ( function animloop() { _this.ctx.clearRect(0, 0, _this.clientWidth, _this.clientHeight); _this.loop(); animationId = window.requestAnimationFrame(animloop); if (_this.countDown === 0) { _this.gameOver = true ; window.cancelAnimationFrame(animationId); } if (Date.now() - _this.timeTag >= 1000) { _this.countDown--; _this.timeTag = Date.now(); } })(); }; }, methods: { initTreasures() { let _this = this ; Object.keys(_this.treasureObj).forEach(key => { _this.treasureObj[key].src = new Image(); _this.treasureObj[ key ].src.src = require(`@/assets/img/caishen/${key}.png`); }); for (let i = 0; i < MaxNum; i++) { let random = Math.floor(Math.random() * TreasureNames.length); _this.treasureArr.push({ x: _this.channelWidth * (1 / 2 + i) - 30, y: _this.getRandomArbitrary(0, 20), name: TreasureNames[random], speed: _this.getRandomArbitrary(2, 4) }); } }, loop() { let _this = this ; _this.drawCountDown(); _this.drawCaishen(); _this.moveCaishen(); _this.generateTreasure(); _this.drawTreasure(); _this.drawScore(); }, drawCaishen() { let _this = this ; _this.ctx.save(); _this.ctx.drawImage( _this.caishenImg, _this.caishen.x, _this.caishen.y, 120, 120 ); _this.ctx.restore(); }, moveCaishen() { this .caishen.x += this .caishen.dx; if ( this .caishen.x > this .clientWidth - 120) { this .caishen.x = this .clientWidth - 120; } else if ( this .caishen.x < 0) { this .caishen.x = 0; } }, drawScore() { let _this = this ; _this.ctx.beginPath(); _this.ctx.fillStyle = "#fff" ; _this.ctx.textAlign = "center" ; _this.ctx.textBaseline = "middle" ; _this.ctx.fillText(_this.score + "分" , 30, _this.clientHeight - 10); _this.ctx.closePath(); }, drawCountDown() { // 画进度环 let _this = this ; _this.progress += Date.now() - _this.timeTag2; _this.timeTag2 = Date.now(); _this.ctx.beginPath(); _this.ctx.moveTo(50, 50); _this.ctx.arc( 50, 50, 40, Math.PI * 1.5, Math.PI * (1.5 + 2 * (_this.progress / (countDownInit * 1000))), false ); _this.ctx.closePath(); _this.ctx.fillStyle = "yellow" ; _this.ctx.fill(); // 画内填充圆 _this.ctx.beginPath(); _this.ctx.arc(50, 50, 30, 0, Math.PI * 2); _this.ctx.closePath(); _this.ctx.fillStyle = "#fff" ; _this.ctx.fill(); // 填充文字 _this.ctx.font = "bold 16px Microsoft YaHei" ; _this.ctx.fillStyle = "#333" ; _this.ctx.textAlign = "center" ; _this.ctx.textBaseline = "middle" ; _this.ctx.moveTo(50, 50); _this.ctx.fillText(_this.countDown + "s" , 50, 50); }, filterTreasure(item) { let _this = this ; if ( item.x <= _this.caishen.x + 110 && item.x >= _this.caishen.x && item.y > _this.caishen.y ) { // 判断和财神的触碰范围 _this.score += _this.treasureObj[item.name].score; return false ; } if (item.y >= _this.clientHeight) { return false ; } return true ; }, drawTreasure() { let _this = this ; _this.treasureArr = _this.treasureArr.filter(_this.filterTreasure); _this.treasureArr.forEach(item => { _this.ctx.drawImage( _this.treasureObj[item.name].src, item.x, item.y, 60, 60 ); item.y += item.speed; }); }, getRandomArbitrary(min, max) { return Math.random() * (max - min) + min; }, generateTreasure() { let _this = this ; if (_this.treasureArr.length < MaxNum) { let random = Math.floor(Math.random() * TreasureNames.length); let channel = _this.getRandomArbitrary(1, 5); _this.treasureArr.push({ x: _this.channelWidth * (1 / 2 + (channel - 1)) - 30, y: 0, name: TreasureNames[random], speed: _this.getRandomArbitrary(2, 4) }); } } } }; </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped lang= "scss" > #caishen { background-color: #b00600; background-image: url( "~assets/img/caishen/brick-wall.png" ); } .container { position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0, 0, 0, 0.3); text-align: center; font-size: 0; white-space: nowrap; overflow: auto; } .container:after { content: "" ; display: inline-block; height: 100%; vertical-align: middle; } .dialog { width: 400px; height: 300px; background: rgba(255, 255, 255, 0.5); box-shadow: 3px 3px 6px 3px rgba(0, 0, 0, 0.3); display: inline-block; vertical-align: middle; text-align: left; font-size: 28px; color: #fff; font-weight: 600; border-radius: 10px; white-space: normal; text-align: center; .once-again-btn { background: #1f9a9a; border: none; color: #fff; } } </style> |
到此,关于“如何用vue框架写个接元宝小游戏”的学习就结束了,希望能够解决大家的疑惑,另外大家动手实践也很重要,对大家加深理解和学习很有帮助。如果想要学习更多的相关知识,欢迎关注群英网络,小编每天都会给大家分享实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
长按识别二维码并关注微信
更方便到期提醒、手机管理