日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

Building JavaScript Games for Phones Tablets and Desktop(3)-创造一个游戏世界

發(fā)布時間:2023/12/4 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Building JavaScript Games for Phones Tablets and Desktop(3)-创造一个游戏世界 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

創(chuàng)造一個游戲世界

這章教會你如何通過內(nèi)存中儲存的信息創(chuàng)造一個游戲世界。介紹了基本類型和變量并且這些變量是如何儲存和改變信息的。接下來,你會看到如何用對象儲存更復(fù)雜的信息,里面包含成員變量和方法。

基本類型和變量

先前的章節(jié)幾次討論到了內(nèi)存。你已經(jīng)看到了如何執(zhí)行一個簡單的指令譬如canvasContext.fillStyle = “blue”;為畫布設(shè)置一個顏色。在這章例子里,你使用內(nèi)存儲存臨時信息,是為了記住一些簡單運算的結(jié)果。在這個例子里,你使用經(jīng)過的時間來改變背景色。

類型

類型,或者數(shù)據(jù)類型,代表不同類型的信息。先前的例子使用了不同類型的信息,當(dāng)做參數(shù)傳遞給了函數(shù)。例如,fillRect函數(shù),需要4個整數(shù),start函數(shù)需要一個文本標(biāo)識引用自canvas,update和draw函數(shù)不需要任何信息。瀏覽器/解釋器可以區(qū)分這些不同類型的信息并且很多情況下可以把一個類型的信息轉(zhuǎn)換成另一個類型的信息。比如,在JavaScript里,你可以使用單引號或者雙引號來標(biāo)識文本。如下的兩句指令是一樣的:

canvas = document.getElementById("myCanvas"); canvas = document.getElementById('myCanvas');

瀏覽器可以自動的在不同類型的信息間進行轉(zhuǎn)換。比如,下面的語句不會導(dǎo)致語法錯誤:

canvas = document.getElementById(12);

這個當(dāng)做參數(shù)傳遞的整數(shù)會被轉(zhuǎn)換成文本。在這種情況下,當(dāng)然,這里沒有ID叫做12的canvas,所以這個程序是不正確的。如果你像下面一樣替換canvas的ID,程序就會正常運行:

<canvas id="12" width="800" height="480"></canvas>

瀏覽器自動的在文本和數(shù)字之間進行轉(zhuǎn)換。

現(xiàn)代編程語言都比JavaScript嚴(yán)格的多。比如Java和C#,不同類型之間轉(zhuǎn)換是有限制的。大多時候,你必須明確的告訴編譯器這里有一個類型轉(zhuǎn)換。

為什么對類型轉(zhuǎn)換需要這么嚴(yán)格的限制呢?首先,一個函數(shù)或方法中明確的類型參數(shù)可以讓程序員簡單的使用函數(shù)。比如下面這樣:

function playAudio (audioFileId)

簡單的看一下,你并不確定audioFileId是一個數(shù)字還是文本。在C#里,這個函數(shù)會類似這樣:

void playAudio(string audioFileId)

可以看出,不僅有參數(shù)名,而且還附有參數(shù)類型。類型是string,也就是字符串。此外,在函數(shù)名前面還有void這個單詞,表示這個函數(shù)沒有返回值。

聲明和變量賦值

在JavaScript里面儲存信息和使用信息都是很簡單的。你需要做的只是為你要引用的信息提供一個名字。這個名字就叫做變量。當(dāng)你想在程序里使用變量時,在使用之前聲明它是一個好習(xí)慣。下面是如何聲明一個變量:

var red;

在這里,red就是變量名。你可以在你隨后的程序用它儲存信息。

當(dāng)你聲明了一個變量,你不需要提供你需要儲存信息的類型。變量僅僅是內(nèi)存里有一個名字。有些編程語言要求在變量聲明時確定變量的類型。比如在Java或者C++里。然而,很多的腳本語言(包括JavaScript)允許你聲明一個變量而不需要指定其類型。當(dāng)編程語言聲明變量時不要指定類型,這個編程語言就有松散類型。在JavaScript里,你可以聲明不止一個變量,比如:

var red, green, fridge, grandMa, applePie;

這里你聲明了5個不同的變量。當(dāng)你聲明這些變量時,你沒有賦值。在這種情況下,變量是未定義的。你可以用賦值指令為變量賦值。比如,給red賦值:

red = 3;

賦值指令包含下面這些:

  • 變量名
  • ”=“符號
  • 變量值
  • 一個分號

你可以發(fā)現(xiàn)賦值指令中間有個等號。然而,在JavaScript里,最好把”=“看成”變?yōu)椤岸皇恰钡扔凇啊.吘?#xff0c;變量還沒有等于右邊的值,它在指令執(zhí)行之后才變成那個值。下面的語法圖描述了賦值指令。(圖3-1)

(省略圖3-1)

現(xiàn)在知道如何聲明一個變量和給變量賦值了。如果你在聲明變量時就知道給變量賦什么值,你可以聲明的同時進行賦值。比如:

var red = 3;

當(dāng)執(zhí)行這句后,內(nèi)存中就會有3這個值,如圖3-2所示。

(省略圖3-2)

這里有更多關(guān)于數(shù)值變量的聲明和賦值:

var age = 16; var numberOfBananas; numberOfBananas = 2; var a, b; a = 4; var c = 4, d = 15, e = -3; c = d; numberOfBananas = age + 12;

在第四行,你可以發(fā)現(xiàn)一次可以聲明多個變量。也可以像第六行一樣有多個變量進行了聲明賦值。在賦值的右邊,你可以放置其它變量或者數(shù)學(xué)表達式,正如最后兩行所示。指令 c = d;讓儲存在d中的值也儲存在了c里面。因為d是15,所以執(zhí)行這條指令后,c也是15.最后一條指令讓age加上12,把結(jié)果儲存在numberOfBananas里面。概括來說,執(zhí)行這些指令后,這些內(nèi)存值看起來就像圖3-3所示:

(省略圖3-3)

全局變量和嚴(yán)格模式

除了在使用變量之前進行聲明,javascript也可以直接使用變量而不用聲明它。比如下面這樣:

var a = 3; var b; b = 4; x = a + b;

如上所示,前兩行指令通過var關(guān)鍵字聲明了變量a和b。變量x從來沒有聲明,但是它用來儲存a和b的和。javascript允許這樣。這很糟糕,而且這也是它為什么糟糕的原因。問題在于沒有聲明的變量javascript解釋器會自動的把它進行聲明,而你卻完全不知道。如果你在其它地方使用同樣名字的變量,你的程序可能發(fā)生你意想不到的結(jié)果。另外,如果你使用了很多不同的變量,你最好同時記錄這些變量。但是最大的問題是下面這個:

var myDaughtersAge = 12; var myAge = 36; var ourAgeDifference = myAge - mydaughtersAge;

當(dāng)編寫這些指令時,你希望的是ourAgeDifference的值是24.但是,這個值是未定義的。這是因為第三行這里有個打字錯誤。變量的名字不是mydaughtersAge,而是myDaughtersAge。這種情況下,瀏覽器/解釋器是悄悄的聲明一個叫做mydaughtersAge的變量而不是拋出一個錯誤停止運行腳本。因為變量沒有定義,所有與此變量有關(guān)的計算都是未定義的。因此,變量ourAgeDifference也是未定義的。

這個問題真的很棘手。幸運的是,新的EMCAScript5標(biāo)準(zhǔn)有個叫做嚴(yán)格模式的東西。當(dāng)腳本用嚴(yán)格模式來解釋時,它不允許在沒有聲明變量之前使用變量。如果你想你的腳本在嚴(yán)格模式下執(zhí)行,你需要做的僅僅只是在腳本開始加上下面這行,例如:

"use strict"; var myDaughtersAge = 12; var myAge = 36; var ourAgeDifference = myAge - mydaughtersAge;

“use strict”告訴了解釋器在嚴(yán)格模式下解釋腳本。如果你現(xiàn)在執(zhí)行這段代碼,瀏覽器會停止運行且拋出錯誤告知有變量沒有進行聲明。

除了能檢查變量在使用之前是否聲明了,嚴(yán)格模式也包含了其他一些能讓書寫正確javascript代碼更簡單的東西。

我非常推薦你在嚴(yán)格模式下書寫你所有的javascript代碼。所以本書的所有javascript代碼都是嚴(yán)格模式下書寫的,這能幫程序員省掉很多麻煩且這樣的代碼在未來版本的javascript也是無可挑剔的。

指令和表達式

如果你看到語法圖里面的元素,你可能已經(jīng)注意到一些在賦值語句右邊的值或者程序片段,它們被叫做表達式。那么表達式和指令有什么不同呢?兩者不同的是指令某種方式上改變內(nèi)存,而表達式有一個值。指令通常使用表達式。這里有幾個表達式的例子:

16 numberOfBananas 2 a + 4 numberOfBananas + 12 - a -3 "myCanvas"

所有的這些表達式代表了一個確定的類型。除了最后一行,所有的表達式都是數(shù)值。最后一行是字符串。除了數(shù)值和字符串,還有其他類型的表達式。我討論的是本書最重要的表達式。比如,下節(jié)我會討論運算符的表達式,第7章會講使用函數(shù)或者方法作為一個表達式。

運算符和更復(fù)雜的表達式

這節(jié)討論javascript中不同的運算符。你會了解到運算符的優(yōu)先級。你也可以了解到有些時候,在javascript中表達式也能相當(dāng)?shù)膹?fù)雜。比如,一個變量可以包含多個值,或者可以代表一個函數(shù)。

算術(shù)運算符

(省略)

運算符優(yōu)先級

(省略)

把一個函數(shù)賦值給變量

在javascript中,函數(shù)被儲存在內(nèi)存里。正因為如此,函數(shù)也是表達式。所以,可以給一個變量賦值為函數(shù)。例如:

var someFunction = function () { // do something }

這個例子聲明了一個變量并進行了賦值。這個變量的值是一個無名函數(shù)。如果你想執(zhí)行這個函數(shù),你可以通過變量名字調(diào)用,如下:

someFunction();

那么像下面這樣定義一個函數(shù)和你之前看到的有什么不同呢?

function someFunction () { // do something }

實際上,這并沒有什么不同。主要是如果不像傳統(tǒng)方式那樣定義一個函數(shù),那么這個函數(shù)在定義之前不能使用。當(dāng)瀏覽器執(zhí)行一個javascript文件,有兩個步驟。第一個步驟,瀏覽器構(gòu)造一個函數(shù)的列表。第二步,瀏覽器解釋剩下的腳本。這對正確執(zhí)行腳本很有必要,瀏覽器需要知道哪個函數(shù)是有效的。比如,下面的這段代碼可以運行,即使這個在后面被定義:

someFunction(); function someFunction () { // do something }

然而,如果一個函數(shù)被賦值給一個變量,那么就只剩上述的第二步了。意味著下面的代碼會報錯。

someFunction(); var someFunction = function () { // do something }

瀏覽器會告知有一個變量沒有進行聲明。在定義之后進行調(diào)用就可以了,比如:

someFunction(); var someFunction = function () { // do something }

多個值組成的變量

一個變量可以由多個值組成而不是只能單一值。這就像函數(shù)里面做的一樣,把指令組合在一起。比如:

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

跟函數(shù)一樣,你可以把一些變量放在一個更大的變量里面。這個更大的變量就有了更多的值。就像下面這個例子:

var gameCharacter = { name : "Merlin", skill : "Magician", health : 100, power : 230 };

這是一個復(fù)合變量的例子。變量gameCharacter有一些變量。這些變量有名字和值。因此,在某種意義上說,變量gameCharacter由其它一些變量組成。每個子變量都有一個名字,冒號后面是值。這個包含名字的表達式和花括號之間的值被叫做對象字變量。圖3-6顯示了對象字變量的的語法圖:

(省略圖3-6)

在聲明和實例化變量gameCharacter之后,這塊內(nèi)存看起來如圖3-7:

(省略3-7)

你可以像下面這樣獲取一個復(fù)合變量的值。

gameCharacter.name = "Arjan"; var damage = gameCharacter.power * 10;

正如你看到的那樣,你可以獲取gameCharacter變量中的某個變量,通過在gameCharacter后面加上一個點和子變量名。javascript甚至允許在聲明和實例化復(fù)合變量后修改這個復(fù)合變量的值。舉個例子,看下面這段代碼:

var anotherGameCharacter = { name : "Arthur", skill : "King", health : 25, power : 35000 };anotherGameCharacter.familyName = "Pendragon";

現(xiàn)在anotherGameCharacter有5部分了,name, skill, health, power, familyName。

因為變量也可以指向函數(shù),所以你可以包含一個指向函數(shù)的子變量。如下所示:

var anotherGameCharacter = { name : "Arthur", familyName : "Pendragon", skill : "King", health : 25, power : 35000, healMe : function () { anotherGameCharacter.health = 100; } };

像之前一樣,你可以在這之后也能為其子變量添加一個函數(shù)。

anotherGameCharacter.killMe = function () { anotherGameCharacter.health = 0; };

你可以調(diào)用其他變量一樣調(diào)用這些函數(shù)。下面的指令恢復(fù)了游戲角色的生命:

anotherGameCharacter.healMe();

如果你想殺死角色,可以調(diào)用anotherGameCharacter.killMe();組合變量和函數(shù)最棒的地方在于你可以把相關(guān)的變量和函數(shù)放在一起。這個例子就是把與同一個游戲角色相關(guān)的變量放在了一起,同時增加了幾個相關(guān)的函數(shù)。從現(xiàn)在開始,如果一個函數(shù)屬于一個變量,我把這個函數(shù)叫做方法。把一個由其他變量組成的變量叫做對象。如果一個變量是對象的一部分,這個變量叫做成員變量。

你可以想象對象和方法的能量有多強大。它們提供了一個進入復(fù)雜游戲世界的方式。如果javascript沒有這種能力,那么在程序的開頭,你會聲明一長串的變量,并且不知道這些變量之間如何相關(guān)且能做什么。把變量裝進對象里且給對象提供方法,你可以寫出更容易理解的程序。在下節(jié),你就要使用這種強大的能力來寫一個簡單的移動方塊程序。

方塊移動游戲

這節(jié)實現(xiàn)了一個方塊在畫布上移動的簡單程序。主要有兩個目的:

  • 關(guān)于游戲循環(huán)里Update和draw函數(shù)的更多細(xì)節(jié)
  • 如何使用對象來結(jié)構(gòu)化程序

在寫這個程序之前,讓我們再一次看看BasicGame例子的代碼:

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 () { canvasContext.fillStyle = "blue"; canvasContext.fillRect(0, 0, canvas.width, canvas.height); } function mainLoop () { update(); draw(); window.setTimeout(mainLoop, 1000 / 60); }

我們現(xiàn)在用上面學(xué)到的關(guān)于對象的知識來把這段代碼重新整理成一個游戲程序。如下:

"use strict"; var Game = { canvas : undefined, canvasContext : undefined };Game.start = function () { Game.canvas = document.getElementById("myCanvas"); Game.canvasContext = Game.canvas.getContext("2d"); Game.mainLoop(); }; document.addEventListener('DOMContentLoaded', Game.start); Game.update = function () { }; Game.draw = function () { Game.canvasContext.fillStyle = "blue"; Game.canvasContext.fillRect(0, 0, Game.canvas.width, Game.canvas.height); }; Game.mainLoop = function () { Game.update(); Game.draw(); window.setTimeout(mainLoop, 1000 / 60); };

你創(chuàng)建了一個叫做Game的復(fù)合變量。這個對象有兩個成員變量,canvas和canvasContext。此外,你給這個對象添加了幾個方法,包括構(gòu)成這個游戲循環(huán)的方法。你分開定義了這個對象的方法。這樣做的原因是你可以清楚的分辨出這些數(shù)據(jù)和方法組成的對象可以如何與數(shù)據(jù)打交道。需要注意的是,像我推薦的一樣,添加了“use strict”。

現(xiàn)在,讓我們對這個程序進行擴展。它需要顯示一個在屏幕上移動的方塊。你想隨著時間改變方塊的X坐標(biāo)值。為了做到這些,你必須把現(xiàn)在的X坐標(biāo)值儲存在變量里。那樣你就可以在update里面給變量進行賦值并且使用這個值在draw方法里畫出這個方塊。放置這個變量的地方是在Game對象里,你可以像下面一樣聲明和實例化:

var Game = { canvas : undefined, canvasContext : undefined, rectanglePosition : 0 };

使用變量rectanglePosition來儲存在方塊的X坐標(biāo)值。在draw方法里,你可以使用這個值在屏幕上畫出方塊。這個例子里,繪畫出一個不超過畫布大小的方塊,下面是draw方法的內(nèi)容:

Game.draw = function () { Game.canvasContext.fillStyle = "blue"; Game.canvasContext.fillRect(Game.rectanglePosition, 100, 50, 50); }

現(xiàn)在你需要做的就是計算X的坐標(biāo)值。在update方法里計算X值,因為改變X值以為著更新著游戲世界。在這個例子里,我們基于時間的流逝來改變方塊的坐標(biāo)值。在javascript獲取系統(tǒng)的時間值:

var d = new Date(); var currentSystemTime = d.getTime();

在此之前,你還沒看過像第一行那樣的符號。現(xiàn)在,假設(shè)new Date()創(chuàng)造了一個復(fù)合變量(對象),里面有跟時間相關(guān)的信息,還有一些有用的方法。其中一個方法是getTime。你通過對象d調(diào)用此方法并儲存在currentSystemTime里。現(xiàn)在這個變量就有了從1970年1月1日開始的時間。你可以想象到這個變量值有點大。如果你想設(shè)置X坐標(biāo)值,那么就要一個很大的屏幕。當(dāng)然,不可能這樣做,你用時間對畫布的寬度求余,余數(shù)作為X坐標(biāo)值。那樣,你始終得到的是一個在0到畫布寬度之間的值。update方法如下:

Game.update = function () { var d = new Date(); Game.rectanglePosition = d.getTime() % Game.canvas.width; };

如你所知,update和draw方法順序調(diào)用,大約每秒60幀。每一次時間改變,系統(tǒng)時間也改變,意味著方塊的坐標(biāo)值也改變,那么方塊顯示的地方就與之前不一樣。

在這個例子運行的如你所想之前你還要做一件事情。如果你像這樣運行程序,一個藍色的條將出現(xiàn)在屏幕上。因為你在舊的方塊上繪畫了新的方塊。為了解決這個問題,每次畫方塊之前你需要清除畫布。清除畫布用clearRect方法。這個方法清除掉指定大小的畫布。舉例:

Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height);

為了方便,把這條指令放在一個叫做clearCanvas的方法里,如下:

Game.clearCanvas = function () { Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height); };

你需要做的就是在游戲循環(huán)里,在update和draw方法之前調(diào)用上面的方法。

Game.mainLoop = function() { Game.clearCanvas(); Game.update(); Game.draw(); window.setTimeout(Game.mainLoop, 1000 / 60); };

這個例子就完成了。運行結(jié)果如圖3-8所示:

(省略圖3-8)

變量的范圍

你聲明變量的地方?jīng)Q定了你能在哪些地方使用這些變量。看上面程序的d變量。它聲明在update方法里,所以它只能在update方法里面使用。就是說,你不能在draw方法里面使用這個變量。當(dāng)然,你可以在draw方法里面重新申請一個d變量,但是需要意識到的是這里的d變量和update方法里面的d變量是不一樣的。

相對的,你在一個對象的水平上聲明一個變量,你就可以在任何地方使用這個變量。你需要在update和draw方法里面使用方塊的X坐標(biāo),因為在update里面需要更新坐標(biāo)值,draw里面需要繪畫出方塊。因此需要在對象這個水平上聲明變量,那樣對象的所有方法都可以使用這個變量。

變量可以在哪里使用叫做變量范圍。在這個例子里面,d的變量范圍是update方法,Game.recentPostion是全局范圍。

你學(xué)到了什么

在這章里,你學(xué)到了:

  • 怎樣使用變量在內(nèi)存里儲存信息
  • 怎樣創(chuàng)建含有變量和方法的對象
  • 怎樣通過變量和update方法改變游戲世界和draw方法在屏幕上顯示游戲世界

轉(zhuǎn)載于:https://my.oschina.net/u/1587304/blog/399862

總結(jié)

以上是生活随笔為你收集整理的Building JavaScript Games for Phones Tablets and Desktop(3)-创造一个游戏世界的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。