Tom's Halls - JavaScript 平台游戏引擎






4.91/5 (10投票s)
使用 JavaScript DOM 操作和 CSS 的 2D 平台游戏引擎。

版本 3.0,2010 年 4 月
Tom's Halls v3.0 版本基本上“完成了”游戏——字面意思,因为现在有了获胜的过场动画。游戏方面,游戏区域已扩展到 41 个屏幕,出现了各种新的复古精灵图,并且许多现有屏幕都经过了调整和修改。
从技术角度来看,我花了时间对“应用程序”进行了全面的优化改造
- 以前存在许多全局变量和函数。现在几乎所有内容都已合并到一个名为
game
的单一对象字面量中。Viewport
,以前是一个可实例化的“类”,现在是game
的一个属性,拥有自己的子属性。我决定,既然只有一个game
和一个固定尺寸的Viewport
,那就不如相应地进行优化和简化。我考虑过将可实例化的Player
也作为game
的一个属性,但选择不这样做,因为它体积庞大且复杂,会使game
对象难以管理。 - 各种以前可配置的属性,例如
Player
的宽度和高度以及“常量”,例如“forceLeft
”,现在都已硬编码以提高速度。这消除了大量的对象遍历,并显著加快了速度。这是以牺牲代码的可读性为代价的。例如,“+ this.player.width
”比“+ 32
”更能表明意图,但迫不得已。 - 通过本地缓存消除了许多不必要的连续对象遍历,例如
var v = this.game.viewport;
- 以前我的
setInterval
调用会进行隐式求值,例如:gLoopInterval = setInterval('loop()', gLoopIntervalMs);
现在已变为:
this.loopInterval = setInterval(function () { game.loop(); }, 12);
永远感谢 Douglas Crockford 的 http://jslint.com/ 工具,它这次极大地帮助我使代码更加健壮和高效。
浏览器行为
在我中等配置的笔记本电脑上,Tom's Halls 在 Firefox 3.6、Chrome 4.1 和 Opera 9.62 中运行流畅。在 IE8 中速度极慢,比 IE7 慢得多。我还没有在 Mac 上测试过 v3.0。

版本 2.0,2008 年 11 月
在朋友们反复催促我将 Tom's Halls 扩展成一个更完整的游戏后,我终于找到了时间来做这件事。结果就是 Tom's Halls v2.0,你可以在 www.tomshalls.net 上在线玩。你现在可以探索许多阴险的房间,里面充满了终极极客幽默和私人梗。请小心!
技术变更和改进
- 改进了玩家物理、碰撞检测和精灵交互算法
- 添加了可收集物品。每个屏幕脚本都通过为每个物品分配全局唯一 ID 来添加其可收集物品,如果这些 ID 尚不存在,则会添加到 Viewport 的 collectables 数组中,并且在每次进入屏幕时,如果尚未收集,则会添加到 Element 数组中,从而显示出来。
- 优化了所有循环、数组查找等。
- 调整了整体“对象模型”,并重新排列了方法。
- 改进了图形,同时保留了准 8 位复古风格。
- 预加载图像。
- 通过在可能的情况下使用 CSS 定义宽度和高度,最小化了应用于 Elements 的内联样式数量,例如在不涉及背景图像平铺或精灵尺寸更改的情况下。
- 将信息面板区域中所有 InnerHTML 用法替换为标准的 DOM 方法。
Chrome 观察
自最初版本发布以来,Chrome 浏览器已经出现。我发现 Chrome 的表现与开发最初版本时 Opera 的表现类似:Chrome 的优化,显然且不出所料,并不适用于此类应用程序。与 Firefox 和 IE7 相比,DOM 访问非常缓慢且不稳定。Chrome 还有一个烦人的习惯,就是在页面首次加载几秒钟后冻结,然后似乎重新加载页面或重建 DOM,从而有效地重置你的游戏。最后,即使在屏幕上使用单个小精灵的动态不透明度也会极大地减慢整个屏幕的速度。因此,我放弃了最初的闪烁可收集物品的方法,该方法使它们平滑地淡入淡出,转而采用 80 年代风格的每半秒闪烁一次。

引言
Tom's Halls 是/曾是一个学习项目,旨在通过 JavaScript DOM 操作和 CSS 开发一个简单的 2D 平台游戏引擎。我的主要目的是提高以下方面的技能:
- JavaScript,尤其是原型继承
- DOM 操作
- 基于 W3C 标准的标记
我选择从头开始开发代码,而不是基于 Prototype 等第三方库,部分原因是为了学习这些库是如何工作的,部分原因是为了避免它们的臃肿以及有时报告的它们可能与其他代码不兼容的风险。
使用 DOM,Luke
DOM div
元素用于表示静态房间元素(如墙壁和梯子)以及移动精灵(如玩家和“恶魔玩家”怪物)。动画通过修改精灵改变方向时的 div
style
属性的 left 和 top 属性以及 className
属性来实现。
我最初的碰撞检测方法是通过检查每个 DOM 元素的位置和尺寸来完成的,直接查询其 style
属性的 left
、top
、width
和 height
属性。然而,这被证明太慢了,尤其是在游戏区域变得非常复杂的情况下,部分原因是根据 XHTML Strict,非零属性值必须包含单位(例如 200px
),我必须使用笨拙的 string
操作来去掉单位,然后对剩余的值进行数学运算。
因此,我选择创建一个 Element
类,它“包装”一个 DOM 元素,并在实例化时存储其位置、尺寸和其他有用属性,并将其底层 DOM 元素属性添加到 viewport。每个 Element
还包含一个子 Element
的数组,最顶层的 Element
是 Viewport
,它本身是一个“继承”自 Element
的类。通过迭代 Viewport
的子 Element
s 并测试感兴趣的“固体类型”的每个 Element
,例如固体(墙壁、地板等)或可攀爬(梯子、绳索等),来实现碰撞检测。
游戏循环
使用 15ms 的间隔调用主游戏循环,它执行以下操作:
- 移动精灵
- 检查玩家是否被击中
- 将物理效果应用于玩家,例如重力、跳跃和移动力
屏幕定义和加载
“整个游戏”(此时所有两个屏幕)都发生在同一个 XHTML 页面 Default.htm 中。每个游戏屏幕都在自己的 JavaScript 文件中定义,文件名由其 X
和 Y
坐标决定,例如 screen_2_1.js。当玩家进入新屏幕时,会发生以下情况:
- 游戏循环被暂停
- 除了玩家之外,
Viewport
的所有子Element
s 都被移除 Viewport
的子Element
数组被重新初始化- 当前屏幕的 JavaScript 文件被动态从文档 head 中移除
- 新屏幕的 JavaScript 文件被动态添加到文档 head 中
- 新屏幕脚本通过
Viewport
类中的便捷方法向Viewport
添加子Element
s,例如addElevator
- 新屏幕脚本重新启动游戏循环
这种方法意味着技术上可以添加任意数量的新屏幕,而无需修改主引擎,或者(我希望)增加内存使用量。我承认我没有对浏览器内存进行深入分析,因为 JavaScript 文件会被链接和取消链接,DOM 元素会被添加和移除。我推测这取决于垃圾回收是如何实现的(或没有实现)。我很想听听这个领域专家的意见。
W3C 标准和浏览器行为
我很高兴能够让这款游戏通过 XHTML (1.0) Strict 验证,所有 JavaScript 都被非侵入性地链接。到目前为止,它已在 PC 上的 Firefox、Opera 和 Internet Explorer 7 中得到测试并已知可以正常工作。我曾尝试让它在 Internet Explorer 6 中基本运行(在进行了一些 CSS 技巧后,这让我非常痛苦),但后来放弃了,并且还没有能够在 Internet Explorer 6 中预览最新版本。它不是一个商业“网站”,目的是提高对基于标准的优化的掌握程度,而不是最大化兼容性。因此,我对不引入条件浏览器注释或任何进一步 CSS 技巧的决定感到满意。
关于浏览器性能的一个值得注意的方面是,虽然根据我的经验,Opera 通常是上述三种浏览器中最快的,但在处理 JavaScript 操作的 DOM 元素时,它似乎是速度最慢的,而且慢了很多。当移动精灵的数量增加时,它的速度似乎会急剧下降。我刚刚升级到 9.25 版本,它似乎比以前的版本更快,但与 Firefox 和 Internet Explorer 7 相比仍然很吃力。
结论
虽然我的工作领域不涉及任何类型的游戏开发,但我通过这个项目获得的额外理解已经在我们的商业项目中派上了用场,其中大部分直接或间接地利用了 JavaScript。希望其中一些方法可能对其他人有用。我不声称这些代码是完美的,因为其中很大一部分对我来说相当陌生!非常感谢评论和有益的批评。