用HTML5開發一個小遊戲

先上效果圖

替代文字

開始以前的準備

  1. game.html
  2. js/ 裏面建立game.js
  3. images/ 裏面放三張圖片,一張背景圖片(background.png),一張英雄圖片(hero.png),一張怪物的圖片(monster.png)

game.html裏面寫上如下幾行簡單的HTML代碼:html


<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Simple Canvas Game</title> </head> <body> <script src="js/game.js"></script> </body> </html>

咱們在game.html引入了game.js文件,沒錯,剩下的全部工做都是在操做game.js,爲其添加遊戲的js代碼。前端

建立畫布

在game.js 裏面,咱們首先須要爲遊戲的舞臺建立一張畫布(canvas):web

var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);

這裏經過js來建立了一個元素並設置canvas的寬和高,最後將其添加到<body>標籤後。var ctx = canvas.getContext("2d");中的ctx變量是咱們後面會用到的,具體的canvas用法查看這裏的連接:canvas

https://developer.mozilla.org/en/canvas_tutorial瀏覽器

準備圖片

遊戲須要加載咱們以前存放在images文件夾下面的三張圖片:app

// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
    bgReady = true;
};
bgImage.src = "images/background.png";

// Hero image
var heroReady = false;
var heroImage = new Image();
heroImage.onload = function () {
    heroReady = true;
};
heroImage.src = "images/hero.png";

// Monster image
var monsterReady = false;
var monsterImage = new Image();
monsterImage.onload = function () {
    monsterReady = true;
};
monsterImage.src = "images/monster.png";

以上三張圖片都是經過建立簡單的圖片對象來實現加載的,相似bgReady的三個變量用來標識圖片是否已經加載完成,若是若是在圖片加載未完成狀況下進行繪製是會報錯的。若是你不太肯定new Image()究竟是個什麼東西,你能夠在bgImage.src = "images/background.png";以後使用console.log(bgImage);來查看,你看到的將是相似:dom

<img src="images/background.png" >

遊戲對象

咱們須要定義一些對象,以便咱們在後面會用到:異步

var hero = {
    speed: 256 // movement in pixels per second
};
var monster = {};
var monstersCaught = 0;

既然是英雄抓獲怪物,咱們得要有一個英雄怪物的對象。而英雄有一個speed屬性用來控制他每秒移動多少像素。怪物遊戲過程當中不會移動,因此暫時不用設置屬性。monstersCaught則用來存儲怪物被捉住的次數,初始值固然爲0了。函數

處理用戶的輸入

遊戲是給人玩的,那麼咱們怎麼知道用戶到底在這個過程當中幹了什麼?按了鍵盤?點了鼠標?這些都是用戶在玩遊戲的時候的輸入,因此咱們一旦捕獲到這些輸入,咱們就能夠根據遊戲的邏輯對用戶的輸入進行處理了:oop

// Handle keyboard controls
var keysDown = {};

addEventListener("keydown", function (e) {
    keysDown[e.keyCode] = true;
}, false);

addEventListener("keyup", function (e) {
    delete keysDown[e.keyCode];
}, false);

這裏咱們只是監聽兩個用戶的輸入:

  1. keydown
  2. keyup

而後咱們將用戶的輸入先保存起來,並無當即響應。爲此,咱們用keysDown這個對象來保存用戶按下的鍵值(keyCode),若是按下的鍵值在這個對象裏,那麼咱們就作相應處理。

在前端開發中,通常是用戶觸發了點擊事件而後纔去執行動畫或發起異步請求之類的

開始一輪遊戲

遊戲在結束的時候,咱們須要開始新的一輪遊戲,因此在game.js添加reset函數

// Reset the game when the player catches a monster
var reset = function () {
    hero.x = canvas.width / 2;
    hero.y = canvas.height / 2;


    // Throw the monster somewhere on the screen randomly
    monster.x = 32 + (Math.random() * (canvas.width - 64));
    monster.y = 32 + (Math.random() * (canvas.height - 64));

};

reset()函數用於開始新一輪和遊戲,在這個方法裏咱們將英雄放回畫布中心同時將怪物放到一個隨機的地方。

更新對象

在遊戲的過程當中,不論是用戶在玩(有正確輸入的狀態)仍是遊戲結束,咱們都是須要及時更新遊戲的對象:

var update = function (modifier) {
    if (38 in keysDown) { // Player holding up
        hero.y -= hero.speed * modifier;
    }
    if (40 in keysDown) { // Player holding down
        hero.y += hero.speed * modifier;
    }
    if (37 in keysDown) { // Player holding left
        hero.x -= hero.speed * modifier;
    }
    if (39 in keysDown) { // Player holding right
        hero.x += hero.speed * modifier;
    }

    // Are they touching?
    if (
        hero.x <= (monster.x + 32)
        && monster.x <= (hero.x + 32)
        && hero.y <= (monster.y + 32)
        && monster.y <= (hero.y + 32)
    ) {

        ++monstersCaught;
        reset();
    }


};

update函數負責更新遊戲的各個對象,會被規律地重複調用。首先它負責檢查用戶當前按住的是中方向鍵,而後將英雄往相應方向移動。

有點費腦力的或許是這個傳入的modifier 變量。你能夠後面將要實現的main 方法裏看到它的來源,但這裏仍是有必要詳細解釋一下。它是基於1開始且隨時間變化的一個因子。例如1秒過去了,它的值就是1,英雄的速度將會乘以1,也就是每秒移動256像素;若是半秒鐘則它的值爲0.5,英雄的速度就乘以0.5也就是說這半秒內英雄以正常速度一半的速度移動。理論上說由於這個update函數被調用的很是快且頻繁,因此modifier的值會很小,但有了這一因子後,無論咱們的代碼跑得快慢,都可以保證英雄的移動速度是恆定的。

這裏須要說明一下下面的判斷怪物和英雄是什麼根據:

if (
        hero.x <= (monster.x + 31)
        && monster.x <= (hero.x + 31)
        && hero.y <= (monster.y + 32)
        && monster.y <= (hero.y + 32)
    )

上面的31,32是由heromonster圖片的大小決定的,咱們的hero圖片是32x32,monster圖片是30x32,因此根據座標的位於圖片中心的法制,就能夠獲得上面的判斷條件。

如今英雄的移動已是基於用戶的輸入(按下鍵)了,接下來該檢查移動過程當中所觸發的事件了,也就是英雄與怪物相遇。這就是本遊戲的勝利點,monstersCaught +1而後從新開始新一輪。

渲染物體

以前寫的代碼都是在準備前期工做和處理一些遊戲的狀態等,下面將進入正題:咱們須要將全部的東西畫出來

// Draw everything
var render = function () {
    if (bgReady) {
        ctx.drawImage(bgImage, 0, 0);
    }

    if (heroReady) {
        ctx.drawImage(heroImage, hero.x, hero.y);
    }

    if (monsterReady) {
        ctx.drawImage(monsterImage, monster.x, monster.y);
    }

    // Score
    ctx.fillStyle = "rgb(250, 250, 250)";
    ctx.font = "24px Helvetica";
    ctx.textAlign = "left";
    ctx.textBaseline = "top";
    ctx.fillText("Goblins caught: " + monstersCaught, 32, 32);
};

這裏的ctx就是最前面咱們建立的變量。而後利用canvasdrawImage()首先固然是把背景圖畫出來。而後如法炮製將英雄和怪物也畫出來。這個過程當中的順序是有講究的,由於後畫的物體會覆蓋以前的物體。

這以後咱們改變了一下Canvas的繪圖上下文的樣式並調用fillText來繪製文字,也就是記分板那一部分。本遊戲沒有其餘複雜的動畫效果和打鬥場面,繪製部分大功告成

主循環函數

咱們實現了將畫面畫出來之後,咱們緊接着須要實現的就是遊戲的循環結構,因而將它放在main函數裏:

// The main game loop
var main = function () {
    var now = Date.now();

    var delta = now - then;
    //console.log(delta);
    update(delta / 1000);
    render();

    then = now;

    // Request to do this again ASAP
    requestAnimationFrame(main);
};

上面的主函數控制了整個遊戲的流程。先是拿到當前的時間用來計算時間差(距離上次主函數被調用時過了多少毫秒)。獲得modifier後除以1000(也就是1秒中的毫秒數)再傳入update函數。最後調用render 函數而且將本次的時間保存下來。

設置requestAnimationFrame()

在上面的main函數中,咱們經過requestAnimationFrame()調用了main函數,因此咱們須要聲明:

var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;

這裏這麼多的||,不爲別的,就是考慮到瀏覽器兼容問題而已。

最後啓動遊戲

萬事具有,只欠東風。到此,全部的遊戲代碼基本就寫完了,咱們如今須要作的就是調用相應的函數來啓動遊戲:


// Let's play this game! var then = Date.now(); reset(); main();

到這裏代碼就寫完了。先是設置一個初始的時間變量then用於首先運行main函數使用。而後調用 reset 函數來開始新一輪遊戲(若是你還記得的話,這個函數的做用是將英雄放到畫面中間同時將怪物放到隨機的地方以方便英雄去捉它)

用瀏覽器打開game.html,開始玩遊戲吧!

進一步思考

在玩遊戲的過程當中,你會發現每一次hero捕獲到monsterhero就回到了canvas畫布的正中間。那麼如今須要作的就是,將hero在捕捉到monster的時候讓hero就停留在捕獲的位置,再也不是回到canvas正中間。

這個現象的出現主要是由於在reset函數中將hero.xhero.y寫死了,因此一個最簡單的方法就是在reset中傳入參數:

var reset = function (x,y) {
    hero.x = x;
    hero.y = x;
};

而後在update調用reset的時候傳入捕獲時位置的參數:

var update = function (modifier) {

        //...other codes

        ++monstersCaught;
        reset(heor.x,hero.y);

};

最後在開始遊戲的時候將hero放在canvas最中間便可:


var then = Date.now(); reset(canvas.width / 2,canvas.height / 2); main();

大功告成!

Hapyy Hacking

相關文章
相關標籤/搜索