摘要:它是基于開(kāi)始且隨時(shí)間變化的一個(gè)因子。最后調(diào)用函數(shù)并且將本次的時(shí)間保存下來(lái)。這個(gè)現(xiàn)象的出現(xiàn)主要是因?yàn)樵诤瘮?shù)中將和寫死了,所以一個(gè)最簡(jiǎn)單的方法就是在中傳入?yún)?shù)然后在調(diào)用的時(shí)候傳入捕獲時(shí)位置的參數(shù)最后在開(kāi)始游戲的時(shí)候?qū)⒎旁谧钪虚g即可大功告成
先上效果圖 開(kāi)始之前的準(zhǔn)備
game.html
js/ 里面創(chuàng)建game.js
images/ 里面放三張圖片,一張背景圖片(background.png),一張英雄圖片(hero.png),一張怪物的圖片(monster.png)
在game.html里面寫上以下幾行簡(jiǎn)單的HTML代碼:
Simple Canvas Game
我們?cè)?b>game.html引入了game.js文件,沒(méi)錯(cuò),剩下的所有工作都是在操作game.js,為其添加游戲的js代碼。
創(chuàng)建畫布在game.js 里面,我們首先需要為游戲的舞臺(tái)創(chuàng)建一張畫布(canvas):
var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); canvas.width = 512; canvas.height = 480; document.body.appendChild(canvas);
這里通過(guò)js來(lái)創(chuàng)建了一個(gè)元素并設(shè)置canvas的寬和高,最后將其添加到
標(biāo)簽后。var ctx = canvas.getContext("2d");中的ctx變量是我們后面會(huì)用到的,具體的canvas用法查看這里的鏈接:https://developer.mozilla.org/en/canvas_tutorial
準(zhǔn)備圖片游戲需要加載我們之前存放在images文件夾下面的三張圖片:
// 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";
以上三張圖片都是通過(guò)創(chuàng)建簡(jiǎn)單的圖片對(duì)象來(lái)實(shí)現(xiàn)加載的,類似bgReady的三個(gè)變量用來(lái)標(biāo)識(shí)圖片是否已經(jīng)加載完成,如果如果在圖片加載未完成情況下進(jìn)行繪制是會(huì)報(bào)錯(cuò)的。如果你不太確定new Image()到底是個(gè)什么東西,你可以在bgImage.src = "images/background.png";之后使用console.log(bgImage);來(lái)查看,你看到的將是類似:
游戲?qū)ο?/b>
我們需要定義一些對(duì)象,以便我們?cè)诤竺鏁?huì)用到:
var hero = { speed: 256 // movement in pixels per second }; var monster = {}; var monstersCaught = 0;
既然是英雄抓獲怪物,我們得要有一個(gè)英雄和怪物的對(duì)象。而英雄有一個(gè)speed屬性用來(lái)控制他每秒移動(dòng)多少像素。怪物游戲過(guò)程中不會(huì)移動(dòng),所以暫時(shí)不用設(shè)置屬性。monstersCaught則用來(lái)存儲(chǔ)怪物被捉住的次數(shù),初始值當(dāng)然為0了。
處理用戶的輸入游戲是給人玩的,那么我們?cè)趺粗烙脩舻降自谶@個(gè)過(guò)程中干了什么?按了鍵盤?點(diǎn)了鼠標(biāo)?這些都是用戶在玩游戲的時(shí)候的輸入,所以我們一旦捕獲到這些輸入,我們就可以根據(jù)游戲的邏輯對(duì)用戶的輸入進(jìn)行處理了:
// Handle keyboard controls var keysDown = {}; addEventListener("keydown", function (e) { keysDown[e.keyCode] = true; }, false); addEventListener("keyup", function (e) { delete keysDown[e.keyCode]; }, false);
這里我們只是監(jiān)聽(tīng)兩個(gè)用戶的輸入:
keydown
keyup
然后我們將用戶的輸入先保存起來(lái),并沒(méi)有立即響應(yīng)。為此,我們用keysDown這個(gè)對(duì)象來(lái)保存用戶按下的鍵值(keyCode),如果按下的鍵值在這個(gè)對(duì)象里,那么我們就做相應(yīng)處理。
開(kāi)始一輪游戲在前端開(kāi)發(fā)中,一般是用戶觸發(fā)了點(diǎn)擊事件然后才去執(zhí)行動(dòng)畫或發(fā)起異步請(qǐng)求之類的
游戲在結(jié)束的時(shí)候,我們需要開(kāi)始新的一輪游戲,所以在game.js添加reset函數(shù)
// 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()函數(shù)用于開(kāi)始新一輪和游戲,在這個(gè)方法里我們將英雄放回畫布中心同時(shí)將怪物放到一個(gè)隨機(jī)的地方。
更新對(duì)象在游戲的過(guò)程中,不管是用戶在玩(有正確輸入的狀態(tài))還是游戲結(jié)束,我們都是需要及時(shí)更新游戲的對(duì)象:
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函數(shù)負(fù)責(zé)更新游戲的各個(gè)對(duì)象,會(huì)被規(guī)律地重復(fù)調(diào)用。首先它負(fù)責(zé)檢查用戶當(dāng)前按住的是中方向鍵,然后將英雄往相應(yīng)方向移動(dòng)。
有點(diǎn)費(fèi)腦力的或許是這個(gè)傳入的modifier 變量。你可以后面將要實(shí)現(xiàn)的main 方法里看到它的來(lái)源,但這里還是有必要詳細(xì)解釋一下。它是基于1開(kāi)始且隨時(shí)間變化的一個(gè)因子。例如1秒過(guò)去了,它的值就是1,英雄的速度將會(huì)乘以1,也就是每秒移動(dòng)256像素;如果半秒鐘則它的值為0.5,英雄的速度就乘以0.5也就是說(shuō)這半秒內(nèi)英雄以正常速度一半的速度移動(dòng)。理論上說(shuō)因?yàn)檫@個(gè)update函數(shù)被調(diào)用的非常快且頻繁,所以modifier的值會(huì)很小,但有了這一因子后,不管我們的代碼跑得快慢,都能夠保證英雄的移動(dòng)速度是恒定的。
這里需要說(shuō)明一下下面的判斷怪物和英雄是什么根據(jù):
if ( hero.x <= (monster.x + 31) && monster.x <= (hero.x + 31) && hero.y <= (monster.y + 32) && monster.y <= (hero.y + 32) )
上面的31,32是由hero和monster圖片的大小決定的,我們的hero圖片是32x32,monster圖片是30x32,所以根據(jù)坐標(biāo)的位于圖片中心的法制,就可以得到上面的判斷條件。
現(xiàn)在英雄的移動(dòng)已經(jīng)是基于用戶的輸入(按下上,下,左,右鍵)了,接下來(lái)該檢查移動(dòng)過(guò)程中所觸發(fā)的事件了,也就是英雄與怪物相遇。這就是本游戲的勝利點(diǎn),monstersCaught +1然后重新開(kāi)始新一輪。
渲染物體之前寫的代碼都是在準(zhǔn)備前期工作和處理一些游戲的狀態(tài)等,下面將進(jìn)入正題:我們需要將所有的東西畫出來(lái)
// 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就是最前面我們創(chuàng)建的變量。然后利用canvas的drawImage()首先當(dāng)然是把背景圖畫出來(lái)。然后如法炮制將英雄和怪物也畫出來(lái)。這個(gè)過(guò)程中的順序是有講究的,因?yàn)楹螽嫷奈矬w會(huì)覆蓋之前的物體。
這之后我們改變了一下Canvas的繪圖上下文的樣式并調(diào)用fillText來(lái)繪制文字,也就是記分板那一部分。本游戲沒(méi)有其他復(fù)雜的動(dòng)畫效果和打斗場(chǎng)面,繪制部分大功告成
主循環(huán)函數(shù)我們實(shí)現(xiàn)了將畫面畫出來(lái)以后,我們緊接著需要實(shí)現(xiàn)的就是游戲的循環(huán)結(jié)構(gòu),于是將它放在main函數(shù)里:
// 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); };
上面的主函數(shù)控制了整個(gè)游戲的流程。先是拿到當(dāng)前的時(shí)間用來(lái)計(jì)算時(shí)間差(距離上次主函數(shù)被調(diào)用時(shí)過(guò)了多少毫秒)。得到modifier后除以1000(也就是1秒中的毫秒數(shù))再傳入update函數(shù)。最后調(diào)用render 函數(shù)并且將本次的時(shí)間保存下來(lái)。
設(shè)置requestAnimationFrame()在上面的main函數(shù)中,我們通過(guò)requestAnimationFrame()調(diào)用了main函數(shù),所以我們需要聲明:
var w = window; requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;
這里這么多的||,不為別的,就是考慮到瀏覽器兼容問(wèn)題而已。
最后啟動(dòng)游戲萬(wàn)事具備,只欠東風(fēng)。到此,所有的游戲代碼基本就寫完了,我們現(xiàn)在需要做的就是調(diào)用相應(yīng)的函數(shù)來(lái)啟動(dòng)游戲:
// Let"s play this game! var then = Date.now(); reset(); main();
到這里代碼就寫完了。先是設(shè)置一個(gè)初始的時(shí)間變量then用于首先運(yùn)行main函數(shù)使用。然后調(diào)用 reset 函數(shù)來(lái)開(kāi)始新一輪游戲(如果你還記得的話,這個(gè)函數(shù)的作用是將英雄放到畫面中間同時(shí)將怪物放到隨機(jī)的地方以方便英雄去捉它)
用瀏覽器打開(kāi)game.html,開(kāi)始玩游戲吧!
進(jìn)一步思考在玩游戲的過(guò)程中,你會(huì)發(fā)現(xiàn)每一次hero捕獲到monster,hero就回到了canvas畫布的正中間。那么現(xiàn)在需要做的就是,將hero在捕捉到monster的時(shí)候讓hero就停留在捕獲的位置,不再是回到canvas正中間。
這個(gè)現(xiàn)象的出現(xiàn)主要是因?yàn)樵?b>reset函數(shù)中將hero.x和hero.y寫死了,所以一個(gè)最簡(jiǎn)單的方法就是在reset中傳入?yún)?shù):
var reset = function (x,y) { hero.x = x; hero.y = x; };
然后在update調(diào)用reset的時(shí)候傳入捕獲時(shí)位置的參數(shù):
var update = function (modifier) { //...other codes ++monstersCaught; reset(heor.x,hero.y); };
最后在開(kāi)始游戲的時(shí)候?qū)?b>hero放在canvas最中間即可:
var then = Date.now(); reset(canvas.width / 2,canvas.height / 2); main();
大功告成!
Hapyy Hacking
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/85822.html
摘要:推薦一些好用的游戲引擎開(kāi)發(fā)庫(kù)引言如果你是一個(gè)游戲開(kāi)發(fā)者,并且正在尋找一個(gè)可以與和無(wú)縫工作的游戲引擎。是另一個(gè)容易使用,適用于移動(dòng)設(shè)備和桌面的游戲引擎。是一個(gè)開(kāi)源的用來(lái)創(chuàng)建使用高級(jí)技術(shù)和服務(wù)的游戲引擎。用于建立游戲和繪圖引擎。 推薦一些好用的 HTML5 & JavaScript 游戲引擎開(kāi)發(fā)庫(kù) 0. 引言 如果你是一個(gè)游戲開(kāi)發(fā)者,并且正在尋找一個(gè)可以與 JavaScript 和 HT...
摘要:推薦一些好用的游戲引擎開(kāi)發(fā)庫(kù)引言如果你是一個(gè)游戲開(kāi)發(fā)者,并且正在尋找一個(gè)可以與和無(wú)縫工作的游戲引擎。是另一個(gè)容易使用,適用于移動(dòng)設(shè)備和桌面的游戲引擎。是一個(gè)開(kāi)源的用來(lái)創(chuàng)建使用高級(jí)技術(shù)和服務(wù)的游戲引擎。用于建立游戲和繪圖引擎。 推薦一些好用的 HTML5 & JavaScript 游戲引擎開(kāi)發(fā)庫(kù) 0. 引言 如果你是一個(gè)游戲開(kāi)發(fā)者,并且正在尋找一個(gè)可以與 JavaScript 和 HT...
閱讀 1634·2023-04-26 02:11
閱讀 2979·2023-04-25 16:18
閱讀 3711·2021-09-06 15:00
閱讀 2630·2019-08-30 15:55
閱讀 1934·2019-08-30 13:20
閱讀 2051·2019-08-26 18:36
閱讀 3121·2019-08-26 11:40
閱讀 2538·2019-08-26 10:11