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

Derek Detweiler
Derek Detweiler

簡介

2010 年夏季,我們打造了 Sand Trap 這款手機遊戲,並參與智慧型手機 HTML5 遊戲競賽。但大多數手機都只展示部分遊戲,就是尺寸過小,因此完全無法玩遊戲。因此,我們自行挑選出合適的遊戲尺寸,讓遊戲能夠流暢地配合各種解析度調整。我們稍微重新設計了遊戲程式,並運用本文所述的構想後,成功打造出一款適用於各種新型瀏覽器的遊戲,無論是在電腦或行動裝置上執行都沒問題。

thwack 全螢幕的螢幕截圖
顯示縮小在瀏覽器視窗中縮小畫面的螢幕截圖

這個做法非常適合 Sand Trap,因此我們最新的遊戲《Twack!》也採用相同的做法。遊戲會自動調整螢幕解析度,以符合全螢幕和自訂大小的視窗,如以下螢幕截圖所示。

導入這段程式碼必須同時善用 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 所示。

gameArea 子元素的維度 (以像素為單位)
圖 1: gameArea 子元素的維度,以像素為單位

將這些值轉譯為百分比後,畫布寬度會變為 100%,高度為 100% (在 gameArea 上,而非視窗)。將 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;
}

現在,您希望在每次重新調整視窗大小時,或 (行動裝置) 的螢幕方向改變時,自動做出這些調整。讓這些事件呼叫 sizeGame() 函式以處理這些事件,如下所示:

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

如果調整視窗大小過高或螢幕方向為直向,則表示視窗寬度已增加 100%,如果視窗縮小了寬度,或是螢幕方向為橫向,高度就會佔視窗的 100%。剩餘的尺寸將根據預先定義的寬高長寬比進行調整。

摘要

Gopherwood Studios 在所有 HTML5 遊戲中都採用這種架構,而且在支援多種螢幕解析度和各種行動裝置時非常實用。此外,由於我們支援全螢幕瀏覽器,因此網頁遊戲的沉浸式體驗比傳統桌面遊戲來得有別,而非許多瀏覽器遊戲。隨著 HTML5 和網頁技術持續發展,我們期許能創造更多網路遊戲的創新成果。