您好,登錄后才能下訂單哦!
這篇文章主要介紹“p5.js怎么實現聲音控制警察抓小偷游戲”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“p5.js怎么實現聲音控制警察抓小偷游戲”文章能幫助大家解決問題。
之前一直用原生canvas寫小游戲,很多邏輯都需要自己一點點封裝,最近看了下p5.js,哇哦,好的很嘞!就用它開發了一款名為“警察抓小偷”的游戲。這是一款非常有趣的游戲,玩家扮演警察追捕小偷,通過大聲喊話來控制警察的速度,抓住小偷。游戲中,小偷會在棋盤上移動,而警察則會向小偷靠近。如果警察追上小偷,就算警察勝利;如果小偷逃脫到棋盤的最下方,那么小偷勝利。
點擊開始游戲按鈕小偷會在棋盤上移動,游戲會調用麥克風,玩家通過大聲狂呼亂叫,此時警察則會向小偷靠近,你喊得越大聲,警察移動速度越快,當警察追上小偷,就算警察勝利;如果小偷跑到的右下方出口處逃脫,那么小偷勝利。
p5.js
庫基于 Processing,是一個 JavaScript 庫,可以輕松地創建交互式圖形和動態圖像。它的核心是使用 HTML5 Canvas 元素來創建圖形。p5.js 提供了簡單易用的 API,讓編寫和理解代碼變得更加容易。
p5.sound.min.js
是 p5.js 庫的音頻處理插件,它基于 Web Audio API,可以加載、播放和操縱音頻文件。Web Audio API 是 HTML5 中的一項標準,提供了低延遲、高品質的音頻處理能力,可以通過 JavaScript 對音頻進行混合、變形、過濾等操作。
設計游戲規則和界面
使用p5.js繪制游戲界面和元素,這比自己用原生canvas寫迅速多了。
實現小偷和警察的移動邏輯
實現通過音量控制警察的速度
實現勝利邏輯和動畫效果
添加開始游戲和重新開始的按鈕
預加載音樂和圖片資源
核心功能點主要包括繪制棋盤、小偷的移動邏輯、警察的移動邏輯以及勝利邏輯函數。大家著重理解這些開發思路。
在 p5.js 的 draw()
函數中,調用rect()
方法來繪制20X15的游戲棋盤,image()
方法來繪制小偷、警察元素,并實時繪制小偷和警察的新位置和動態更新頁面上玩家的麥克風音量刻度條,通過whoWin
變量檢查是否勝利,以及執行相應的文案繪制效果。
//循環體 function draw() { //..... // 繪制格子盤 for (let i = 1; i < col + 1; i++) { for (let j = 0; j < row; j++) { fill('pink') rect(i * gridW, j * gridH, gridW, gridH - 1); // 將高度改為gridH-1 } } // 繪制小偷和警察 image(thiefImg, thiefXPos, thiefYPos - 5, gridW, gridH + 5); image(policeImg, policeXPos, policeYPos - 20, gridW, gridH + 25); if (whoWin === 'police') { fill(255, 0, 0); textSize(30) text("恭喜你抓住小偷啦,警察勝利嘍!", col * gridW / 6, 60); } if (whoWin === 'thief') { fill(255, 0, 0); textSize(30) text("小樣,還想抓住我,哈哈哈", col * gridW / 5, 60); } //..... // 顯示當前玩家數據 thiefVoiceValNode.innerText = thiefProgressNode.value = policeDir / policeStepLen; policeValNode.innerText = policeProgressNode.value = Math.abs(policeDir); }
小偷的移動邏輯是每隔一定時間在 X 軸方向上以固定的步長移動,步長由stepLen
常量控制,在到達棋盤邊界if (thiefXPos >= (col + 1) * gridW)
改變運動方向dir = -stepLen
,并在 Y 軸方向上增加一個棋盤格子高thiefYPos += gridH
。小偷的移動步長可以通過調整 stepLen
的值來改變,值越大,小偷移動的越快。
警察的移動邏輯比較復雜,需要根據下面代碼中getVoiceSize()
獲取的音量值來動態調整警察的速度policeDir = Math.floor(voiceSize * 100) / 10 * policeStepLen
。通過 setInterval()
函數來定時改變警察的橫坐標的位置policeXPos += policeDir;
,并根據警察的位置和小偷的位置來判斷是否勝利。
//獲取音量大小 async function getVoiceSize() { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true } });// echoCancellation: true以減少回音和噪聲的影響 const audioContext = new AudioContext(); const source = audioContext.createMediaStreamSource(stream); const processor = audioContext.createScriptProcessor(1024, 1, 1); source.connect(processor); processor.connect(audioContext.destination); processor.onaudioprocess = function (event) { const buffer = event.inputBuffer.getChannelData(0); const sum = buffer.reduce((acc, val) => acc + val ** 2, 0);//計算音量大小 voiceSize = Math.sqrt(sum / buffer.length); }; } catch (err) { console.log('getUserMedia error: ', err); } }
在開始游戲點擊事件的回調函數中,判斷游戲勝利的條件并播放勝利音樂。在 draw()
函數中通過whoWin
來判斷游戲勝利的條件繪制頁面上的文案。當游戲勝利時,頁面上繪制對應文案并播放勝利音樂。
//開始游戲事件 startBtn.addEventListener('click', () => { //....省略其它邏輯代碼 //賊勝利邏輯 if (thiefYPos >= row * gridH) { winLogicFunc('thief') } //警察勝利邏輯 if (policeYPos === thiefYPos && (policeStepLen > 0 && policeXPos >= thiefXPos || policeStepLen < 0 && Math.abs(policeXPos) <= thiefXPos)) { winLogicFunc('police') } }, moveTime); });
為了增加游戲的趣味性,可以考慮增加更多的道具和障礙物。
道具:可以設置一些可以幫助小偷逃脫警察追捕的道具,例如加速藥水、隱身衣、煙霧彈等。這些道具可以增加小偷的移動速度、暫時讓小偷隱身躲避警察的視線、或者釋放煙霧干擾警察的追蹤。
障礙物,可以設置一些可以對小偷和警察造成影響的障礙物,例如路障、警車、人群等。這些障礙物可以阻礙小偷的逃脫,或者讓警察無法前進,從而給小偷爭取時間。
除了增加道具和障礙物,我們還可以增加多種不同的游戲模式,例如多人模式、計時模式等等。
多人模式:可以讓兩個玩家分別扮演小偷和警察,進行對抗。
計時模式中,可以設置一個時間限制,讓警察在規定時間內抓住小偷。
還有可以美化頁面還有音效、動效等,讓游戲更加豐富多彩,讓玩家有更多的選擇和挑戰。
為了避免文章過于臃腫,這里只放js的實現代碼,對于頁面布局代碼可以到我的github上去閱讀代碼,代碼里都有豐富的注釋,相信大家可以快速讀懂代碼,如果我用原生canvas開發,估計工作量會大很多。不過,代碼在封裝方面還有待提高,除此之外,玩法也還有更多創意有待實現。
各位如果更好玩的實現玩法,不妨把代碼fork
過去實現,記得評論區告訴我,我期待你的實現。
const gridW = 30; //棋盤格子寬 const gridH = 30; //棋盤格子高 const row = 15; //棋盤行數 const col = 20; //棋盤列數 let sone = null;//勝利音樂 let whoWin = ''; //誰勝利可選值'police|thief' let interval = null;//循環句柄 const moveTime = 16;//循環時間 let thiefImg = null;//小偷圖片 let thiefXPos = 40;//小偷橫坐標 let thiefYPos = 0;//小偷縱坐標 const stepLen = 2; //小偷移動步長(速度) let policeImg = null;//警察圖片 let policeXPos = 0;//警察橫坐標 let policeYPos = 0;//警察縱坐標 let policeStepLen = 2;//警察移動步長基數 let policeDir = 0; //警察動態移動步長(速度) let voiceSize = 0; //獲取DOM節點 const thiefProgressNode = document.getElementById('progress'); const thiefVoiceValNode = document.getElementById('voiceVal'); const policeProgressNode = document.getElementById('policeProgress'); const policeValNode = document.getElementById('policeVal'); const startBtn = document.getElementById('start-btn'); const restartBtn = document.getElementById('restart-btn'); const thiefWinGifNode = document.getElementById('thiefGif'); //小偷勝利動圖 const policeWinGifNode = document.getElementById('policeGif'); //警察勝利動圖 //預加載圖片 function preload() { song = loadSound('./resources/勝利配樂.mp3'); thiefImg = loadImage('./resources/thief.png'); policeImg = loadImage('./resources/police.png'); } //初次調用 function setup() { createCanvas(col * gridW + 60, row * gridH).parent('gameCanvas') document.querySelector('.page').style.cssText = `width:${(col + 2) * gridW}px` } //循環體 function draw() { noStroke(); background('#fff'); // 繪制格子盤 for (let i = 1; i < col + 1; i++) { for (let j = 0; j < row; j++) { fill('pink') rect(i * gridW, j * gridH, gridW, gridH - 1); // 將高度改為gridH-1 } } // 繪制小偷和警察 image(thiefImg, thiefXPos, thiefYPos - 5, gridW, gridH + 5); image(policeImg, policeXPos, policeYPos - 20, gridW, gridH + 25); if (whoWin === 'police') { fill(255, 0, 0); textSize(30) text("恭喜你抓住小偷啦,警察勝利嘍!", col * gridW / 6, 60); } if (whoWin === 'thief') { fill(255, 0, 0); textSize(30) text("小樣,還想抓住我,哈哈哈", col * gridW / 5, 60); } //繪制通道 for (let i = 1; i < row; i++) { fill('pink'); (i % 2 === 0) ? rect((col + 1) * gridW, gridH * i - 1, gridW, 3) : rect(0, gridH * i - 1, gridW, 3); } //繪制出口文字 fill(0, 0, 0); textSize(12) text("出口", (col + 1) * gridW + 2, row * gridH - 10); // 顯示當前玩家數據 thiefVoiceValNode.innerText = thiefProgressNode.value = policeDir / policeStepLen; policeValNode.innerText = policeProgressNode.value = Math.abs(policeDir); } //獲取音量大小 async function getVoiceSize() { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true } });// echoCancellation: true以減少回音和噪聲的影響 const audioContext = new AudioContext(); const source = audioContext.createMediaStreamSource(stream); const processor = audioContext.createScriptProcessor(1024, 1, 1); source.connect(processor); processor.connect(audioContext.destination); processor.onaudioprocess = function (event) { const buffer = event.inputBuffer.getChannelData(0); const sum = buffer.reduce((acc, val) => acc + val ** 2, 0);//計算音量大小 voiceSize = Math.sqrt(sum / buffer.length); }; } catch (err) { console.log('getUserMedia error: ', err); } } //勝利邏輯函數 function winLogicFunc(who) { if (who == 'thief' || who == 'police') { clearInterval(interval); song.play(); whoWin = who noLoop(); (who === 'thief' ? thiefWinGifNode : policeWinGifNode).style.visibility = 'visible'; } } //開始游戲事件 startBtn.addEventListener('click', () => { getVoiceSize() startBtn.disabled = true; let dir = stepLen; // policeDir = Math.floor(voiceSize * 100) / 10 * policeStepLen; //小偷、警察運動循環體 interval = setInterval(() => { policeDir = Math.floor(voiceSize * 100) / 10 * policeStepLen thiefXPos += dir; policeXPos += policeDir; //小偷的改變方向邏輯 if (thiefXPos >= (col + 1) * gridW) { thiefXPos = (col + 1) * gridW - stepLen dir = -stepLen thiefYPos += gridH } else if (thiefXPos < 0) { thiefXPos = -stepLen dir = stepLen thiefYPos += gridH } //警察的改變方向邏輯 if (policeXPos >= (col + 1) * gridW) { policeXPos = (col + 1) * gridW - stepLen policeStepLen = -policeStepLen policeYPos += gridH } else if (policeXPos < 0) { policeXPos = 1 policeStepLen = -policeStepLen policeYPos += gridH } //賊勝利邏輯 if (thiefYPos >= row * gridH) { winLogicFunc('thief') } //警察勝利邏輯 if (policeYPos === thiefYPos && (policeStepLen > 0 && policeXPos >= thiefXPos || policeStepLen < 0 && Math.abs(policeXPos) <= thiefXPos)) { winLogicFunc('police') } }, moveTime); }); //重新開始游戲 restartBtn.addEventListener('click', () => { thiefWinGifNode.style.visibility = 'hidden'; policeWinGifNode.style.visibility = 'hidden'; startBtn.disabled = false; // 重新開始游戲 clearInterval(interval); thiefXPos = 40; thiefYPos = 0; policeXPos = 0; policeYPos = 0; whoWin = ''; loop(); });
頁面的布局結構相對簡單,大家可以到github頁面上去閱讀源碼。頁面布局每個人的審美不同,實現的各有千秋,大家著重理js核心代碼,頁面實現那是三下五除二的。
關于“p5.js怎么實現聲音控制警察抓小偷游戲”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。