VUE+Canvas實(shí)現(xiàn)財(cái)神爺接元寶小游戲
之前的canvas小游戲系列歡迎大家戳:
《VUE實(shí)現(xiàn)一個(gè)Flappy Bird~~~》
《VUE+Canvas 實(shí)現(xiàn)桌面彈球消磚塊小游戲》
《VUE+Canvas實(shí)現(xiàn)雷霆戰(zhàn)機(jī)打字類(lèi)小游戲》
如標(biāo)題,這個(gè)游戲大家也玩過(guò),隨處可見(jiàn),左右方向鍵控制財(cái)神移動(dòng),接住從天而降的金元寶等,時(shí)間一到,則游戲結(jié)束。先來(lái)看一下效果:
相比于之前的雷霆戰(zhàn)機(jī)要打出四處飛的子彈,這次元素的運(yùn)動(dòng)軌跡就很單一了,垂直方向的珠寶和水平移動(dòng)的財(cái)神爺,類(lèi)似于之前的代碼,這里就說(shuō)一下關(guān)鍵步驟點(diǎn)吧:
1、鍵盤(pán)控制水平移動(dòng)的財(cái)神爺
這個(gè)很簡(jiǎn)單,同理于《VUE+Canvas 實(shí)現(xiàn)桌面彈球消磚塊小游戲》滑塊的控制:
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、從天而降的珠寶
這個(gè)也很簡(jiǎn)單,但要注意的是,珠寶的初始x值不能隨機(jī)取0~clientWidth了,因?yàn)檫@樣很容易造成珠寶堆積在一起,影響了游戲的可玩性,所以珠寶最好是分散在不同的軌道上,這里我們把畫(huà)布寬度分為5條軌道,初始珠寶的時(shí)候,我們就把珠寶分散在軌道上,并且y值隨機(jī)在一定高度造成參差。而后新生成的珠寶都依據(jù)軌道分布來(lái)生成,避免珠寶擠在一起。
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 ) { // 判斷和財(cái)神的觸碰范圍 _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函數(shù)過(guò)濾掉應(yīng)該消失的珠寶,如果用for+splice+i--的方法會(huì)造成抖動(dòng)。
然后給予每個(gè)珠寶隨機(jī)的運(yùn)動(dòng)速度,當(dāng)珠寶進(jìn)入財(cái)神爺?shù)膱D片范圍時(shí)則累加相應(yīng)分?jǐn)?shù)。
3、倒計(jì)時(shí)圓環(huán)
設(shè)置倒計(jì)時(shí)30s,那么在requestAnimationFrame的回調(diào)里計(jì)算當(dāng)前時(shí)間與上次時(shí)間戳毫秒差值是否大于1000,實(shí)現(xiàn)秒的計(jì)算,然后取另一時(shí)間戳累加progress,實(shí)現(xiàn)圓環(huán)的平滑移動(dòng)。
drawCountDown() { // 畫(huà)進(jìn)度環(huán) 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(); // 畫(huà)內(nèi)填充圓 _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(); } })();
至此,一個(gè)非常簡(jiǎn)單的財(cái)神爺接元寶的小游戲就完成了,當(dāng)然可以為了增加難度,設(shè)置不間斷地丟炸彈這一環(huán)節(jié),原理同珠寶的運(yùn)動(dòng)是一樣的。
下面還是附上全部代碼,供大家參考學(xué)習(xí):
<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/財(cái)神爺.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() { // 畫(huà)進(jìn)度環(huán) 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(); // 畫(huà)內(nèi)填充圓 _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 ) { // 判斷和財(cái)神的觸碰范圍 _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>
到此這篇關(guān)于VUE+Canvas實(shí)現(xiàn)財(cái)神爺接元寶小游戲的文章就介紹到這了,更多相關(guān)vue接元寶游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Vue使用Canvas生成隨機(jī)大小且不重疊圓
- Vue使用canvas實(shí)現(xiàn)圖片壓縮上傳
- vue+canvas繪制時(shí)間軸的方法
- VUE+Canvas實(shí)現(xiàn)簡(jiǎn)單五子棋游戲的全過(guò)程
- 如何用VUE和Canvas實(shí)現(xiàn)雷霆戰(zhàn)機(jī)打字類(lèi)小游戲
- VUE+Canvas 實(shí)現(xiàn)桌面彈球消磚塊小游戲的示例代碼
- Vue使用鼠標(biāo)在Canvas上繪制矩形
- vue使用canvas實(shí)現(xiàn)移動(dòng)端手寫(xiě)簽名
- vue+canvas實(shí)現(xiàn)拼圖小游戲
- vue使用canvas手寫(xiě)輸入識(shí)別中文
相關(guān)文章
利用vue實(shí)現(xiàn)打印頁(yè)面的幾種方法總結(jié)
在項(xiàng)目里有個(gè)打印功能,將頁(yè)面的部分內(nèi)容打印出來(lái),所以下面這篇文章主要給大家介紹了關(guān)于利用vue實(shí)現(xiàn)打印頁(yè)面的幾種方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11uniApp?h5項(xiàng)目如何通過(guò)命令行打包并生成指定路徑及文件名稱(chēng)
用uni-app來(lái)寫(xiě)安卓端,近日需要將程序打包為H5放到web服務(wù)器上,經(jīng)過(guò)一番折騰,這里給大家分享下,這篇文章主要給大家介紹了關(guān)于uniApp?h5項(xiàng)目如何通過(guò)命令行打包并生成指定路徑及文件名稱(chēng)的相關(guān)資料,需要的朋友可以參考下2024-02-02vue前端img訪問(wèn)鑒權(quán)后端進(jìn)行攔截的代碼示例
路由攔截是一種在用戶(hù)訪問(wèn)特定頁(yè)面之前對(duì)其進(jìn)行攔截和處理的機(jī)制,下面這篇文章主要給大家介紹了關(guān)于vue前端img訪問(wèn)鑒權(quán)后端進(jìn)行攔截的相關(guān)資料,需要的朋友可以參考下2024-03-03vue實(shí)現(xiàn)循環(huán)滾動(dòng)圖片
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)循環(huán)滾動(dòng)圖片,多圖片輪播,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07Vue 數(shù)組和對(duì)象更新,但是頁(yè)面沒(méi)有刷新的解決方式
今天小編就為大家分享一篇Vue 數(shù)組和對(duì)象更新,但是頁(yè)面沒(méi)有刷新的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11vue數(shù)組對(duì)象排序的實(shí)現(xiàn)代碼
這篇文章主要介紹了vue數(shù)組對(duì)象排序的實(shí)現(xiàn)代碼,這里整理了詳細(xì)的代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-06-06vue長(zhǎng)按事件和點(diǎn)擊事件沖突的解決
這篇文章主要介紹了vue長(zhǎng)按事件和點(diǎn)擊事件沖突的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10在VUE中實(shí)現(xiàn)文件下載并判斷狀態(tài)的方法
今天小編就為大家分享一篇在VUE中實(shí)現(xiàn)文件下載并判斷狀態(tài)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11vue父組件傳值子組件報(bào)錯(cuò)Avoid?mutating?a?prop?directly解決
這篇文章主要為大家介紹了vue父組件傳值子組件報(bào)錯(cuò)Avoid?mutating?a?prop?directly解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09