歡迎你們前往騰訊雲技術社區,獲取更多騰訊海量技術實踐乾貨哦~css
做者: QQ音樂技術團隊
2017年春節期間,一款來自東洋霓虹的遊戲開始在微博、朋友圈火了起來,這款遊戲就是《不要停!八分音符醬》。八分音符醬之因此可以火起來,是由於它不經過手工操做,而是經過聲音來控制遊戲的行走和跳躍,這樣會讓用戶感受很新穎。其有趣的玩法也在網上產生了不少段子,如」要不是鄰居來敲門,我早就通關了「等等,如今網上都有人經過樂器來玩這個遊戲。html
一開始八分音符醬只有PC版本,目前又好像開始有了ios、android版,相關資源能夠自行搜索下載。本文則嘗試使用JS,結合web端音頻處理接口webAudio,實現一個H5版本的《不要停!八分音符醬》demo。本人也是第一次寫小遊戲,文章中出現的不足(好比遊戲建模、代碼實現)也麻煩讀者們批評指正,共同窗習。前端
先看下游戲的截圖吧,體驗地址(因爲系統兼容問題,建議複製地址在微信內webview打開) zhazhaxia.github.io/server/publ…android
玩法ios
鏈接耳機後,最好在微信或手Q打開這個頁面(系統需android5.0+),贊成獲取麥克風權限。而後對着麥克風大聲說幾句話,如「啊……」,而後遊戲裏面的doge就會開始走了,聲音大到必定程度,doge就會跳起來,掉坑則輸。git
本質上這應該是一個碰撞模型的遊戲,碰撞模型中幾個主要的概念是github
根據以上的概念咱們能夠開始設計這款遊戲了。web
先看一張初始設計圖吧算法
圖中棕色物體爲目標物體,是咱們視覺中的操做對象,能夠進行行走或者跳躍canvas
圖中藍色框則爲遊戲中的路,承載了物體的行走。遊戲中的路是一個總體,咱們實際在代碼操做的對象,能夠對下方的路總體移動,在視覺上感受是目標物體的移動。移動後以下圖
碰撞物體其實就是遊戲路中的坑。目標物體移動的時候,遊戲會給物體設置障礙,目標物體必須跳過這些坑,不然就遊戲就失敗重來了。
遊戲建模設計後就能夠開始實現了,因爲這個是單頁面且動做相對簡單,因此採用單體的設計模式實現。
實現部分分兩塊介紹,第一部分介紹遊戲的整體實現思路,這部分相對比較容易。第二部分主要介紹遊戲中的webAudio聲控部分,這部分是遊戲的核心。
遊戲中涉及到一些參數的配置用來控制遊戲的狀態,具體的配置能夠在編寫的時候生成,這裏有本文部分的配置信息。
config:{
barrierWidth:80,//障礙物寬
containerWidth:$('.container').width(),//大容器寬
numberOfBarrier:0,//容器障礙物數目
rank : 2,//難度1,2,3
timer:null,
lockMove:false,//鎖定移動
lockLost:false,//坑來了,開始判斷
dangerArea:[null,null],//危險區域,碰撞區域
$tmpBarrier:$(".barrier-low"),
lockConsole:true,
volSum:0,//音量大小
vol:0,
score:0,
gameEnd:false,
walkValue:1,//走的音量臨界值
jumpValue:~~$('.threshold').val()//起跳的音量臨界值
}
複製代碼
初始化主要是生成載體,填充到頁面中。本文主要根據遊戲容器的寬,生成初始載體的個數,填充到容器中。
initStat:function () {//初始化障礙物寬高,初始化載體
$('.barrier').width(exports.config.barrierWidth);
exports.config.numberOfBarrier = Math.ceil(exports.config.containerWidth / exports.config.barrierWidth) + 2;
$('.bottom-barrier').width(exports.config.numberOfBarrier * exports.config.barrierWidth);//障礙物容器寬
exports.createBarrier(exports.config.numberOfBarrier);//建立並填充
}
複製代碼
本文遊戲中的各類物體設計採用的是DOM來實現,固然也能夠採用canvas或其餘實現。載體移動到必定距離便在容器後面插入一個載體,插入的載體有多是路,也多是坑。插入後要把前面移動過的載體刪了,以避免DOM過多形成的能性能問題。
createBarrier:function (num) {//建立障礙物,num個數
...//其餘代碼
$bc.append(exports.getBarrier(num,type));
},
getBarrier:function (num,type) {//獲取障礙物
var html = "";
for(var i = 0; i < num; i++){
html += '<div class="barrier '+(type === 1 ? "barrier-high" : "barrier-low")+'" data-id="'+new Date().getTime()+'">》</div>'
}
return html;
}
複製代碼
當音量達到必定條件,目標物體在視覺中就開始移動,實際咱們移動的是目標物體下面的載體。
letsGo:function (vol) {//達到條件行走或者跳躍
if (vol > exports.config.walkValue ) {//走
exports.moveBarrier();
if (vol > exports.config.jumpValue) {//跳
exports.jumpNotes();
}
}else {//停
exports.stopBarrier();
}
}
複製代碼
碰撞檢測就是對目標物體和碰撞物體之間距離的檢測。在本文這個遊戲中,採用一個數組來更新碰撞物體,碰撞物體來的時候添加,離開的時候再更新一次。邊移動邊檢測。
judgeLost:function(){//是否失敗,碰撞檢測
....//其餘代碼
if(exports.config.$tmpBarrier.attr('data-id') !== $barrier.attr("data-id")){//更新碰撞物體
...//其餘代碼,更新,計分
}
...//
if (parseInt($notes.css("bottom")) <= 200) {//判斷是否在區間
var left = exports.config.dangerArea[0],
right = exports.config.dangerArea[1];
if(left <= 80 && right >= 160){//是否達到碰撞條件
exports.lost();
}
}
}
複製代碼
遊戲失敗後會從新初始化設置參數,重複以上步驟
lost:function () {//輸了掉坑了
$('.title').text('啊!掉坑了!從新來一遍吧!');//一下是重置部分
exports.stopBarrier();
exports.config.gameEnd = true;
$notes.stop(true).animate({bottom:0},500)
setTimeout(function () {
exports.config.gameEnd = false;
$('.bottom-barrier').html("");
exports.initStat();
$notes.css("bottom",200)
$('.title').text('大聲點!不要停!八分音符醬')
exports.config.score = 0;
$('.j_score').text(exports.config.score);
}, 3000)
}
複製代碼
在web中獲取麥克風能夠經過navigator.getUserMedia獲取,不過目前在移動端只有android5.0+纔有這個功能,iPhone目前尚未提供這方面的接口給JS調用。目前國內部分手機廠商的默認瀏覽器對這個權限也有限制,或者有兼容問題,建議用微信、手Q等webview採用QQ瀏覽器X5內核的app進行體驗(賣了個廣告)。
navigator.getUserMedia在pc的兼容通常是
navigator.getUserMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
複製代碼
獲取麥克風的大小須要用到webAudioApi的相關接口(webAudioApi的瞭解能夠參考筆者以前寫的介紹github.com/zhazhaxia/w… )
webAudioApi是W3C制定的用來處理web音頻的規範。核心是 AudioContext , AudioContext 是處理web音頻的核心對象,全部的處理接口以節點方式鏈接。以下圖所示,描述了一個源節點到目標節點的web音頻處理過程。
音頻返耳指的是在錄音的過程當中,麥接收的音頻在耳機的實時反饋。
利用webAudioApi的scriptProcessNode能夠獲取到麥克風的音頻數據,將音頻數據再輸出,就會有返耳效果。
實現過程:webAudio獲取到麥克風音頻源後,鏈接到ScriptProcess節點,ScriptProcess能夠獲取音頻輸入數據,並將音頻實時輸出,從而達到返耳效果。
var source=exports.audioContext.createMediaStreamSource(stream);
//用於錄音的processor節點
var recorder=exports.audioContext.createScriptProcessor(1024,1,1);
source.connect(recorder);//節點的鏈接
recorder.onaudioprocess=function(e){//正在錄音
var inputBuffer = e.inputBuffer;
var outputBuffer = e.outputBuffer;
for (var channel = 0; channel < outputBuffer.numberOfChannels; channel++) {
var inputData = inputBuffer.getChannelData(channel);//音頻輸入
var outputData = outputBuffer.getChannelData(channel);
for (var sample = 0; sample < inputBuffer.length; sample++) {
outputData[sample] = inputData[sample];//返耳
}
}
};
複製代碼
獲取音頻振幅能夠理解爲獲取音頻的音量大小。
利用webAudioApi的Analyser接口能夠獲取到音頻通過傅里葉變換後的數據,這些數據包含了音頻振幅等信息。若是要實時獲取音頻振幅大小,須要在 onaudioprocess 中獲取數據。因爲麥克風獲取到的音頻噪音成分有點大,此處做一個加權處理,平均後的值做爲目標振幅值。最後根據處理後的音頻振幅進行遊戲的行走和跳躍。
var analyser = exports.audioContext.createAnalyser();//音頻解析器
recorder.connect(analyser);
analyser.connect(exports.audioContext.destination);
// 設置數據
analyser.fftSize = 1024;//頻道數量
bufferLength = analyser.fftSize;
dataArray = new Float32Array(bufferLength);//每一個頻道的頻率
recorder.onaudioprocess=function(e){
analyser.getFloatTimeDomainData(dataArray);//獲取振幅信息
exports.getVolume(dataArray);//加權振幅
}
};
複製代碼
1.因爲不一樣硬件之間的差距,返耳效果的延遲有所區別
2.因爲PC跟手機硬件有所區別,實際的振幅值,PC會明顯高於手機
以上就是本文遊戲的主要設計的相關思路。
本文從PC遊戲《不要停?八分音符醬》的靈感出發,描述了其H5簡易版本的開發思路,遊戲的設計許多不足,請讀者們批評指正。
筆者開發H5版本《八分音符醬》的意圖不僅是爲了把pc的遊戲用H5來實現,並且想經過這麼一個在玩法上有些創新的遊戲,來完成一個webAduio的demo。目前web正在蓬勃發展,W3C也出了許多新的web標準,如webAudioApi,webAssembly,webAR,webGL等,這些都在發展階段,在實際的應用中尚未普遍應用。因此但願經過這麼一個demo,可以有更多想法,利用webAudio作出更多好玩有趣的應用。
W3C webAudioApi www.w3.org/TR/webaudio…
webAudioApi及應用案例分析 github.com/zhazhaxia/w…
王者榮耀高併發背後的故事
最快速的視野管理算法
web 前端入門神經網絡(一)
此文已由做者受權騰訊雲技術社區發佈,轉載請註明文章出處原文連接:https://cloud.tencent.com/community/article/429878