webAudio 開發 H5 版《 八分音符醬 》

歡迎你們前往騰訊雲技術社區,獲取更多騰訊海量技術實踐乾貨哦~css

做者: QQ音樂技術團隊 

遊戲demo來源背景

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

  • 目標物體:遊戲中doge方塊
  • 碰撞物體:遊戲中的坑
  • 輸贏條件:目標物體與碰撞物體部分體積重合則判爲輸

根據以上的概念咱們能夠開始設計這款遊戲了。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)
}
複製代碼

利用webAudio控制遊戲的行走和跳躍

  • 獲取麥克風跟音量大小

在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的相關接口(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

相關文章
相關標籤/搜索