之前的canvas小游戏系列欢迎大家戳:
《VUE实现一个Flappy Bird~~~》
《猜单词游戏》
《VUE+Canvas 实现桌面弹球消砖块小游戏》
《VUE+Canvas实现雷霆战机打字类小游戏》
如标题,这个游戏大家也玩过,随处可见,左右方向键控制财神移动,接住从天而降的金元宝等,时间一到,则游戏结束。先来看一下效果:
相比于之前的雷霆战机要打出四处飞的子弹,这次元素的运动轨迹就很单一了,垂直方向的珠宝和水平移动的财神爷,类似于之前的代码,这里就说一下关键步骤点吧:
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,实现圆环的平滑移动。
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); }
(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+Canvas实现财神爷接元宝小游戏的文章就介绍到这了,更多相关vue接元宝游戏内容请搜索NICE源码以前的文章或继续浏览下面的相关文章希望大家以后多多支持NICE源码!