自動縮放 HTML5 遊戲的解析度

11 篇文章 / 0 新
最新文章
Yusen Jeng
Yusen Jeng's 的頭像
自動縮放 HTML5 遊戲的解析度

=============================================
原文來自: Case Study: Auto-Resizing HTML5 Games
作者: Derek Detweiler
=============================================

自動縮放 HTML5 遊戲的畫面解析度

2010 年夏天,Gopherwood Studios 寫了 Sand Trap 這款完全由 HTML5 製作的小遊戲,手機可玩!可多數手機不是只能部分顯示,就是畫面整個太小,嚴重降低可玩性,所以想了點辦法讓遊戲能夠順暢地適應各種螢幕解析度。經過一番重工以及本文提及的技巧之後,可得到一個畫面伸縮自如的遊戲、跨多數桌機手機上的主流瀏覽器。

本文的方法在 Sand Trap 運行得很好,所以工作室也將把相同技巧用在下一款遊戲 Thwack!這款遊戲自動適應螢幕解析度,如上圖所示,不管全螢幕模式或著視窗模式都成。

實作面需要一點進階的 CSS 與 JavaScript 知識。用 CSS 填滿螢幕很簡單,但 CSS 無法對畫布維持等比例縮放,於是 JS 來了,JS 能計算、縮放整份網頁文件,並且觸發縮放的事件處理機制。

準備網頁頁面

第一步要先界定遊戲在螢幕中的區域。若使用 div 標籤包覆遊戲區域,可把 canvas 或其他標籤放進 div 內,之後若設定得當,內部元素會跟著外部 div 同時縮放。

如果遊戲區域內有遊玩、計分兩個區塊,看起來會像這樣:

<div id="”gameArea”"><canvas id="”gameCanvas”"></canvas>  
<div id="”statsPanel”"> </div>
</div>

然後馬上設定這些標籤的 CSS 屬性,給伸縮功能鋪路。後續有些 CSS 屬性得用 JS 程式修改,但在此還是先幫外部標籤 gameArea 設定一部分基本屬性。

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

這招把 canvas 的左上角放置螢幕正中央,下一段落還要會寫點 JS 程式添加更多 CSS 來縮放遊戲區域。

因為遊戲區域自動參考視窗大小縮放,改變之後不應讓 gameArea 因此擋住其他網頁元素,最好照百分比伸縮!雖然內部元素的 pixel 值無法在外部 div 改變時跟著變,但可以先設常數 pixel 值,後續再視情況轉用百分比表示法。

例如,一開始遊戲區域為寬 400px 高 300px,canvas 佔據整個遊戲區域,且下方有個半透明的計分列,高度 24 px。

把這些值改為百分比表示法,8% 來自於 24px / 300px,當然,statsPanel 的寬度也是 100%。

現在可以填寫內部元素個別的 CSS 屬性了,參考上圖寫出設定:

#gameCanvas {
width: 100%;
height: 100%;
}
 
#statsPanel {
position: absolute;
width: 100%;
height: 8%;
bottom: 0;
opacity: 0.8;
}

縮放遊戲畫面

動手寫個函式吧,首先把 gameArea 元件取出。

var gameArea = document.getElementById('gameArea');

因為縮放功能需要 pixel 所佔比例而非實際值,從最初始的寬高設定得知寬高比為 4:3。

var widthToHeight = 4 / 3;

當此函式被視窗改變大小事件觸發時,需要知道當時視窗的大小來配套,可用以下方式取得資訊。

var newWidth = window.innerWidth;
var newHeight = window.innerHeight;

於是得知了視窗當下的寬高比例。

var newWidthToHeight = newWidth / newHeight;

這份資訊讓程式曉得要用符合寬度或著符合高度的作法來伸縮畫面,示意圖如下:

如果新的畫面較窄且高,便選擇符合寬度的作法,讓上方和下方留白。若新畫面較為寬矮,就選符合高度的作法。

測試新畫面的寬高比例可以幫忙判斷是哪種狀況,並選擇恰當作法。

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

這就成功伸縮完畫面了,接著實施置中操作。之前僅置中了遊戲區域的左上角,現在要將遊戲畫面的正中央置中。為此程式得讓 margin 的上緣倒退半個高度、左緣倒退半個寬度。

gameArea.style.marginTop = (-newHeight / 2) + 'px';
gameArea.style.marginLeft = (-newWidth / 2) + 'px';

字型能跟著螢幕縮放更好,讓字型大小由螢幕寬度決定。如果這裡只用到一種字型風格,那修改 gameArea div 的 CSS 即可。

gameArea.style.fontSize = (newWidth / 400) + 'em';

最後,來讓 canvas 大小也跟著變。切記遊戲相關的程式必須讓更新自己的邏輯區域大小,來配合新的繪圖區域大小,因為畫面解析度已經是動態的了。

var gameCanvas = document.getElementById('gameCanvas');
gameCanvas.width = newWidth;
gameCanvas.height = newHeight;

整理一下完整的 resize 縮放函式:

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

讓縮放函式在視窗改變大小時自動被呼叫,比如說旋轉手機螢幕造成畫面改變,可以 callback 形式把它掛到相關事件。

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

如果視窗畫面太高或著手機直立起來,畫面就縮放成符合寬度,反之則縮放成符合高度。縮放的比例依據參考自最初始時直接設定的寬與高 pixel 值。

結論

Gopherwood Studios (開發遊戲 Sand Trap) 已經在自己的幾個 HTML5 遊戲上運用這類縮放方法,證實在多重螢幕解析度的現實狀況非常好用(特別是移動裝置)。此外,支援瀏覽器全螢幕模式讓他們的遊戲提供了比一般網頁遊戲更接近桌機遊戲的擬真體驗。Gopherwood Studios 會持續追求像 HTML5 或其他網路技術在網頁遊戲的創新發展。

frank1232100
frank1232100's 的頭像

請問 

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

要打在哪裡?
 

johnson_lin
johnson_lin's 的頭像

那是 css,就寫在你吃進來的 css 檔裡吧

frank1232100
frank1232100's 的頭像

to johnson_lin: 
問題1:
是這樣的小弟寫了一個canvas的小遊戲,可是照著本文章所打沒辦法達到所謂的自動縮放
問題2:
我還有一個問題就是 我用瀏覽器執行,假如我把網頁整體滑動 就會出現xy座標消失的現象
,也就是說無法正常執行遊戲
我本身遊戲外成事<html>包著<sj>並無使用到css請問他要寫在<html>~</html>之間 還是<body>~
</body>還是<head>? 是用<style>標籤? 有沒有其他的聯絡方式.信箱?

frank1232100
frank1232100's 的頭像

希望能在口語化的講解一下 哪一行寫在哪個標籤裡或是怎樣的,小弟程式不太好理解力又差

Yusen Jeng
Yusen Jeng's 的頭像

Hi Frank,

一般 HTML 引用外部 CSS 的標籤寫在 <head> 之間,類似
https://gist.github.com/anonymous/6409791

而 CSS 檔案的內容只有 CSS
https://gist.github.com/anonymous/6409812

 

miriam
miriam's 的頭像

你好:
statspane的高度px變%數
事要如何去換算??

johnson_lin
johnson_lin's 的頭像

照文中範例來說的話 .... 就 24 / 300 = 0.08 = 8% ?@_@

學昱 呂
學昱 呂's 的頭像

參考了文章實作後,打包起來方便重複使用。github link

johnson_lin
johnson_lin's 的頭像

感謝樓上 XD

Hiro
Hiro's 的頭像

不知道是不是我的錯覺,手機上似乎有點 blur?
ps: 我用 nexus5 看得。