1. 程式人生 > >在微信小遊戲中實現語音互動

在微信小遊戲中實現語音互動

之前在unity裡嘗試用過語音控制,當時的想法是實時控制遊戲角色的移動與攻擊,這在通過線上api解析語義的方式下體驗一般,不過也想到在實時性要求不那麼高的互動場景應該可以用起來。這裡就在微信小遊戲中嘗試一下。

語音互動自然需要一個物件,像我這種手殘人士最適合的設計當然就是卡通的小動物了。經過多次修改,在iPad上完成了形象設計(有點醜,有點歪,大家不要見怪):

形象

設計好形象之後就可以設計動畫了。一幀一幀的畫出連貫的動作。因為偷懶,所以一個動畫也就幾幀,準備了下面幾個動畫:

  1. 眨眼。1幀,空閒狀態的時候播放,避免什麼都不做的時候畫面看起來太死板。
  2. 撓頭。2幀,錄音結束髮給伺服器等待伺服器結果的時候播放,使得能在話音剛落時就給出反應。
  3. 微笑。1幀,對它說“笑一個”的時候播放。
  4. 舉牌子。3幀,狗狗不會說話,要通過寫字的方式和玩家交流。

接下來就可以參考飛機示例,完成遊戲的程式碼部分。

首先新建一個class InterAction,在constructor中進行各種初始化,主要是載入一些圖片資源,註冊事件等:

constructor(c, returnTitle) {
    ctx = c
    this.returnTitle = returnTitle

    this.bg = wx.createImage()
    this.bg.src = 'images/bg2.png'

    this.recordBtn = wx.createImage()
    this
.recordBtn.src = 'images/record.png' this.recordBtnDown = wx.createImage() this.recordBtnDown.src = 'images/recorddown.png' this.btn = this.recordBtn this.dog = new Dog() this.idleFrameCount = 0 this.result = '' this.recorderManager = wx.getRecorderManager() this.recorderManager.onStop(this
.onRecordEnd.bind(this)) this.restart() } restart() { this._touchStartHandler = this.touchStartHandler.bind(this) wx.onTouchStart(this._touchStartHandler) this._touchEndHandler = this.touchEndHandler.bind(this) wx.onTouchEnd(this._touchEndHandler) requestAnimationFrame(this.loop.bind(this)) }

之後,可以看到主執行緒結構和飛機的基本一樣:

loop() {
    this.update()

    this.render()

    requestAnimationFrame(this.loop.bind(this))
  }

在update和按鈕、錄音的回撥函式中更新狀態,在render中顯示當前幀的內容。

錄音結束後,呼叫happycxz提供的介面進行語音解析:

onRecordEnd(res) {
   // 向伺服器傳送錄音檔案
   let url = 'https://api.happycxz.com/wxapp/mp32asr'
   this.dog.playAnimation('thinking', 0, true)
   this.idleFrameCount = 0
   this.processFileUploadForAsr(url, res.tempFilePath)
 }

 processFileUploadForAsr(url, filePath) {
    wx.uploadFile({
      url: url,
      filePath: filePath,
      name: 'file',
      formData: { 'appKey': appkey, 'appSecret': appsecret, 'userId': UTIL.getUserUnique()},
      header: {'content-type': 'multipart/form-data'},
      success: function(res) {
        let nliResult = this.getNliFromResult(res.data)
        let stt = this.getSttFromResult(res.data)

        let result = ''
        if (nliResult != undefined && nliResult.length != 0) {
          result = nliResult[0].desc_obj.result
        } else {
          result = '出了點問題'
        }
        this.handleResult(result)
      }.bind(this),
      fail: function (res) {
        wx.showModal({
          title: '提示',
          content: "網路請求失敗,請確保網路是否正常",
          showCancel: false,
          success: function (res) {
          }
        })
      }.bind(this)
    })
  }

處理返回結果:

handleResult(res) {
    if (res.length > 6) {
      res = res.substr(0, 6)
    }
    this.result = res
    if (this.result == '微笑') {
      this.dog.playAnimation('smile', 0, true, 60)
      this.idleFrameCount = 0
    } else if (this.result == 'exit') {
      wx.offTouchStart(this._touchStartHandler)
      wx.offTouchEnd(this._touchEndHandler)
      this.returnTitle()
    } else {
      this.dog.playAnimation('answer', 
                              0, 
                              false, 
                              240, 
                              function () {
                                ctx.fillStyle = "#ffffff"
                                ctx.font = "15px Arial"

                                ctx.fillText(
                                  this.result,
                                  constants.screenWidth * 17 / 32,
                                  constants.screenHeight * 15 / 32
                                )}.bind(this), 
                              2)
      this.idleFrameCount = 0
    }
  }

Dog是繼承自我擴充套件之後的Animation類,主要的改動有兩個,一個是支援多個動畫,另一個是在playAnimation時增加了多個引數,可以控制動畫迴圈的方式,並可以指定在特定的幀顯示時呼叫的回撥函式。具體的程式碼可以看文末的程式碼包,這裡就不多說了。

最後來看看測試效果。

待機狀態

按下錄音鍵,說話,鬆開後會做撓頭動作(有點難看),等待一段時間會舉起牌子顯示結果。目前只在olami平臺配置了閒聊、24點和算術功能。為了適應牌子大小,結果做了截斷處理。下面是幾個測試例子:

笑一個/笑一笑:會做出笑臉
再見/拜拜:會返回標題介面
其他:會通過牌子上顯示文字資訊

另外,程式碼中還包含了一個跳躍的小遊戲,通過在主介面點選開始可以進入。如果進一步開發,可以把小遊戲的分數做為獎勵,在互動介面購置一些裝飾品之類的,應該會增加不少遊戲的趣味性。

附: