個案研究 - 自動重新調整 HTML5 遊戲大小

Derek Detweiler
Derek Detweiler

簡介

2010 年夏天,我們製作了「Sand Trap」遊戲,並參加行動裝置 HTML5 遊戲競賽。但大多數手機都只顯示部分遊戲畫面,或是將遊戲縮小到無法操作的程度。因此,我們決定自行調整遊戲,讓遊戲能流暢地調整至任何解析度。經過一些重新編寫程式和運用本文所述的構想後,我們製作的遊戲便可在任何新式瀏覽器中執行,無論是桌上型電腦或行動裝置皆可。

thwack 全螢幕畫面的螢幕截圖
thwack 在瀏覽器視窗中的螢幕截圖 (較小)

這種方法在《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 操控,但為了讓這些屬性運作,請設定幾個其他 CSS 屬性,並以父項 gameArea div 區塊開始:

#gameArea {
 
position: absolute;
 
left:     50%;
 
top:      50%;
}

這樣一來,畫布的左上角就會位於螢幕中央。下一個章節所述的 JavaScript 自動調整大小函式會操控其他 CSS 屬性,藉此調整遊戲區大小,並將遊戲區置於視窗中央。

由於遊戲區會根據視窗的尺寸自動調整大小,因此請勿使用像素為單位的尺寸來設定 gameArea div 區塊的子項元素,而是應使用百分比。像素值不允許內部元素隨著父 div 的變更而縮放。不過,建議您先以像素為單位開始,然後在找到合適的版面配置後,再將像素轉換為百分比。

在本例中,遊戲區域的高度和寬度分別為 300 和 400 像素。畫布會覆蓋整個遊戲區域,並在底部顯示 24 像素高的半透明統計資料面板,如圖 1 所示。

gameArea 子項元素的尺寸 (以像素為單位)
圖 1:gameArea 子元素的尺寸 (以像素為單位)

將這些值轉換為百分比後,畫布的寬度和高度 (gameArea 的高度,而非視窗高度) 會變成 100%。將 24 除以 300 後,統計資料面板的高度為 8%,由於它會覆蓋遊戲區域的底部,因此寬度也會是 100%,如圖 2 所示。

gameArea 子元素的尺寸 (以百分比表示)
圖 2:gameArea 子項元素的尺寸 (以百分比表示)

既然您已確定遊戲區域及其子項元素的尺寸,現在可以將兩個內部元素的 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 所示。

將 gameArea 元素套用至視窗,同時維持顯示比例
圖 3:將 gameArea 元素套用至視窗,同時維持顯示比例

如果所需的遊戲區域形狀比視窗形狀寬 (且高度較短),您必須將視窗填滿,並在頂端和底部留出邊距。同樣地,如果所需的遊戲區域形狀高於視窗形狀 (且寬度較窄),您需要將視窗填滿,並在左右留出邊界。

如要這麼做,請使用目前視窗的寬高比,測試所需的寬高比,然後根據以下方式進行適當調整:

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;
}

接下來,您希望在調整視窗大小或變更行動裝置螢幕方向時,系統能自動進行這些調整。請讓這些事件呼叫 resizeGame() 函式,以便處理這些事件,如下所示:

window.addEventListener('resize', resizeGame, false);
window
.addEventListener('orientationchange', resizeGame, false);

如果視窗的大小調整過高,或螢幕方向為直向,則視窗的寬度會調整為 100%;如果視窗的大小調整過寬,或螢幕方向為橫向,則視窗的高度會調整為 100%。系統會根據預先設定的寬高比,調整剩餘的尺寸。

摘要

Gopherwood Studios 已在所有 HTML5 遊戲中使用這類結構的版本,證明這類結構非常適合用於支援多種螢幕解析度和各種行動裝置。此外,透過全螢幕瀏覽器的輔助,我們的網頁遊戲能提供沉浸式體驗,與許多以瀏覽器為基礎的遊戲相比,更接近傳統桌上型遊戲。隨著 HTML5 和網頁技術持續進化,我們期待網頁遊戲能有更多創新。