使用Web Audio和SVG创建可访问的Breakout游戏





5.00/5 (1投票)
我挑战自己创建了一个完全可供视障人士使用的 WebGL 游戏。我将与您分享这款游戏的背景故事以及实现它所涉及的所有实验。
作为 WebGL 游戏引擎 Babylon.js 的联合作者,我在网络会议上听到人们讨论可访问性最佳实践时,总感到有些不安。使用 Babylon.js 创建的内容对盲人来说确实完全不可访问。让网络对每个人都可访问非常重要。我对此深信不疑,因为我自己的儿子也深受其影响。因此,我想为网络的可访问性做出一些贡献。
这就是为什么我决定着手创建一个使用 WebGL 且完全可访问的游戏,以证明视觉游戏并非天生就不可访问。我选择保持简单,因此我创建了一个 Breakout 克隆游戏,您可以在下面的 YouTube 视频中看到它的运行效果
您可以在支持 Web Audio 的浏览器中进行测试(请参阅 caniuse.com 获取列表)或在 Github 上下载或查看源代码。
现在,我将与您分享这款游戏的背景故事以及所有相关的实验……
很久很久以前
一切都始于 2014 年的 Kiwi Party 2014 会议,当时我正在听 Laura Kalbag 关于顶级可访问设计注意事项的指南的演讲。我与 Stéphane Deschamps(一个可爱、有趣且有才华的人)讨论了我对如何使 WebGL 可访问的知识不足,以及我如何避免人们创建大量不可访问的内容。为了激励我,他向我发起了挑战。可能他低估了后果:“如果你能做一个可访问的 Breakout 游戏,那将非常酷!” 砰。就在那时,你现在看到的一切的种子就种在了我的脑子里。我开始认真思考这个问题,并研究如何创造这样的体验。
首先,我发现 audiogames.net 和 game-accessibility.com 上已经有可访问的音频游戏。我还研究了为盲人创建游戏的最佳实践。虽然读起来很有趣,但我并没有找到我想要的东西。我不想为盲人创建专门的体验,我想创建一个通用的游戏,任何人都可以玩,无论能力如何。我坚信网络正是为此而创建的,我的梦想是让我的游戏拥抱这一理念。我想创造一种独特的体验,让所有类型的用户都能玩,让他们能够共同分享快乐。我想要出色的视觉和声音效果,而不是“看,这是可访问的,所以它不能像那样好”的解决方案。
为此,我开始做一些实验。我使用了我朋友 David Catuhe 编写的、使用 SVG 的小型 Breakout 游戏。我对其进行了一些重构,使用了 SVG viewbox 以实现更好的屏幕缩放,并将其重写为 TypeScript。我还用 Babylon.js 的 WebGL 画布替换了背景中动画的 2D 画布,以增加星空体验。
这成为了我实验的基础。
对于音频,我有几个想法。我想使用的主要技巧是空间音频,以便人们无需看到屏幕就能知道他们在棋盘上的位置。这可以使用 Web Audio 实现。由于我没有视障测试者的机会,我通过戴一副好耳机并闭上眼睛来“作弊”。稍后您会看到,与真正的盲人用户测试游戏帮助我修复了更多问题,但作为开始,这是一种测试游戏的不错方式。
我开始使用 HTML5Rocks 上一篇出色的教程中的示例来 tinkering Web Audio API。主要演示在“3D positional sound”部分
然后,我将鼠标光标发出的声音替换为游戏中球的位置。测试结果并不如我所愿。仅凭声音很难准确判断球在屏幕上的位置,而且您无法像看到屏幕那样预测球的轨迹。不过,我认为在球碰到物体(砖块或墙壁)时发出一些 3D 音效很有趣。这些信息对任何人都有用,所以我保留了这部分。
由于我业余时间也是一名作曲家,我的下一个想法是为每个砖块列使用一个特定的钢琴音符,从而增加左右感。默认情况下,我选择使用 8 列来覆盖一个八度。我编写了代码,结果……很有趣,但并没有帮助游戏玩法。
我知道我需要帮助,所以我给我的大儿子看了我所做的,他提出了最好的解决方案。他告诉我,使用声音的播放速率和效果来提供球的位置信息是有意义的。经过几次测试,我得出了以下算法
- 如果球垂直对齐挡板,则以“正常”速率播放声音
- 如果球未对齐挡板,则减慢播放速率。球离挡板越远,声音播放得越慢。这将为盲人用户提供即时反馈,让他们知道球不再对齐,并且需要移动挡板以避免漏接球
- 以空间化的方式播放音乐声音:如果球位于挡板中心,则 X 轴为 0;根据球与挡板的距离,X 轴为 –值和 +值。
该算法的初步测试非常令人鼓舞——我几乎可以在闭着眼睛的情况下玩游戏。过了一段时间,我调整了游戏玩法和算法来解决我看到的一些问题。当您看不到球时,您无法预测球的方向,因此当音乐突然减慢时,移动挡板过于困难。您根本无法及时调整挡板位置。
为了解决这个问题,我增加了一些容错空间。首先,在“可访问模式”下,挡板宽度是原来的两倍,以弥补无法看到挡板的不足。其次,当球到达屏幕垂直高度的 80% 时,我会减慢球速,以便用户在球击中地面之前有更多时间反弹它。最后,我在球与挡板宽度 66% 对齐时更改了播放速率。挡板的其余部分仍然可以用于球的碰撞,但使用这种方法可以让盲人用户预测何时球即将错过挡板。
我对使用这些游戏参数的游戏非常满意。我一直在与我的几位同事测试这款游戏,他们可以在闭着眼睛的情况下玩游戏。但他们都知道 Breakout 游戏应该是什么样子,因此,他们的大脑或多或少能够预测游戏机制。他们已经被“条件反射”了。
我的最终测试是在 2014 年的 Paris Web 2014 上进行的,这是法国一个很棒且知名的会议。我的目标是完成游戏的第一稿,用于著名的闪电演讲。我对自己所做的有点紧张,又遇到了 Stéphane,分享了我的担忧。他建议我和Sylvie Duchateau谈谈,她是一位参与网络可访问性的盲人,向她描述我所做的事情并与她进行一次快速测试。
在休息时间,我与她分享了我的项目以及其背后的音频游戏想法。令我惊讶的是,她告诉我她不知道 Breakout 游戏是什么!这很明显。如果您看不见,纯粹的视觉游戏对您没有多大吸引力。然而,她发现一个带有空间音频的游戏的想法很有趣,所以我们尝试了一下。
她戴上我的耳机,我开始了游戏……令我沮丧的是,她根本无法玩游戏。音频信息太多,无法精确判断该做什么。我现在应该向左还是向右移动?与她进行了简短的讨论后,她告诉我应该删除一些音频细节。她还建议我避免对音乐使用 Web Audio 空间化(它会根据与挡板的距离从中心向左或向右移动),而只启用右侧或左侧扬声器,以提供非常清晰的操作指令。在她那里的时候,我很快修复了代码,然后她立即能够打破她的前 2 块砖。我太高兴了,您无法想象。她甚至问我应该打破的最高分数是多少,这意味着我实现了提供可访问游戏的目标——至少对视障人士而言。
处理这一切的主要代码在这里
// To help visually impaired users, the sound is being played at normal rate
// on 66% of the global width. It's to help them anticipating the ball
export const ACCESSIBLE_PAD_TOLERANCE = 0.66;
export const DEFAULT_MUSIC_PLAYRATE = 1.3;
private _updateAccessibilityMusic() {
var paddleX = this._padX;
var paddleW = this._padWidth;
var ballPosition = { x: this._ballX, y: this._ballY };
var deltaX = paddleW * ((1 - ACCESSIBLE_PAD_TOLERANCE) / 2);
if (ballPosition.x > deltaX + 10 && ballPosition.x < this._viewPortWidth - (deltaX + 10)) {
paddleX += paddleW * ((1 - ACCESSIBLE_PAD_TOLERANCE) / 2);
paddleW = paddleW * ACCESSIBLE_PAD_TOLERANCE;
}
// If paddle & ball aligned, sound is played on both ears (X = 0, for center)
// If the ball is on the left, musicIndicatorX should be negative otherwise positive
var musicIndicatorX;
// Position coordinates are in normalized canvas coordinates
// with -0.5 < x, y < 0.5
if (ballPosition) {
var x = (ballPosition.x - this._viewPortCenter.x) / this._viewPortWidth;
// Ball and paddle are vertically aligned
if (ballPosition.x >= paddleX && ballPosition.x <= paddleX + paddleW) {
this._music.setPlaybackRate(DEFAULT_MUSIC_PLAYRATE)
musicIndicatorX = 0;
}
else {
var distanceFromPaddle;
// Ball is on the left of the paddle
if (ballPosition.x < paddleX) {
distanceFromPaddle = paddleX - ballPosition.x;
musicIndicatorX = -30;
}
else {
distanceFromPaddle = ballPosition.x - paddleX - paddleW;
musicIndicatorX = 30;
}
var distanceFromPaddleNormalized = distanceFromPaddle / this._viewPortWidth;
// Slowing down the play rate based on the distance from the paddle
this._music.setPlaybackRate(0.9 * (1 - distanceFromPaddleNormalized));
}
// Playing music on left or right speaker based on the ball position from the paddle
this._music.setPosition(new BABYLON.Vector3(musicIndicatorX, 0.5, 0));
}
}
注意:我正在使用构建在 Web Audio 之上的 BABYLON.JS 音频堆栈。您可以阅读我们的文档或在 Github 上获取代码。
我在游戏中添加的其他想法
我不记得我尝试过的所有技巧,以便优化游戏玩法使其“通用”,因此我将总结我已实现的内容。
语音合成
有些用户可能无法看到剩余砖块的数量。同样,他们无法通过视觉了解自己是否获胜或失败。这就是为什么我认为使用 Web Audio 语音库 meSpeak.js 来添加音频线索是一个好主意。然而,在与Anthony Ricaud和活动中的其他几个人讨论后,事实证明这并不是最佳解决方案。问题在于我会在代码中强制使用特定的语音和语速。然而,辅助技术用户已经拥有首选设置——特定的语音和定义的语速。因此,最好使用 ARIA Live Region 来在游戏过程中更新用户。我确信我还可以做更多;如果您愿意,请随时改进我的代码,我将不胜感激。
语音合成目前会告知您剩余砖块的数量、游戏已开始或已结束(通过失败或获胜),以及您的最终得分。作为 ARIA live region 中的值,屏幕阅读器会自动将此信息读给用户。视觉用户不需要机器人语音来告诉他们发生了什么。
SVG 样式
我决定在此游戏中将 SVG 用于几个原因:它在所有屏幕上完美缩放,因为它是矢量基础,它可以与 CSS 结合用于设计,最重要的是,它与 ARIA 配合得非常好。我在本文前面已经提到了缩放部分,并且我还没有对 ARIA(除了 Live Regions 之外)在此情况下与 SVG 有用的地方进行足够的研究。
另一方面,CSS 对我很有帮助。提醒一下,我的目标是拥有同一个游戏,使用相同的代码库,供任何人使用。当您加载游戏时,我会加载默认样式表,并针对视障人士进行优化。原因如下:
- 如果您看不见或只能部分看见,最好从高对比度的视觉效果开始。我默认加载“indexvi.css”以获得高对比度的黄黑配色。我还禁用了背景 WebGL 画布以减少视觉混乱。如果您能看到并且不喜欢,您可以取消选中相应的选项,并获得星空和不太鲜艳的视觉效果。
- 如果您完全看不见,您可以禁用“视觉障碍”选项以启用高质量图形。这将加载“index.css”样式表并启用 WebGL 背景。得益于 SVG 与 CSS 的完美结合,我们只需要加载这个新的样式表,其余的就会自动完成。当然,看不见的人不在乎图形的好坏。但对于观看您玩游戏的人来说,这更好,因为它表明可访问的游戏不一定看起来很基础。
- 如果您能清楚地看到,取消选中所有选项。您将获得出色的视觉效果,并且挡板的速度和宽度将调整为更具挑战性。您也不会收到关于剩余砖块数量以及您是否获胜或失败的音频提示。这本是不必要的——应该很明显。
总之,工作流程如下
- 首次启动游戏时,我们预判存在视觉障碍,并为您提供游戏的高对比度版本
- 如果您完全看不见,您可以取消选中“视觉障碍”选项以启用为周围观众准备的精美图形。挡板宽度保持不变,您仍然有音频辅助
- 如果您没有视觉障碍,您可以取消选中所有选项,以使挡板更窄,球速更快
未实现的想法和结论
我给自己的挑战是提供出色的游戏体验,无论一个人是否能看到。我知道我还没有完全履行这个承诺——例如,如果您完全看不见,您不知道屏幕上还剩下多少砖块需要打破,而如果您能看到或有轻微的视力障碍,您很可能可以找到剩余的砖块并调整球的方向来打破它们。
我最初的想法是,当只剩下 10 块砖时使用语音合成。它可以说:“左边 4 块,中间 4 块,右边 2 块”。但这并不精确,而且在没有视觉信息的情况下,仍然很难改变球的方向。但也许你们中的某个人会找到一个很棒且优雅的解决方案来解决这个问题(提示,提示)。
尽管如此,我对这个挑战感到非常满意,并且在尝试解决它时获得了很大的乐趣。我通过阅读有关可访问性的文章学到了很多东西。我还希望我已经证明了可访问性可以提供给人们,即使是在意想不到的领域,只需思考一下可能。最后但并非最不重要的一点是,我了解到通过在游戏中启用可访问性,您可以为每个人改善体验。
更多关于 JavaScript 的实践
本文是微软技术传道士关于实用 JavaScript 学习、开源项目和互操作性最佳实践的 Web 开发系列的一部分,包括 Microsoft Edge 浏览器和新的 EdgeHTML 渲染引擎。
我们鼓励您跨浏览器和设备进行测试,包括 Microsoft Edge——Windows 10 的默认浏览器——使用 dev.modern.IE 上的免费工具 dev.modern.IE
- 扫描您的网站是否存在过时库、布局问题和可访问性问题
- 将虚拟机用于 Mac、Linux 和 Windows
- 在您自己的设备上远程测试 Microsoft Edge
- GitHub 上的编程实验室:跨浏览器测试和最佳实践
来自我们工程师和布道者的 Microsoft Edge 和 Web 平台深度技术学习
- Microsoft Edge Web Summit 2015(对新浏览器、新支持的 Web 平台标准以及来自 JavaScript 社区的特邀演讲者的期望)
- 哇,我可以在 Mac 和 Linux 上测试 Edge 和 IE!(来自 Rey Bango)
- 在不破坏 Web 的情况下推进 JavaScript(来自 Christian Heilmann)
- 让 Web 正常工作的 Microsoft Edge 渲染引擎(来自 Jacob Rossi)
- 使用 WebGL 和 Microsoft Edge 释放 3D 渲染(来自 David Catuhe,包括 vorlon.JS 和 babylonJS 项目)
- 托管 Web 应用和 Web 平台创新(来自 Kevin Hill 和 Kiril Seksenov,包括 manifold.JS 项目)
更多免费的跨平台工具和网络平台资源