之前在unity里尝试用过语音控制,当时的想法是实时控制游戏角色的移动与攻击,这在通过在线api解析语义的方式下体验一般,不过也想到在实时性要求不那么高的互动场景应该可以用起来。这里就在微信小游戏中尝试一下。
语音交互自然需要一个对象,像我这种手残人士最适合的设计当然就是卡通的小动物了。经过多次修改,在iPad上完成了形象设计(有点丑,有点歪,大家不要见怪):
设计好形象之后就可以设计动画了。一帧一帧的画出连贯的动作。因为偷懒,所以一个动画也就几帧,准备了下面几个动画:
接下来就可以参考飞机示例,完成游戏的代码部分。
首先新建一个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点和算术功能。为了适应牌子大小,结果做了截断处理。下面是几个测试例子:
笑一个/笑一笑:会做出笑脸
再见/拜拜:会返回标题界面
其他:会通过牌子上显示文字信息
另外,代码中还包含了一个跳跃的小游戏,通过在主界面点击开始可以进入。如果进一步开发,可以把小游戏的分数做为奖励,在互动界面购置一些装饰品之类的,应该会增加不少游戏的趣味性。
附:
此项目源码地址:https://gitee.com/stdioh_cn/jumpingdog