Building JavaScript Games for Phones Tablets and Desktop(2)-遊戲編程基礎

這章覆蓋了遊戲編程的基本原理。首先,你會了解到遊戲的基本框架,就是一個遊戲世界和一個遊戲循環。你將會經過不一樣的例子學到如何用JavaScript建立遊戲框架,最終,我會討論如何經過註釋,佈局和恰到好處的空白來增長代碼可讀性。程序員

搭積木般建立遊戲

這部分討論瞭如何像搭積木同樣建立遊戲。我論述遊戲世界的同時經過一個更新-繪畫循環來向你演示這個過程,這個循環就是,更新遊戲世界,顯示遊戲世界。編程

遊戲世界

是什麼讓遊戲有了如此的娛樂性能讓你樂此不疲的探索這個虛構的世界,作你在現實世界中作不到的事情。你能夠騎在龍背上,摧毀整個太陽系統,或者創造一個有至關複雜文明的角色,角色說着虛構的語言。這個想象力就被叫作遊戲世界。遊戲世界能夠是一個小的區域好比俄羅斯方塊這個遊戲世界,也能夠複雜到像俠盜獵車手和魔獸世界那樣的虛構世界。canvas

當一個遊戲運行在電腦或者智能手機上,這個設備維持着這個遊戲世界的內部表徵。這個表徵不是你玩遊戲在屏幕上看到的那些東西。它包含了大量描述對象的信息,一個敵人能有多少生命值,一個角色上有多少物品等等。幸虧,程序知道如何把這些內部表徵顯示到屏幕上。不然,玩遊戲將會變得很是乏味,由於玩家不得不翻閱大量的數據查看他們是否拯救了女王或者已經死亡。windows

玩家永遠不會看到遊戲世界的內部表徵,可是遊戲開發者能夠。當你想開發遊戲,你須要考慮如何設計遊戲內部。你遊戲編程的樂趣之一就是你完成了這些內部設計。數組

另一個重要的事情是要意識到與現實世界同樣,這個遊戲世界隨時處於改變中。怪物移動到不一樣的地方,天氣發生改變,車會跑的沒油,敵人會掛掉等等。此外,玩家的操做會直接影響遊戲世界的改變!所以簡單的在計算機內存中儲存單一的遊戲世界內部表徵是不夠的。一個遊戲須要持續不斷的記錄玩家的所做所爲而且以此更新內部表徵。此外,須要把遊戲世界展示到玩家的眼前,經過電腦顯示器,電視,或者智能手機的屏幕。處理這些事情的過程稱爲遊戲循環。瀏覽器

遊戲循環

遊戲循環處理遊戲的動態方面。在遊戲運行過程當中有不少事情發生。玩家按下鍵盤或者點擊觸摸屏。一個不斷改變的遊戲世界包含了等級、怪物,和其它須要保持更新的角色。也有其它一些特殊效果,好比爆炸,聲音等等。這些不一樣的任務都須要經過遊戲循環處理,這些東西能夠分爲兩類:服務器

  • 跟遊戲世界更新和維護相關的
  • 跟顯示遊戲世界相關的

遊戲循環會不斷的執行這個任務,一個接一個(如圖2-1)。例如,讓咱們在吃豆人這個遊戲裏看如何處理角色的移動。吃豆人出如今迷宮中的某個地方而且朝一個固定的方向移動。第一個任務裏(更新和維護世界),檢測玩家是否按下了方向鍵。若是按下了,則須要更新吃豆人的位置。也許,由於吃豆人的移動,它吃掉了一個豆,這個豆代表獲得了分數。你須要檢測這個豆是不是吃豆人吃掉的最後一個豆子,由於吃完豆子代表已經經過了本關卡。最終,若是吃掉了一個大豆,則幽靈就禁止了。而後你須要更新剩下的遊戲世界。幽靈的位置須要更新,還要決定積分結果顯示在哪裏,須要檢測吃豆人是否與幽靈發生了碰撞等等。你會發現即便在像吃豆人這樣簡單的遊戲裏,許多工做都須要在第一個任務裏作。從如今開始,我把這些不一樣的跟更新和維護遊戲世界相關的任務叫作更新行爲。markdown

(省略圖2-1)app

與第二部分任務相關的就是向玩家展現這個遊戲世界。在吃豆人這個遊戲裏,意味着要繪畫出迷宮,幽靈,吃豆人和一些向玩家展現的重要信息,好比玩家獲得了多少分,還剩下幾條命等等。這些東西能夠被放在屏幕上不一樣的位置,好比頂部或底部。這部分也被叫作擡頭顯示部分(HUD)。現代3D遊戲有更復雜的繪畫任務。這些遊戲須要處理燈光和陰影,反光,相似爆炸的視覺效果等等。框架

使用JavaScript建立一個遊戲

前面的章節教會了如何建立一個簡單的JavaScript程序。在那個程序裏,指令被裝進一個函數裏,相似下面:

changeCanvasColor = function(){
    document.body.style.background = "blue";
}

這樣書寫程序考慮到的是JavaScript是過程式語言:指令由函數組合在一塊兒。第一步是用JavaScript寫一個簡單的遊戲循環。以下所示:

var canvas = undefined;
var canvasContext = undefined;

function start () {
    canvas = document.getElementById("myCanvas");
    canvasContext = canvas.getContext("2d");
    mainLoop();
}

document.addEventListener('DOMContentLoaded', start);

function update () {

}

function draw() {

}

function mainLoop () {
    canvasContext.fillStyle = "blue";
    canvasContext.fillRect(0, 0, canvas.width, canvas.height);
    update();
    draw();
    window.setTimeout(mainLoop, 1000 / 60);
}

上面的腳本中有多個函數。由於下面一條指令,當HTML加載時執行start函數:

document.addEventListener('DOMContentLoaded', start);

在start函數裏,你獲取畫布和畫布上下文;把這兩個東西儲存在變量裏以便在其它程序裏可使用。而後執行另一個函數mainLoop。這個函數裏有其它指令,其中兩條指令處理背景顏色。而後調用update函數,以後是draw函數。這兩個函數又含有其它指令。最後一條指令是下面這條:

document.addEventListener('DOMContentLoaded', start);

這句指令的意思在一個肯定的時間後(1000/60,大約16.6毫秒)再次執行mainLoop函數。當mainLoop函數被再次調用,背景色再次發生改變且update函數和draw函數也再次被調用。在這裏,update函數和draw函數都是空的,可是能夠向裏面添加東西用來更新和繪畫遊戲世界。須要注意的是在循環之間使用setTimeout函數並非最好的解決方案。有時這個方法的影響能超出你控制以外,好比在緩慢的電腦,在瀏覽器中打開的其餘東西,或者同時其餘一些須要處理器運行的應用在運行等等。當你須要處理敏感的時間操做(好比玩家須要5分鐘後復活),此時你不是再依靠setTimeout函數而是根據系統的事件調度或者在update函數中檢測是否發生這些事件來進行處理。

當你運行這個例子程序,update和draw函數被不斷的執行:更新,繪畫,更新,繪畫等。此外,這一切發生的很快。這個例子運行速度大概是60幀。這種循環稱做是固定的時間循環,它在一些小遊戲中是很是流行的。你也能夠設計不一樣的遊戲以不一樣的循環方式運行着而不是每秒60幀這樣。

這本書教會了你不少不一樣的方法來填充update和draw函數。在這個過程當中,我會介紹許多有用編程技術,對遊戲和其餘應用程序都有用。下面的部分將更細節的講解基礎遊戲應用。那時,你將會爲這個遊戲基本框架添加其餘的指令。

程序結構

這節細節的講程序結構。在早些時候,許多程序員使用文本而不是圖形編程。這種基於文本的應用叫作控制檯程序。除了在屏幕上輸出文字,它也能夠接收來自用戶的文本輸入。因此,全部與用戶的交互都在一個問答表列表裏(Do you want to format the hard drive (Y/N)? Are you sure(Y/N)? and so on))。在基於窗口的操做系統變得流行以前,基於文本的接口對應文本編輯程序,電子表格,數學應用甚至遊戲都是常見的。這些遊戲被叫作文字冒險遊戲,文字描述了這個遊戲世界。玩家經過輸入命令與遊戲世界交互。

用JavaScript編寫控制檯應用是能夠實現的。雖然看上去頗有趣,但我是仍是把注意力放在現代圖形遊戲上。

應用類型

控制檯應用只是應用程序類型的其中一種。其餘常見的類型是Windows應用。這類型應用在一個屏幕裏包含了窗口,按鈕和其餘用戶圖形接口(GUI)。這類型應用通常是事件驅動的,好比按下按鈕或者選擇一個菜單。

另一個應用類型是APP,運行在智能手機或平板電腦上。這類型的應用屏幕空間通常有限,可是有更多的互動。好比GPS能夠找到設備的地點,傳感器能夠知道設備的方向,還有觸摸屏幕。

當開發應用程序,讓其運行在不一樣的平臺上是一個很大的挑戰。建立windows應用和app是很是不一樣的。而且複用不一樣類型的應用代碼是很是難的。所以,基於網頁的應用變得愈來愈流行。在這種狀況下,應用被放在服務器端,用戶在瀏覽器中運行程序。這裏有許多這種例子:好比基於網頁的Email程序或者社交網站。這本書裏,學的就是基於網頁的應用。

函數

記得在一個過程式程序裏,指令作着這個程序裏實際的工做:它們一個接一個的執行。改變着內存或者屏幕顯示,這樣就注意到它們的存在。在BasicGame程序裏,不是全部的行都是指令。好比一個指令context.fillRect(0, 0, canvas.width,canvas.height)。

由於Java是過程式語言,指令能夠被放在函數裏。指令不是必須就要被放在函數裏的。好比下面的一條指令就不屬於函數:

var canvas = undefined;

然而,函數很是有用。它能夠避免代碼的零散化,由於指令只在一處地方出現,而且可讓程序員經過函數名輕鬆的進行調用。函數中的指令在兩個花括號之間。這些指令被叫作函數體。在函數體外,書寫函數頭部,好比下面這樣:

var canvas = undefined;

頭部包含了函數名。你能夠爲函數取任何名字。你能夠看到gameloop函數有兩個部分,draw和update。這兩部分也在函數裏面。在函數名以前須要function這個單詞。在函數名以後是一對花括號。

語法圖

若是你不知道編程語言規則那麼使用相似JavaScript這樣的語言編程是很是困難的。這本書會用被叫作語法圖的東西來闡述編程語言的組成結構。一個編程語言的語法參考正式的規則,這些規則決定這是否是一個有效的程序(換句話說,程序能讓編譯器或解釋器讀懂)。相比之下,程序的語義參考了它實際的意思。爲了區分語法和語義的區別,請看這句話:all your base are belong to us。在語法上來講,這句話有問題。可是語義上至關清楚。

解釋器能夠檢查程序的語法:違背語法的程序都會被解釋器報錯。不幸的是,解釋器不能檢查程序的語義是否爲程序員心中所想的那樣。因此程序語法的正確並不表明語義上的正確。若是語法都不正確的話,程序確定不能運行。語法圖幫你形象化編程語言的規則。好比,下圖就是一個關於JavaScript如何定義函數的語法圖(圖2-2)。

(省略圖2-2)

函數調用

(省略)

update與draw

gameloop裏面有update和draw函數。由於函數就是指令的集合,每當update函數被調用,函數中的指令就跟着被執行。draw函數也是如此。

假若有這樣一個例子,想象你設計個氣球跟隨鼠標移動的簡單遊戲。當你移動鼠標,氣球也跟着移動。在update和draw函數裏,你能夠這麼作。在update函數裏,你須要獲取鼠標當前位置並把它儲存起來。在draw函數裏,你須要把氣球顯示在剛纔儲存的位置上。固然,你不知道這些指令是否存在,而且你也不知道這些指令是什麼樣子。也許你會想這些指令爲何這樣運行。你沒有移動氣球,你只是簡單的把它畫在update裏面儲存的位置上。在一個很快的速度裏反覆調用update和draw函數。由於如此快的速度,在不一樣的位置畫出氣球感受看起來就像氣球在移動(實際上不是這樣)。這就是全部的遊戲的遊戲世界繪畫的方式和玩家怎樣被遊戲的世界吸引。本質上,就是以很快的速度的在不一樣的位置上進行畫圖。

程序佈局

這節處理程序源代碼的佈局。你首先會了解到如何爲你的代碼添加註釋。而後你會學到如何寫出清晰的代碼經過使用空格,縮進,單行或多行。

註釋

(省略)

指令 vs 多行

(省略)

空格和縮進

(省略)

你學到了什麼

在這章裏,你學到了:

  • 遊戲的框架是什麼,包含遊戲循環和遊戲循環對於遊戲世界的做用
  • 怎麼組織遊戲程序,包括用幾條指令獲取canvas,update和draw函數構成了遊戲循環
  • JavaScript程序的基本佈局規則,包括怎樣在代碼中添加註釋和什麼時候放入空格來增長代碼的易讀性
相關文章
相關標籤/搜索