《哞哞小贝拉》网页小游戏开发记录
我向流星许愿:“希望拉姐的烦恼全消失!”然后包括我在内的牛批都死光了。(好似)
被封在家里四十多天,再纯良的贝极星也会变成牛牛民捏。
游戏介绍
灵感来源
某天向晚在直播里玩了这个游戏Find the Invisible Cow,并大加赞赏:“大家快做几个,我要玩!”
既然晚晚这么说了,那我高低得给她整一个。
游戏简介
拉姐隐藏在空白屏幕里。鼠标/触摸屏来控制浮标位置,根据浮标与拉姐的左右位置和相对距离,会发出不同声道和强度的声音。 在声音的帮助下,找到被牛牛民藏起来的拉姐吧!
随着距离变化,拉姐会发出三种不同的音频。至于是哪三种呢——
第一次,第二次,终极……
确立游戏框架
原游戏是网页游戏,用了Web Audio API。不知道正常的程序员到了这一步会如何继续实现,是去看人家实现、自己寻思所需模块还是直接打开VS开写,但我的第一步其实是——
上GitHub找类似项目并Fork一份。
朋友们,是这样的。我智商过低,码力不足,又贪得无厌,想在最短时间内马上建起一份可以跑的工程。虽然大学时学过一点网页开发,但已经好几辈子没写过完整项目,比起自己慢慢温习,直接去抄一份岂不更是美事?
而且,抄也是需要一点技术含量的。大概。搜了下关键字,最后找出一份最值得借鉴的工程,Invisible Cocogoat。它的几个亮点:
- 只用了一个HTML文件,三百行代码解决所有功能。Less is more
- 注释非常齐全,不仅会解释此功能是干什么的,还会解释下为什么这么写,读完就有受益。
- 和原游戏一样用了Web Audio API,可以学习下这个模块。
火速Fork下来,在本地debug跑测几次,大概熟悉了下函数和执行顺序,这下开始换皮啦……我是指,改造。
素材内容换皮
换皮虽然换的只是皮,但只有熟悉了工程的所有内容,才能准确地知道要换掉哪些皮。
对于这个简单的小游戏,只有一个HTML文件,所有图片和音频素材都在根目录下,换皮难度相当低。更重要的是,在换皮工程中,理解对方是如何写的。
个人发现的几个做法亮点:
- Web Audio API的清晰运用。照着人家的写法,可以挨个搜用途。
- 用一个空白图遮住需要找的图片,在图片被点击时去掉空白图。
- 计算鼠标位置的数学方法写的非常严谨,且考虑到了除以0的error情况。
总而言之,经过一系列换皮,这个工程已经初步变成了《哞哞小贝拉》的形状。
加个新功能
原版工程只能播放一种音频,然而拉姐的《勇敢牛牛不怕困难》一共喊了三次。打算给《哞哞小贝拉》加个新功能,根据距离来分别播放三段音频。
创建音频上下文
这个项目是用Web Audio API播放的音频。首先,需要创建必须的节点。
// 创建Audio Context
AudioContext = window.AudioContext || window.webkitAudioContext;
audioCtx = new AudioContext();
// 索引音频
audioElement_1 = document.getElementById('Mow_First');
audioElement_2 = document.getElementById('Mow_Second');
audioElement_3 = document.getElementById('Mow_Final');
// 创建Track
track_1 = audioCtx.createMediaElementSource(audioElement_1);
track_2 = audioCtx.createMediaElementSource(audioElement_2);
track_3 = audioCtx.createMediaElementSource(audioElement_3);
// 创建效果节点
gainNode = audioCtx.createGain();
pannerOptions = {pan: 0};
panner = new StereoPannerNode(audioCtx, pannerOptions);
为了播放三段不同的音频,取了三个不同的音源。
根据距离连接不同音源
原项目是这么计算距离的:
//set volume to a value roughly inversely proportional to the distance between the cursor and cocogoat, but never below -20dB.
if (!audioMuted) {
gainNode.gain.value = Math.min(Math.max((1/(1+0.01*calculateDistance(document.getElementById("cocogoat"), mouseX, mouseY))), 0.1), 1);
}
//set stereo pan based on whether cocogoat is left or right of the cursor position, scaled by width of window
//use square root to make pan effect more significant near the target. Requires workaround to return proper negative values.
let xDistance = calculateXDistance(document.getElementById("cocogoat"), mouseX, mouseY);
panner.pan.value = -1 * Math.sign(xDistance) * Math.sqrt(Math.abs(xDistance)/(document.getElementById('space').clientWidth));
简单来说,随着距离远近,panner.pan.value的值会在0~1之间。
一开始打算把更换音频的逻辑整合到算距离的代码里,后来觉得没必要,用panner.pan.value的最终值来算播哪段音频就好了。距离远近=音量大小=不同音频,逻辑照样成立。
于是,在下面塞了这样一段代码。根据音量大小,连接不同的音源到效果器:
if (!cocogoatFound) {
if (gainNode.gain.value < firstAudioDistance) {
//set up audio path
track_1.connect(panner).connect(gainNode).connect(audioCtx.destination);
audioElement_1.play();
}
else if (gainNode.gain.value >= firstAudioDistance && gainNode.gain.value < secondAudioDistance) {
//set up audio path
track_2.connect(panner).connect(gainNode).connect(audioCtx.destination);
audioElement_2.play();
}
else {
//set up audio path
track_3.connect(panner).connect(gainNode).connect(audioCtx.destination);
audioElement_3.play();
}
}
firstAudioDistance和secondAudioDistance在最前面进行了赋值,分别是0.15和0.4。
当然,数值是凭感觉定的,所谓调参数。
改完了这两个地方,游戏本身已经是完全体了。
套个域名,上线!
用Github Page推送网页有个问题是,域名会暴露自己的GitHub账号,可以的话并不想自爆开盒。盘算了下,打算买个域名做个二次指向。反正域名也不贵,熟悉下套域名的流程也挺好的。
请选择你的域名
第一步当然是买个域名。为了图个方便和便宜,最终选择阿里云。输入
将域名指向Blog
为了让域名能指向blog服务器地址,可以在Source下面新建个CNAME,也可以手动解析。我用的方法是后者。
获取blog的IP地址
在控制台里输入 ping+<blog网址>,获取blog的IP地址。
添加阿里云域名解析
找到对应域名,点击解析,添加两条解析记录,记录类型A/A,主机记录@/www,记录值都是IP地址。
Github Page添加对应域名
打开Github Page配置,在Custom Domain离输入刚刚购买的域名,等待解析成功,保存。
至此,《哞哞小贝拉》成功套皮成功哞,请游玩《哞哞小贝拉》谢谢哞。
参考阅读资料
https://segmentfault.com/a/1190000022521469
《哞哞小贝拉》网页小游戏开发记录