简介
2010 年夏天,我们开发了《Sand Trap》,这款游戏参与了针对手机推出的 HTML5 游戏竞赛。但大多数手机要么仅显示游戏的一部分,要么游戏画面太小,导致游戏根本无法玩。因此,我们决定让游戏能够流畅地调整以匹配任何分辨率。经过一番重新编程和运用本文中概述的理念后,我们开发了一款可以扩展到任何现代浏览器的游戏,无论是在桌面设备还是移动设备上运行。
这种方法在《Sand Trap》中效果良好,因此我们在最新的游戏《Thwack!》中使用了相同的方法。游戏会自动调整屏幕分辨率,以适应全屏窗口和自定义大小的窗口,如以下屏幕截图所示。
实现这一点需要充分利用 CSS 和 JavaScript。使用 CSS 填满整个屏幕非常简单,但 CSS 不允许您保持相同的宽高比来防止画布和游戏区域发生拉伸。这正是 JavaScript 的用武之地。您可以使用 JavaScript 重新缩放文档元素,并在窗口事件时触发调整大小。
准备页面
第一步是指定页面上的游戏区域。如果您将其添加为 div 块,则可在其中放置其他标记或画布元素。正确设置后,这些子元素就会继承父 div 块的缩放设置。
如果您的游戏区域分为两个部分,即游戏区和记分区,则代码可能如下所示:
<div id="gameArea">
<canvas id="gameCanvas"></canvas>
<div id="statsPanel"></div>
</div>
有了基本的文档结构后,您可以为这些元素提供一些 CSS 属性,以便其做好准备,以便调整大小。“gameArea”的许多 CSS 属性都是直接通过 JavaScript 操控的,但为了让它们正常工作,请从父级 gameArea div 代码块开始设置一些其他 CSS 属性:
#gameArea {
position: absolute;
left: 50%;
top: 50%;
}
这会将画布的左上角置于屏幕的中心。下一部分中介绍的 JavaScript 自动调整大小函数会操纵其他 CSS 属性,以调整游戏区域的大小,并将其居中显示在窗口中。
由于游戏区域会根据窗口的尺寸自动调整尺寸,因此,您不需要 gameArea div 块的子元素的尺寸(以像素为单位),而应采用百分比尺寸。像素值不允许内部元素在父 div 发生变化时随其变化而缩放。不过,最好从像素着手,然后在您获得自己喜欢的布局后将它们转换为百分比。
在本例中,先将游戏区域高 300 像素,宽 400 像素。画布覆盖整个游戏区域,一个半透明统计数据面板沿底部,高度为 24 像素,如图 1 所示。
将这些值转换为百分比会使画布的宽度为 100%,高度为 100%(即 gameArea,而不是窗口)。将 24 乘以 300 得到的统计数据面板的高度为 8%,由于它将覆盖游戏区域的底部,因此宽度也为 100%,如图 2 所示。
现在,您已经确定了游戏区域及其子元素的尺寸,您可以将内两者的 CSS 属性放在一起,如下所示:
#gameCanvas {
width: 100%;
height: 100%;
}
#statsPanel {
position: absolute;
width: 100%;
height: 8%;
bottom: 0;
opacity: 0.8;
}
调整游戏大小
现在,您可以创建函数来处理正在调整大小的窗口了。首先,获取对父 gameArea 文档元素的引用。
var gameArea = document.getElementById('gameArea');
由于您不关心确切的宽度或高度,因此需要设置的下一项信息是宽高比。根据您之前对宽 400 像素、高 300 像素的游戏区域的引用,您知道要将宽高比设置为 4 个单位宽 3 个单位高。
var widthToHeight = 4 / 3;
由于每次调整窗口大小时都会调用此函数,因此您还需要获取窗口的新尺寸,以便调整游戏的尺寸以保持一致。可以使用窗口的 innerWidth 和 innerHeight 属性查找此内容。
var newWidth = window.innerWidth;
var newHeight = window.innerHeight;
就像您确定了所需的宽高比一样,现在您可以确定窗口的当前宽高比:
var newWidthToHeight = newWidth / newHeight;
这样您就可以决定是让游戏垂直还是水平填满屏幕,如图 3 所示。
如果所需的游戏区域形状大于窗口形状(且高度较短),您需要水平填满窗口,并在顶部和底部留出外边距。同样,如果所需的游戏区域形状高于窗口的形状(并且宽度较窄),您就需要垂直填满窗口,并左右保留外边距。
为此,请使用当前窗口的宽高比测试所需的宽高比,然后按以下步骤进行适当的调整:
if (newWidthToHeight > widthToHeight) {
// window width is too wide relative to desired game width
newWidth = newHeight * widthToHeight;
gameArea.style.height = newHeight + 'px';
gameArea.style.width = newWidth + 'px';
} else { // window height is too high relative to desired game height
newHeight = newWidth / widthToHeight;
gameArea.style.width = newWidth + 'px';
gameArea.style.height = newHeight + 'px';
}
调整了游戏区域的宽度和高度之后,您需要在顶部(即高度的一半)和左侧(即宽度的一半)放置一个负外边距,使其居中显示。请注意,CSS 已经将 gameArea div 的左上角放在窗口的正中央,因此这会将游戏区域放在窗口中的中心:
gameArea.style.marginTop = (-newHeight / 2) + 'px';
gameArea.style.marginLeft = (-newWidth / 2) + 'px';
您还希望自动调整字体大小。如果所有子元素都使用 em,则只需将 gameArea div 代码块的 fontSize CSS 属性设置为一个由其大小确定的值即可。
gameArea.style.fontSize = (newWidth / 400) + 'em';
最后,您需要将画布绘图的尺寸设置为与其新的宽度和高度相匹配。请注意,游戏代码的其余部分必须将游戏引擎尺寸与画布绘制尺寸分开,以适应动态画布分辨率。
var gameCanvas = document.getElementById('gameCanvas');
gameCanvas.width = newWidth;
gameCanvas.height = newHeight;
因此,完成后的调整大小函数可能如下所示:
function resizeGame() {
var gameArea = document.getElementById('gameArea');
var widthToHeight = 4 / 3;
var newWidth = window.innerWidth;
var newHeight = window.innerHeight;
var newWidthToHeight = newWidth / newHeight;
if (newWidthToHeight > widthToHeight) {
newWidth = newHeight * widthToHeight;
gameArea.style.height = newHeight + 'px';
gameArea.style.width = newWidth + 'px';
} else {
newHeight = newWidth / widthToHeight;
gameArea.style.width = newWidth + 'px';
gameArea.style.height = newHeight + 'px';
}
gameArea.style.marginTop = (-newHeight / 2) + 'px';
gameArea.style.marginLeft = (-newWidth / 2) + 'px';
var gameCanvas = document.getElementById('gameCanvas');
gameCanvas.width = newWidth;
gameCanvas.height = newHeight;
}
现在,您希望每当窗口大小调整或屏幕方向改变(如果使用的是移动设备)时,系统都会自动做出这些调整。处理这些事件,只需让它们调用 sizeGame()函数即可,如下所示:
window.addEventListener('resize', resizeGame, false);
window.addEventListener('orientationchange', resizeGame, false);
如果窗口的尺寸调整得过高或屏幕方向为竖向,您将采用窗口宽度的 100%;如果窗口大小被调整得过宽或屏幕方向为水平方向,则会将窗口高度调整为 100%。其余尺寸按照预定的宽高比调整。
摘要
Gopherwood Studios 在我们的所有 HTML5 游戏中都使用了这种结构,并且事实证明它对于适应多种屏幕分辨率和各种移动设备非常有用。此外,借助全屏浏览器,我们的网页游戏与许多基于浏览器的游戏相比,更接近传统桌面游戏的沉浸式体验。随着 HTML5 和网络技术的不断发展,我们期待网络游戏领域继续推出更多创新。