使用JavaScript和Canvas開發遊戲(一)

李鬆峰發表於2011-08-24

原文作者:Matthew Casperson • 編輯:Michele McDonough
原文連結: Game Development with JavaScript and the Canvas element

  1. 認識一下Canvas
  2. 在Canvas上繪圖
  3. 通過Canvas元素實現高階影像操作
  4. 通過Canvas實現視差滾動
  5. 寫一個遊戲框架(一)
  6. 寫一個遊戲框架(二)
  7. 動畫
  8. JavaScript鍵盤輸入
  9. 綜合運用
  10. 定義級別
  11. 跳躍與墜落
  12. 新增道具
  13. 載入資源
  14. 新增主選單

1、認識一下Canvas

http://www.brighthub.com/internet/web-development/articles/38364.aspx

Canvas元素以及JavaScript引擎中新增的一些特性,讓Web開發人員不必藉助第三方外掛,即可設計開發出精細且具有互動性的2D網頁。這篇文章就向大家介紹一下Canvas元素,以及它的一些可能的用途。

JavaScript與Canvas元素

HTML是為建立靜態頁面而生的。HTML所能實現的動態效果,也僅限於顯示GIF動畫和閃爍的文字。JavaScript改變了這一切,通過它能夠動態修改網頁。今天,很多Web服務都利用AJAX來建立網頁,為使用者提供更加流暢的體驗,也超越了標準HTML頁面中常見的“點選-重新載入-點選”式的互動模式。

然而,JavaScript的某些功能會受到其宿主瀏覽器的制約。儘管可以在網頁中建立和修改任何元素,但JavaScript不能(輕易地)讓瀏覽器顯示一種新物件。通過JavaScript修改文字、插入影像或者縮放表格都很容易,因為這些物件本來就是HTML所支援的。如果你想再玩得刺激一點,比如寫一個網頁遊戲,怎麼辦?那恐怕就得苦心積慮地改變標準HTML元素的用途,克服種種不測才能達到目的。要麼,你就得求助於Flash或Silverlight這樣的外掛。

Canvas元素登場了。這個新HTML元素為JavaScript開發者提供了一種無需外掛即可在網頁中直接繪圖的機制。Canvas元素最早是由蘋果公司在其WebKit框架中引入的,Safari瀏覽器和Dashboard微件都在使用。Canvas元素現在也被建議加入了HTML5規範,得到了最新的Chrome、Firefox、Opera以及Konqueror等瀏覽器的支援。Internet Explorer(至少在IE8之前)還不支援Canvas,但ExplorerCanvas專案倒是為IE提供了與Canvas元素類似的功能。

Canvas元素對做過2D圖形程式設計的人是小菜一碟。可以在這個元素上畫線、畫各種形狀、畫漸變,甚至可以利用與其他2D API中類似的函式來修改其中的每一個畫素。得益於Chrome的V8、Firefox的SpiderMonkey以及Safari的Nitro等最新JavaScript引擎的效能,建立精細且具有互動性的Web應用已經完全沒有問題。

我們這一系列文章將教會大家使用JavaScript和Canvas元素建立一個簡單的平臺遊戲。將要涉及的內容包括動畫、載入資源、分層渲染、滾動和互動。通過一步一步地展示示例程式碼和實際效果,你可以很快學會如何駕馭強大的Canvas元素。

2、在Canvas上繪圖

http://www.brighthub.com/internet/web-development/articles/38744.aspx

下面,我們就通過一個循序漸進的示例及實時演示,來介紹如何使用JavaScript在Canvas元素上繪圖及實現動畫。

準備工作

知道了什麼是Canvas元素之後,該學習在螢幕上繪圖了。首先,需要一個HTML頁面來放置和顯示Canvas元素。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
  <html lang="en">
     <head>
        <title>JavaScript Platformer 1</title>
        <script type="text/javascript" src="jsplatformer1.js"></script>
        <style type="text/css">
           body { font-family: Arial,Helvetica,sans-serif;}
        </style>
     </head>
    <body>
       <p>
          <a href="http://www.brighthub.com/internet/web-development/articles/38364.aspx">
             Game Development with Javascript and the canvas element
          </a>
       </p>
       <canvas id="canvas" width="600" height="400">
          <p>Your browser does not support the canvas element.</p>
       </canvas>
    </body>
 </html>

這些HTML程式碼很直觀。其中有兩個重要的元素。

這裡包含的是將會修改Canvas元素的JavaScript程式碼,對應的Canvas元素的標記如下:

<canvas id="canvas" width="600" height="400">
    <p>Your browser does not support the canvas element.</p>
</canvas>

以上程式碼建立了一個Canvas元素。不支援Canvas的瀏覽器,比如Internet Explorer(IE8之前的版本),會忽略這個元素,而只顯示其子元素。在這個簡單的例子中,這個子元素就是一個段落,其中的文字告訴使用者他們的瀏覽器不支援Canvas元素。而對於那些支援Canvas元素的瀏覽器,如Chrome、Opera和Firefox,則會忽略Canvas元素的子元素。

這個Canvas元素的ID屬性很重要,因為後面的JavaScript將通過它來取得對該元素的引用。而width和height屬性指定了畫布的寬度和高度,這兩個屬性跟table或img等其他HTML元素中的同名屬性作用一樣。

以下是 jsplatformer1.js的程式碼:

//每秒鐘target幀
const FPS = 30;
var x = 0;
var y = 0;
var xDirection = 1;
var yDirection = 1;
var image = new Image();
//建議讀者將圖片下載到本地載入(經測試,此圖片響應頭部的Content-Type為application/empty,瀏覽器無法識別)
image.src = "http://javascript-tutorials.googlecode.com/files/jsplatformer1-smiley.jpg";
var canvas = null;
var context2D = null;

window.onload = init;
function init(){
    canvas = document.getElementById('canvas');
    context2D = canvas.getContext('2d');
    setInterval(draw, 1000/FPS);
}
function draw(){
    context2D.clearRect(0, 0, canvas.width, canvas.height);
    context2D.drawImage(image, x, y);
    x += 1* xDirection;
    y += 1* yDirection;

    if (x >= 450) {
        x = 450;
        xDirection = -1;
    }else if(x <= 0){
        x = 0;
        xDirection = 1;
    }
    if (y >= 250) {
        y = 250;
        yDirection = -1;
    }else if(y <= 0){
        y = 0;
        yDirection = 1;
    }
}

如果只是一個Canvas元素,也沒有什麼用。JavaScript必須要在這塊畫布上面畫點什麼,相應的程式碼儲存在 jsplatformer1.js中。簡單來說,JavaScript在這裡先載入了一幅影像,然後將其畫在畫布上面,最後讓它在畫布上移動。

首先,定義一些全域性變數。

const FPS = 30;

FPS定義的是畫布重繪的頻率。

var x = 0;
var y = 0;
var xDirection = 1;
var yDirection = 1;

變數x、y、xDirection和yDirection用於定義影像(相對於畫布左上角)的位置,以及它在任意一時刻移動的方向。

var image = new Image();
image.src = "http://javascript-tutorials.googlecode.com/files/jsplatformer1-smiley.jpg";

要把影像畫到畫布上,必須先載入一幅影像。為此,我們建立一個Image物件,將其src屬性設定為一幅影像檔案的URL(建議把圖片下載到本地。——譯者注)。

var canvas = null;
var context2D = null;

我們還需要取得對Canvas元素以及繪圖上下文(稍後再詳細介紹繪圖上下文)的引用。稍後我們會把正確的值賦給這兩個變數,現在先把它們設定為null。

window.onload = init;

最後,當頁面載入完成後,我們必須知道立即執行繪製畫布的程式碼;因此,在window物件的onload事件發生時,立即呼叫init函式。

function init(){
    canvas = document.getElementById('canvas');
    context2D = canvas.getContext('2d');
    setInterval(draw, 1000/FPS);
}

頁面載入完畢後就會呼叫上面這個init函式。在這個函式中,我們先通過在HTML檔案中指定的ID屬性取得畫布元素(毫無疑問,除了把它叫做畫布,還能叫個啥?),然後再取得這個畫布的2D繪圖上下文物件。

上下文物件用於定義如何在畫布上繪圖。顧名思義,2D上下文嘛,支援在畫布上繪製2D圖形、影像和文字。支援畫布元素的瀏覽器都支援2D上下文,除了2D上下文,還有其他試驗性的上下文物件。Opera有一個專門為遊戲設計的2D上下文,而Mozilla則有一個能夠顯示3D場景的上下文。可惜呀,目前這些上下文物件只有特定的瀏覽器才支援。如果你想用畫布來建立Web應用,最好還是隻使用常見的2D上下文。

因為我們在這裡是想繪製一幅能移動的影像,所以必須建立渲染迴圈(render loop)。所謂渲染迴圈,實際上就是一個被重複呼叫的函式,渲染迴圈的每一次迭代,(在這個例子中)都可以讓影像在螢幕上產生一點位移,如此迴圈往復就能給人影像在移動的感覺。為此,我們呼叫了setInterval函式,它的第一個引數是應該被重複呼叫的函式,這裡的函式名是draw。setInterval函式的第二個引數指定呼叫函式的頻率。這個引數值的單位是毫秒,而用1000除以早先定義的FPS得到的就是每次呼叫之間相隔的毫秒數。

這裡需要注意一下,雖然我們指定每秒鐘呼叫30次draw函式,但實際上不會呼叫30次。多長時間呼叫一次draw函式,取決於底層JavaScript引擎的速度和要執行的draw函式程式碼的複雜程度。如果系統很慢的話,很可能每秒鐘只能呼叫一次draw函式。所以說,這裡指定給setInterval的頻率只是一種最理想的情況。

draw函式

在畫布上繪圖的操作實際上都是由draw函式來完成的。下面我們就一步一步地說明其中的繪圖操作。

context2D.clearRect(0, 0, canvas.width, canvas.height);

所有繪圖操作都是在上下文物件上發生的,並不是在畫布元素上發生的。這裡首先清空上下文,以便為繪製每一幀畫面準備一個乾淨的版面。

context2D.drawImage(image, x, y);

緊接著,就把影像繪製到上下文物件中,引數x和y指定了繪製影像的左上角座標。

x += 1 * xDirection;
y += 1 * yDirection;

為了讓影像在畫布上移動,需要根據xDirection和yDirection是等於1(向右或向下)還是等於-1(向左或向上),來遞增或遞減x與y的值。

if (x >= 450){
    x = 450;
    xDirection = -1;
} else if (x <= 0) {
    x = 0;
    xDirection = 1;
}
if (y >= 250) {
    y = 250;
    yDirection = -1;
} else if (y <= 0) {
    y = 0;
    yDirection = 1;
}

如果影像移動到了畫布外面,則反轉影像的移動方向。我們知道影像的大小是150×150畫素,而畫布的大小的是600×400畫素,因而就有了450(600 - 150)和250(400 - 150)這兩個值。

最後的效果就是笑臉影像會在畫布的範圍內反彈往復。此時此刻,有讀者可能會想:同樣的效果如果通過修改DIV元素的位置來實現可能更容易一些。這一點我不否認。但這個例子只演示了畫布元素所能實現的簡單效果。下一篇文章我們就會介紹使用畫布元素能夠實現的高階效果,同樣的效果若採用其他方式,恐怕就要困難多了。

為之漫筆 最後編輯於: 2011/08/12 @ 06:39

相關文章