欢迎你们前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~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