在這章,開始對Painter遊戲的源代碼進一步的整理組織。這很是有必要,由於源代碼裏面有不少行代碼。在上章裏,咱們把變量組合到了對象裏面。這章裏,會使用到更多的對象而且把代碼分離到不一樣的文件裏面去。javascript
你發現你的javascript文件已經有點大了。一個javascript中包含你全部的代碼並不明智,由於很難從其中找到咱們想要的代碼。把文件分離開來頗有意義。一個好的方式就是把不一樣的javascript文件根據每一個javascript文件含有的對象分離開來。Painter3程序包含了以前章節裏的對象,而且每一個對象都寫入了一個獨立的javascript文件。如今找到指定代碼和理解程序結構就變的容易多了。你甚至能夠把文件放入不一樣的文件夾,代表它們屬於哪一個對象。好比能夠把Keyboard和Mouse文件放進一個input文件夾裏。這樣,咱們能清楚的知道這些文件都是處理用戶輸入用的。java
加載這些分離的文件到瀏覽器中有點麻煩。以前是這樣加載javascript文件的:web
<script src="FlyingSpriteWithSound.js"></script>
你也許想的是像下面這樣加載多個javascript文件:編程
<script src="input/Keyboard.js"></script> <script src="input/Mouse.js"></script> <script src="Canvas2D.js"></script> <script src="system/Keys.js"></script> <script src="Painter.js"></script> <script src="Cannon.js"></script>
不幸的是,這樣行不通。由於javascript文件從服務器那裏獲取,因此不肯定哪個javascript首先加載。假設第一個被加載的文件是Painter.js,瀏覽器不能解釋其中的代碼由於其中的相關代碼跟另外一個文件中的代碼有關。因此,爲了加載好文件,你須要知道它們之間的依賴關係和順序關係。換句話說,若是A文件須要B文件,那麼先須要加載B文件。canvas
在javascript中,能夠修改HTML頁面。所以理論上,你能夠在HTML頁面添加額外的腳本元素,用來開始加載另外一個JavaScript文件。經過巧妙的使用事件處理,能夠想象寫個JavaScript代碼來加載預先定義好的JavaScript文件。你能夠不用本身幹這事,由於有人已經寫好了。瀏覽器
這本書裏面,我選擇使用一個叫作LABjs的動態腳本加載工具。這是一個能讓你動態的加載預先定義的JavaScript文件的簡單腳本。下面是一個使用LABjs加載你JavaScript 文件的例子:服務器
<script src="../LAB.min.js"></script> <script> $LAB.script('input/Keyboard.js').wait() .script('input/Mouse.js').wait() .script('Canvas2D.js').wait() .script('system/Keys.js').wait() .script('Painter.js').wait() .script('Cannon.js').wait(function () { Game.start('mycanvas'); }); </script>
能夠看出,使用LABjs很簡單。只需簡單的調用一系列JavaScript文件和wait方法。最後一個wait方法傳遞了一個函數做爲參數。在這個函數裏,啓動遊戲。經過改變這些腳本文件的調用順序,就能夠改變JavaScript文件的加載順序。當你開發遊戲或者更大的JavaScript應用程序時,使用這個腳本很是有用,由於可讓開發和維護代碼變得容易。Painter3例子能夠看到一個完整的加載不一樣的JavaScript文件的代碼。markdown
也許你不想在最終的遊戲裏面使用上述方法,由於瀏覽器須要加載不少的JavaScript文件。那麼可使用另一個程序把全部的JavaScript文件都放在一個更大的JavaScript文件裏面,這樣加載更快。此外,更常見的作法是對儘量小的JavaScript文件的代碼結構作優化。這個過程叫作壓縮。第30章會詳細的說明這一切。網絡
先前,我說明了瀏覽器會無序的加載JavaScript文件。這個規則一樣適用於加載遊戲資源好比精靈和音效。下面的方法是你截止目前看到的加載遊戲資源的方法:ide
var sprite = new Image(); sprite.src = "someImageFile.png"; var anotherSprite = new Image(); anotherSprite.src = "anotherImageFile.png"; // and so on
看起來很簡單,對每個要加載的精靈建立一個Image對象而且賦值src變量。給src賦值並不意味着圖片立刻就會被加載。它只是簡單的告訴瀏覽器從服務器開始獲取這些圖片。這些都跟網絡速度有關,有可能須要會兒時間。若是你想立刻畫出圖片,JavaScript會報錯(還沒加載完就開始畫圖)。爲了不這種狀況,這是以前加載精靈的例子:
sprites.background = new Image(); sprites.background.src = spriteFolder + "spr_background.jpg"; sprites.cannon_barrel = new Image(); sprites.cannon_barrel.src = spriteFolder + "spr_cannon_barrel.png"; sprites.cannon_red = new Image(); sprites.cannon_red.src = spriteFolder + "spr_cannon_red.png"; sprites.cannon_green = new Image(); sprites.cannon_green.src = spriteFolder + "spr_cannon_green.png"; sprites.cannon_blue = new Image(); sprites.cannon_blue.src = spriteFolder + "spr_cannon_blue.png"; cannon.initialize(); window.setTimeout(Game.mainLoop, 500);
注意最後一行代碼。在賦值完全部Imgae對象的src變量後,你告訴瀏覽器在500毫秒後執行遊戲循環。這樣,瀏覽器就有足夠的時間加載圖片。可是,若是網絡鏈接很慢的話怎麼辦呢?那麼500毫秒根本不夠。若是網絡速度很快呢?則浪費了玩家不少的等待時間。爲了解決這個問題,你須要一個程序在執行主循環以前加載完全部的圖片。可使用事件處理函數。可是在那以前,須要稍微討論下關於方法和函數。
(省略)
爲了讓加載精靈更簡單,給Game對象增長了一個loadSprite方法:
Game.loadSprite = function(imageName) { var image = new Image(); image.src = imageName; return image; }
如今加載不一樣精靈的代碼就變得簡便短小了:
var sprFolder = "../../assets/Painter/sprites/"; sprites.background = Game.loadSprite(sprFolder + "spr_background.jpg"); sprites.cannon_barrel = Game.loadSprite(sprFolder + "spr_cannon_barrel.png"); sprites.cannon_red = Game.loadSprite(sprFolder + "spr_cannon_red.png"); sprites.cannon_green = Game.loadSprite(sprFolder + "spr_cannon_green.png"); sprites.cannon_blue = Game.loadSprite(sprFolder + "spr_cannon_blue.png");
然而,加載精靈的時間問題仍然沒有解決。爲了解決這個問題,首先要作的就是記錄加載了多少精靈。能夠在Game對象中增長一個變量,叫作spritesStillLoading:
var Game = { spritesStillLoading : 0 };
開始spritesStillLoading的值爲0。每次你加載一個精靈,這個值加一。以下所示:
Game.loadSprite = function(imageName) { var image = new Image(); image.src = imageName; Game.spritesStillLoading += 1; return image; }
每次加載完後spritesStillLoading要遞減。這能夠經過一個事件處理來實現,以下所示:
Game.loadSprite = function (imageName) { var image = new Image(); image.src = imageName; Game.spritesStillLoading += 1; image.onload = function () { Game.spritesStillLoading -= 1; }; return image; };
如今能夠經過spritesStillLoading的值來決定是否開始遊戲。當spritesStillLoading是0能夠進行遊戲主循環。爲了作到這些,建立兩個循環:一個資源加載的循環和一個遊戲主循環。在資源加載循環裏面,檢測精靈是否被加載。若是全部的精靈被加載後,在調用遊戲主循環。下面資源加載的循環:
Game.assetLoadingLoop = function () { if (Game.spritesStillLoading > 0) window.setTimeout(Game.assetLoadingLoop, 1000 / 60); else { Game.initialize(); Game.mainLoop(); } };
截止目前爲止,都是用window.setTimeout方法來建立一個遊戲循環。雖然代碼有用,可是它不是最好的。許多瀏覽器都提供了一個交互的進行繪畫的方法,好比遊戲。問題是否是全部的瀏覽器或者瀏覽器版本都使用相同的方法名字。比較新的版本都是用window.requestAnimationFrame方法。然而比較老點的版本使用window.mozRequestAnimationFrame或者 window.webkitRequestAnimationFrame。如今咱們能夠像下面這樣書寫來處理遊戲循環:
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); };
使用||操做符來決定使用哪一個方法名字。若是沒有有效的遊戲循環方法,那麼就調用 window.setTimeout方法。在javascript裏面,window變量是一個所有命名空間的容器。這意味着:
window.requestAnimationFrame(callbackFunction);
等於
requestAnimationFrame(callbackFunction);
painter3例子就是用最優化的遊戲循環方法來運行遊戲資源加載循環和遊戲主循環,以下:
Game.assetLoadingLoop = function () { if (Game.spritesStillLoading > 0) window.requestAnimationFrame(Game.assetLoadingLoop); else { Game.initialize(); Game.mainLoop(); } };
與之類似的是遊戲主循環:
Game.mainLoop = function () { Game.handleInput(); Game.update(); Game.draw(); Mouse.reset(); window.requestAnimationFrame(Game.mainLoop); };
瀏覽器之間的差別性對javascript開發者來講是一個至關的挑戰,即便大部分已經朝標準化發展了。當你開發javascript遊戲時,你可能會趕上不一樣瀏覽器之間的兼容性。所以,在你發佈遊戲以前最好在常見的一些瀏覽器上進行測試。
以前,你沒有區分那些能夠用於不一樣遊戲和特定遊戲之間的代碼。你寫的某部分代碼,好比 Game.mainLoop方法,一樣適用於其餘遊戲。加載精靈的代碼也是如此。你已經知道了如何分離不一樣的代碼到不一樣的文件。經過分離出通用代碼,那麼在以後就很容易進行的代碼的複用。若是你想複用精靈加載的代碼,你只需在新的遊戲裏簡單的包含源文件.另一個緣由是能讓你之後更快的建立類似的遊戲。經過這種方法,你能夠快速的建立出你的新遊戲而不是從新造輪子。
Painter4例子建立了一個Game.js文件,其中包含Game對象和一些與之相關的方法。與Painter遊戲相關的代碼放進了Painter.js文件。此外,還有一個PainterGameWorld.js文件處理遊戲中不一樣的對象。在以前的painter版本里,遊戲世界只有一個背景圖和一個大炮。在下節裏,會添加進一個球。painter的遊戲世界經過一個對象進行定義確保全部的遊戲對象更新和繪畫。下面是painterGameWorld對象的一部分:
var painterGameWorld = { }; painterGameWorld.handleInput = function (delta) { ball.handleInput(delta); cannon.handleInput(delta); }; painterGameWorld.update = function (delta) { ball.update(delta); cannon.update(delta); }; painterGameWorld.draw = function () { Canvas2D.drawImage(sprites.background, { x : 0, y : 0 }, 0, { x : 0, y : 0 }); ball.draw(); cannon.draw(); };
當你初始化遊戲,就會初始化遊戲對象,而且告訴Game對象painterGameWorld正在支配着遊戲世界:
Game.initialize = function () { cannon.initialize(); ball.initialize(); Game.gameWorld = painterGameWorld; };
在Game.mainLoop方法中,如今只需肯定調用gameWorld變量中正確的方法:
Game.mainLoop = function () { Game.gameWorld.handleInput(); Game.gameWorld.update(); Canvas2D.clear(); Game.gameWorld.draw(); Mouse.reset(); requestAnimationFrame(Game.mainLoop); };
結果就是,很是好的分離的通用遊戲代碼(Game.js)和特定的遊戲代碼,其中包含了加載精靈和初始化遊戲,更新和繪畫Painter遊戲中的對象。其餘的與painter有關的遊戲對象也分別放到了獨立的腳本文件中(好比Cannon.js)。
在以前的章節裏,你已經知道了如何經過分離的javascript文件讓你的javascript遊戲應用更加靈活和有效,從特定的遊戲代碼中分離出通用代碼,正確的加載遊戲資源,建立更有效的循環。這節裏,添加一個能夠射擊的球來擴展painter遊戲。所以須要添加一個ball對象。
設置ball對象和cannon對象多少有些類似。在Painter4例子中,能夠看到添加了一個球的遊戲(圖7-1)。能夠經過點擊屏幕的任何地方來從大炮中射出小球。此外,球的顏色和大炮的顏色同樣。能夠在Ball.js文件中找到描述ball對象的代碼。就像cannon對象同樣,ball包含了一些變量,好比位置,當前顏色和最初的球的樣子。由於球要移動,因此還要儲存速度。速度是一個定義球隨着時間改變位置的矢量。好比,若是球的速度爲(0,1),那麼每一秒,球的Y座標都會增長1(意味着球在下落)。最終,球有兩個狀態:不是在空中飛行就是等待被射出。由於,爲ball對象添加一個額外的布爾變量叫作shooting。下面是ball的完整定義:
var ball = { }; ball.initialize = function() { ball.position = { x : 65, y : 390 }; ball.velocity = { x : 0, y : 0 }; ball.origin = { x : 0, y : 0 }; ball.currentColor = sprites.ball_red; ball.shooting = false; };
(省略圖7-1)
本書中開發的遊戲,大多對象都有位置和速度。由於本書只關注2D遊戲,位置和速度都只有x和y變量。當更新這些遊戲對象,須要基於速度矢量和通過的時間計算出新的位置。以後的章節裏,你就知道怎麼作這些了。
爲了可以使用ball對象,須要一些新的精靈。在Game.loadAssets方法裏,加載紅色球,綠色球,藍色球。根據大炮的顏色,改變球的顏色。下面是擴展後的loadAssets方法:
Game.loadAssets = function () { var loadSprite = function (sprite) { return Game.loadSprite("../../assets/Painter/sprites/" + sprite); }; sprites.background = loadSprite("spr_background.jpg"); sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); sprites.cannon_red = loadSprite("spr_cannon_red.png"); sprites.cannon_green = loadSprite("spr_cannon_green.png"); sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); sprites.ball_red = loadSprite("spr_ball_red.png"); sprites.ball_green = loadSprite("spr_ball_green.png"); sprites.ball_blue = loadSprite("spr_ball_blue.png"); };
上面能夠看出如何讓精靈加載在javascript中顯得更加易讀。聲明瞭一個本地變量loadSprite來表明一個函數。這個函數須要一個精靈名字的參數,最後,函數返回Game.loadSprite方法後的結果。
在ball對象的initialize方法中,首先進行變量的賦值。在遊戲開始時,球不會移動。所以,球的速度爲0.固然球的初始位置也爲0.換句話說,球藏在大炮的後面,所以當球不移動時你看不見它。初始化球爲紅色的球,設置shooting變量爲False,以下:
ball.initialize = function() { ball.position = { x : 0, y : 0 }; ball.velocity = { x : 0, y : 0 }; ball.origin = { x : 0, y : 0 }; ball.currentColor = sprites.ball_red; ball.shooting = false; };
緊接着初始化,須要添加一個reset方法來複位球的位置和shooting值:
ball.reset = function () { ball.position = { x : 0, y : 0 }; ball.shooting = false; };
當球飛出屏幕以外,就能夠調用reset方法。此外,添加一個draw方法。若是球沒有被射出,玩家就看不見它。也就是說,只在球狀態爲射出時才繪畫球:
ball.draw = function () { if (!ball.shooting) return; Canvas2D.drawImage(ball.currentColor, ball.position, ball.rotation, ball.origin); };
注意遊戲對象的繪畫順序:首先是背景圖,而後是球,而後纔是大炮。
玩家能夠點擊鼠標左鍵來射出球。球的速度和它移動的方向是由玩家鼠標點擊的位置決定的。玩家點擊的位置距離大炮越遠,球就有更快的速度。對玩家來講控制球速度的直覺就是這樣。當你設計遊戲時,仔細思考能夠從玩家那裏接收怎樣的指令而且什麼是最天然和有效的處理方式。
爲了處理輸入,爲ball對象添加handleInput方法,檢測用戶是否點擊了鼠標左鍵:
if (Mouse.leftPressed) // do something...
然而,由於在任什麼時候刻空中只能有一個球,你須要檢測在空中是否有球。也就是說須要檢測球的射出狀態,若是球被射出了,那麼就再也不處理鼠標點擊事件。所以,須要添加額外的判斷條件:
if (Mouse.leftPressed && !ball.shooting) // do something...
在If語句裏,須要知道玩家點擊了哪裏和球已經射了出來。首先須要給shooting變量一個正確的值,由於球的狀態須要改變:
ball.shooting = true;
由於球在移動了,那麼就須要給它一個速度。速度就是一個玩家點擊位置的矢量。能夠經過鼠標點擊位置減去球的位置來計算出這個值。由於速度有x和y變量,因此二者都要進行運算:
ball.velocity.x = (Mouse.position.x - ball.position.x); ball.velocity.y = (Mouse.position.y - ball.position.y);
這個速度的計算方式有效考慮到了用戶點擊位置距離大炮更遠,速度越快。然而,若是你如今玩這個遊戲的話,你會發現球移動的很慢。所以,須要讓速度乘以一個常量:
ball.velocity.x = (Mouse.position.x - ball.position.x) * 1.2; ball.velocity.y = (Mouse.position.y - ball.position.y) * 1.2;
在進行了不一樣的測試後,選擇了常量1.2。每一個遊戲都有一些遊戲參數須要你在不斷的測試中選出一個合適值。爲這些參數選出一個正確的值是相當重要的,由於爲了平衡遊戲,而且還要保證這些參數不會讓遊戲太難或者太簡單。好比若是用0.3代替1.2,球會移動的更慢。這會讓遊戲變的更難,甚至讓遊戲變得不可玩。
若是你向ball對象添加了handleInput方法,它不會被自動調用。須要在painterGameWorld對象裏進行明確說明。所以,像下面這樣書寫代碼:
painterGameWorld.handleInput = function () { ball.handleInput(); cannon.handleInput(); };
把各類變量和方法組合到各個對象的一個巨大好處就是讓每一個對象看起來清楚和更小。你能夠自行設計遊戲中須要用到的對象。
在ball.update中,球的不一樣行爲是由球的當前狀態決定的。以下:
ball.update = function (delta) { if (ball.shooting) { ball.velocity.x = ball.velocity.x * 0.99; ball.velocity.y = ball.velocity.y + 6; ball.position.x = ball.position.x + ball.velocity.x * delta; ball.position.y = ball.position.y + ball.velocity.y * delta; } else { if (cannon.currentColor === sprites.cannon_red) ball.currentColor = sprites.ball_red; else if (cannon.currentColor === sprites.cannon_green) ball.currentColor = sprites.ball_green; else ball.currentColor = sprites.ball_blue; ball.position = cannon.ballPosition(); ball.position.x = ball.position.x - ball.currentColor.width / 2; ball.position.y = ball.position.y - ball.currentColor.height / 2; } if (painterGameWorld.isOutsideWorld(ball.position)) ball.reset(); };
上面的方法有個參數delta。這個參數是用來計算球的新的位置,你須要知道自從上次調用update後通過了多少時間。這個參數也對某些對象的handInput方法有用——好比,想知道鼠標移動的速度,就須要知道通過的時間。Painter4例子擴展每一個對象都有遊戲循環方法(handleInput, update, draw),把距離上一次更新的時間當作參數。
可是在哪裏計算delta值呢?怎麼計算它?在下面的例子中,在Game.mainLoop方法中進行計算:
Game.mainLoop = function () { var delta = 1 / 60; Game.gameWorld.handleInput(delta); Game.gameWorld.update(delta); Canvas2D.clear(); Game.gameWorld.draw(); Mouse.reset(); requestAnimationFrame(Game.mainLoop); };
由於你想遊戲循環每秒執行60次,因此像下面這樣計算delta的值:
var delta = 1 / 60;
這種計算遊戲循環中通過的時間被叫作固定步長。若是你有一個很慢的電腦不可以一秒鐘執行60次,你仍然須要讓你的遊戲對象知道自從上次時間後經歷了60分之一秒,即便那不是真的。所以,遊戲時間不一樣於真實的時間。另一個方式是經過獲取系統時間來計算真正通過的時間。以下:
var d = new Date(); var n = d.getTime();
變量n含有自1970年來的毫秒數,每一次你運行遊戲循環,就會獲得一次新的時間值,也就是你能獲得經歷的真實時間。這不是固定的時間步長,由於通過的時間跟電腦的速度有關,優先級跟系統有關,此時系統也在處理着其它任務。所以,這種處理遊戲中時間的方法叫作可變的時間步長。
可變的時間步長在高幀率要求的遊戲中特別有用,好比,第一人稱射擊遊戲,鏡頭須要高速的移動。可變時間步長的缺點是當玩家暫時作些其它事情時(好比打開遊戲菜單或者保存遊戲)時間也在繼續。一般玩家不會高興在它們保存遊戲以後,角色就已經掛掉。所以做爲一個遊戲開發者,使用可變的時間步長時須要注意處理這些問題。
另一個使用可變時間步長的例子是與遊戲可玩性的交互。這常常發生,尤爲是開發瀏覽器中的遊戲時。這也是爲何在本書中使用一個固定的時間步長的緣由。當用戶轉換到另外一個選項卡時,沉默的選項卡的程序會一直中止執行直到用戶返回。當使用固定時間步長,當遊戲暫停,用戶從新激活遊戲時,遊戲繼續運行,由於遊戲對象不關心通過的真實時間,只在意固定的delta值。
當前球的位置經過它的速度進行更新:
ball.position.x = ball.position.x + ball.velocity.x * delta; ball.position.y = ball.position.y + ball.velocity.y * delta;
計算球新的位置是基於速度和通過的時間。乘以速度的每一個維度,結果加上當前球的位置。如此的話,即便用高幀率或低幀率,遊戲對象的移動速度也不會改變。
若是球的狀態不爲射出,那麼就能夠改變球的顏色了。這種狀況下,經過獲取大炮的顏色來肯定球的顏色:
if (cannon.currentColor === sprites.cannon_red) ball.currentColor = sprites.ball_red; else if (cannon.currentColor === sprites.cannon_green) ball.currentColor = sprites.ball_green; else ball.currentColor = sprites.ball_blue;
更新球的位置:
ball.position = cannon.ballPosition(); ball.position.x = ball.position.x - ball.currentColor.width / 2; ball.position.y = ball.position.y - ball.currentColor.height / 2; ball.position = cannon.ballPosition(); ball.position.x = ball.position.x - ball.currentColor.width / 2; ball.position.y = ball.position.y - ball.currentColor.height / 2;
爲何改變位置?當球不在空中時,玩家能夠經過轉動大炮來改變球的射出位置。所以,須要根據當前大炮的位置計算出球的正確位置。所以,須要添加一個ballPosition方法到cannon中,此方法基於大炮位置計算球的位置。使用正弦和餘弦函數:
cannon.ballPosition = function() { var opp = Math.sin(cannon.rotation) * sprites.cannon_barrel.width * 0.6; var adj = Math.cos(cannon.rotation) * sprites.cannon_barrel.width * 0.6; return { x : cannon.position.x + adj, y : cannon.position.y + opp }; };
能夠看出,乘以臨邊和對邊的0.6,這樣球看起來比在大炮之上一半多。函數返回一個包含x和y的對象。
當獲取了理想的球的位置後,從其中減去球高和寬的一半。那樣,那樣,球就很是好的顯示在大炮的中間。
ball.update方法的第二部分也是一個if語句:
if (painterGameWorld.isOutsideWorld(ball.position)) ball.reset();
這部分處理當球移除遊戲世界後的狀況。爲了計算這種狀況,添加了isOutsideWorld這個方法。這個方法目的是檢測給定的位置是否超出了咱們規定的範圍。使用一些簡單的規則指定遊戲世界的邊界,記住屏幕的左上角是起點。
若是你看這個方法的話,發現只有一個參數,postion:
painterGameWorld.isOutsideWorld = function (position)
若是你想查看一個座標是否在屏幕以外,須要知道屏幕的寬和高。在一個HTML5遊戲中路Pianter遊戲,對應的就是畫布的尺寸。Painter4爲Game添加了一size變量。當Game.start方法被調用,屏幕的尺寸就會被傳遞給這個參數:
Game.start = function (canvasName, x, y) { Canvas2D.initialize(canvasName); Game.size = { x : x, y : y }; Keyboard.initialize(); Mouse.initialize(); Game.loadAssets(); Game.assetLoadingLoop(); };
在isOutsideWorld方法中,使用Game.size決定座標是否在遊戲世界以外。方法中只有一條return語句,返回一個布爾值。邏輯或運算包含了超出遊戲世界以外的不一樣狀況:
return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y;
如上所示,不介意Y座標是否小於0.這容許球越過屏幕之上而後再掉下來。
最後不要忘了在painterGameWorld.update方法中調用ball.update方法:
painterGameWorld.update = function (delta) { ball.update(delta); cannon.update(delta); };
當運行這個Painter4例子,會發現大炮能夠瞄準了,能夠選顏色了和射出炮彈了。下一章,會在遊戲中添加油漆罐。可是爲了達到這個目的,會引進一個新的JavaScript編程概念:原型。
這章裏,學到了: