溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

p5.js怎么實(shí)現(xiàn)聲音控制警察抓小偷游戲

發(fā)布時(shí)間:2023-05-08 15:59:33 來(lái)源:億速云 閱讀:190 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“p5.js怎么實(shí)現(xiàn)聲音控制警察抓小偷游戲”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“p5.js怎么實(shí)現(xiàn)聲音控制警察抓小偷游戲”文章能幫助大家解決問(wèn)題。

一、游戲介紹

之前一直用原生canvas寫小游戲,很多邏輯都需要自己一點(diǎn)點(diǎn)封裝,最近看了下p5.js,哇哦,好的很嘞!就用它開發(fā)了一款名為“警察抓小偷”的游戲。這是一款非常有趣的游戲,玩家扮演警察追捕小偷,通過(guò)大聲喊話來(lái)控制警察的速度,抓住小偷。游戲中,小偷會(huì)在棋盤上移動(dòng),而警察則會(huì)向小偷靠近。如果警察追上小偷,就算警察勝利;如果小偷逃脫到棋盤的最下方,那么小偷勝利。

玩法指南

點(diǎn)擊開始游戲按鈕小偷會(huì)在棋盤上移動(dòng),游戲會(huì)調(diào)用麥克風(fēng)玩家通過(guò)大聲狂呼亂叫,此時(shí)警察則會(huì)向小偷靠近,你喊得越大聲,警察移動(dòng)速度越快,當(dāng)警察追上小偷,就算警察勝利;如果小偷跑到的右下方出口處逃脫,那么小偷勝利。

二、用到的JS庫(kù)

  • p5.js 庫(kù)基于 Processing,是一個(gè) JavaScript 庫(kù),可以輕松地創(chuàng)建交互式圖形和動(dòng)態(tài)圖像。它的核心是使用 HTML5 Canvas 元素來(lái)創(chuàng)建圖形。p5.js 提供了簡(jiǎn)單易用的 API,讓編寫和理解代碼變得更加容易。

  • p5.sound.min.js 是 p5.js 庫(kù)的音頻處理插件,它基于 Web Audio API,可以加載、播放和操縱音頻文件。Web Audio API 是 HTML5 中的一項(xiàng)標(biāo)準(zhǔn),提供了低延遲、高品質(zhì)的音頻處理能力,可以通過(guò) JavaScript 對(duì)音頻進(jìn)行混合、變形、過(guò)濾等操作。

三、游戲開發(fā)思路

  • 設(shè)計(jì)游戲規(guī)則和界面

  • 使用p5.js繪制游戲界面和元素,這比自己用原生canvas寫迅速多了。

  • 實(shí)現(xiàn)小偷和警察的移動(dòng)邏輯

  • 實(shí)現(xiàn)通過(guò)音量控制警察的速度

  • 實(shí)現(xiàn)勝利邏輯和動(dòng)畫效果

  • 添加開始游戲和重新開始的按鈕

  • 預(yù)加載音樂(lè)和圖片資源

四、核心功能點(diǎn)

核心功能點(diǎn)主要包括繪制棋盤、小偷的移動(dòng)邏輯、警察的移動(dòng)邏輯以及勝利邏輯函數(shù)。大家著重理解這些開發(fā)思路。

1. 繪制棋盤

在 p5.js 的 draw() 函數(shù)中,調(diào)用rect()方法來(lái)繪制20X15的游戲棋盤,image()方法來(lái)繪制小偷、警察元素,并實(shí)時(shí)繪制小偷和警察的新位置和動(dòng)態(tài)更新頁(yè)面上玩家的麥克風(fēng)音量刻度條,通過(guò)whoWin變量檢查是否勝利,以及執(zhí)行相應(yīng)的文案繪制效果。

//循環(huán)體
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);
  }
  //.....
  // 顯示當(dāng)前玩家數(shù)據(jù)
  thiefVoiceValNode.innerText = thiefProgressNode.value = policeDir / policeStepLen;
  policeValNode.innerText = policeProgressNode.value = Math.abs(policeDir);
}

2. 小偷的移動(dòng)邏輯

小偷的移動(dòng)邏輯是每隔一定時(shí)間在 X 軸方向上以固定的步長(zhǎng)移動(dòng),步長(zhǎng)由stepLen常量控制,在到達(dá)棋盤邊界if (thiefXPos >= (col + 1) * gridW)改變運(yùn)動(dòng)方向dir = -stepLen,并在 Y 軸方向上增加一個(gè)棋盤格子高thiefYPos += gridH。小偷的移動(dòng)步長(zhǎng)可以通過(guò)調(diào)整 stepLen 的值來(lái)改變,值越大,小偷移動(dòng)的越快。

3. 警察的移動(dòng)邏輯

警察的移動(dòng)邏輯比較復(fù)雜,需要根據(jù)下面代碼中getVoiceSize()獲取的音量值來(lái)動(dòng)態(tài)調(diào)整警察的速度policeDir = Math.floor(voiceSize * 100) / 10 * policeStepLen。通過(guò) setInterval() 函數(shù)來(lái)定時(shí)改變警察的橫坐標(biāo)的位置policeXPos += policeDir;,并根據(jù)警察的位置和小偷的位置來(lái)判斷是否勝利。

//獲取音量大小
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);//計(jì)算音量大小
      voiceSize = Math.sqrt(sum / buffer.length);
    };
  } catch (err) {
    console.log('getUserMedia error: ', err);
  }
}

4. 勝利邏輯

在開始游戲點(diǎn)擊事件的回調(diào)函數(shù)中,判斷游戲勝利的條件并播放勝利音樂(lè)。在 draw()函數(shù)中通過(guò)whoWin來(lái)判斷游戲勝利的條件繪制頁(yè)面上的文案。當(dāng)游戲勝利時(shí),頁(yè)面上繪制對(duì)應(yīng)文案并播放勝利音樂(lè)。

//開始游戲事件
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);
});

五、待實(shí)現(xiàn)的創(chuàng)意玩法

為了增加游戲的趣味性,可以考慮增加更多的道具和障礙物。

  • 道具:可以設(shè)置一些可以幫助小偷逃脫警察追捕的道具,例如加速藥水、隱身衣、煙霧彈等。這些道具可以增加小偷的移動(dòng)速度、暫時(shí)讓小偷隱身躲避警察的視線、或者釋放煙霧干擾警察的追蹤。

  • 障礙物,可以設(shè)置一些可以對(duì)小偷和警察造成影響的障礙物,例如路障、警車、人群等。這些障礙物可以阻礙小偷的逃脫,或者讓警察無(wú)法前進(jìn),從而給小偷爭(zhēng)取時(shí)間。

除了增加道具和障礙物,我們還可以增加多種不同的游戲模式,例如多人模式、計(jì)時(shí)模式等等。

  • 多人模式:可以讓兩個(gè)玩家分別扮演小偷和警察,進(jìn)行對(duì)抗。

  • 計(jì)時(shí)模式中,可以設(shè)置一個(gè)時(shí)間限制,讓警察在規(guī)定時(shí)間內(nèi)抓住小偷。

還有可以美化頁(yè)面還有音效、動(dòng)效等,讓游戲更加豐富多彩,讓玩家有更多的選擇和挑戰(zhàn)。

六、代碼實(shí)現(xiàn)

為了避免文章過(guò)于臃腫,這里只放js的實(shí)現(xiàn)代碼,對(duì)于頁(yè)面布局代碼可以到我的github上去閱讀代碼,代碼里都有豐富的注釋,相信大家可以快速讀懂代碼,如果我用原生canvas開發(fā),估計(jì)工作量會(huì)大很多。不過(guò),代碼在封裝方面還有待提高,除此之外,玩法也還有更多創(chuàng)意有待實(shí)現(xiàn)。

各位如果更好玩的實(shí)現(xiàn)玩法,不妨把代碼fork過(guò)去實(shí)現(xiàn),記得評(píng)論區(qū)告訴我,我期待你的實(shí)現(xiàn)。

1. js代碼

const gridW = 30; //棋盤格子寬
const gridH = 30;  //棋盤格子高
const row = 15; //棋盤行數(shù)
const col = 20; //棋盤列數(shù)
let sone = null;//勝利音樂(lè)
let whoWin = ''; //誰(shuí)勝利可選值'police|thief'
let interval = null;//循環(huán)句柄
const moveTime = 16;//循環(huán)時(shí)間
let thiefImg = null;//小偷圖片
let thiefXPos = 40;//小偷橫坐標(biāo)
let thiefYPos = 0;//小偷縱坐標(biāo)
const stepLen = 2; //小偷移動(dòng)步長(zhǎng)(速度)
let policeImg = null;//警察圖片 
let policeXPos = 0;//警察橫坐標(biāo)
let policeYPos = 0;//警察縱坐標(biāo)
let policeStepLen = 2;//警察移動(dòng)步長(zhǎng)基數(shù)
let policeDir = 0; //警察動(dòng)態(tài)移動(dòng)步長(zhǎng)(速度)
let voiceSize = 0;
//獲取DOM節(jié)點(diǎn)
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'); //小偷勝利動(dòng)圖
const policeWinGifNode = document.getElementById('policeGif'); //警察勝利動(dòng)圖
//預(yù)加載圖片
function preload() {
  song = loadSound('./resources/勝利配樂(lè).mp3');
  thiefImg = loadImage('./resources/thief.png');
  policeImg = loadImage('./resources/police.png');
}
//初次調(diào)用
function setup() {
  createCanvas(col * gridW + 60, row * gridH).parent('gameCanvas')
  document.querySelector('.page').style.cssText = `width:${(col + 2) * gridW}px`
}
//循環(huán)體
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);
  // 顯示當(dāng)前玩家數(shù)據(jù)
  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);//計(jì)算音量大小
      voiceSize = Math.sqrt(sum / buffer.length);
    };
  } catch (err) {
    console.log('getUserMedia error: ', err);
  }
}
//勝利邏輯函數(shù)
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;
  //小偷、警察運(yùn)動(dòng)循環(huán)體
  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();
});

2.頁(yè)面布局和樣式

頁(yè)面的布局結(jié)構(gòu)相對(duì)簡(jiǎn)單,大家可以到github頁(yè)面上去閱讀源碼。頁(yè)面布局每個(gè)人的審美不同,實(shí)現(xiàn)的各有千秋,大家著重理js核心代碼,頁(yè)面實(shí)現(xiàn)那是三下五除二的。 

關(guān)于“p5.js怎么實(shí)現(xiàn)聲音控制警察抓小偷游戲”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI