自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

舉例闡述制作HTML5手機(jī)游戲的7個(gè)步驟

移動(dòng)開(kāi)發(fā) 移動(dòng)應(yīng)用
希望你通過(guò)這個(gè)粗糙的教程能學(xué)習(xí)到一些制作HTML5游戲的技巧。我們已經(jīng)制作了一款非常簡(jiǎn)單的游戲,它可以在大多數(shù)智能手機(jī)和瀏覽器上運(yùn)行。如果你有興趣進(jìn)一步探索手機(jī)HTML5游戲的潛力,我建議你多多測(cè)試框架,看看什么對(duì)你有用。

想用HTML5制作跨平臺(tái)的手機(jī)游戲?不必理會(huì)Java或Objective-C?也不用管應(yīng)用商店?聽(tīng)起來(lái)真不可思議!

現(xiàn)在有許多游戲開(kāi)發(fā)者都在挖掘手機(jī)HTML手機(jī)游戲的潛能。如《Nutmeg》和《Lunch Bug》就是優(yōu)秀的案例。HTML5游戲的優(yōu)勢(shì)在于,使用相同的代碼就能讓游戲在手機(jī)和電腦上運(yùn)行得一樣好。這是否意味著HTML5能夠讓游戲代碼編寫成為一件一勞永逸的事?

準(zhǔn)備

在你開(kāi)始編寫你自己的“神廟逃亡”或“憤怒的小鳥(niǎo)”以前,以下幾點(diǎn)可能會(huì)澆滅你的創(chuàng)作熱情:

表現(xiàn):

一般說(shuō)來(lái),手機(jī)瀏覽器的JavaScript引擎表現(xiàn)并不出眾。盡管從iOS 6和Chrome的Android測(cè)試版的情況上看,它進(jìn)步得很快。

分辨率:

Android設(shè)備的分辨率已經(jīng)五花八門了,更別說(shuō)iPhone 4和iPad 3的分辨率和像素密度也在不斷提高。

聲音:

但愿你不介意沒(méi)有聲音的游戲——手機(jī)瀏覽器的聲音支持很差。延遲是主要問(wèn)題,大部分設(shè)備只支持一種聲道。iOS甚至要等到用戶主動(dòng)開(kāi)啟才會(huì)加載聲音。

現(xiàn)在,作為網(wǎng)頁(yè)開(kāi)發(fā)者的你已經(jīng)習(xí)慣于處理瀏覽器的這些缺陷。所以,一些技術(shù)問(wèn)題是難不倒你的,對(duì)吧?另外,這些表現(xiàn)和聲音問(wèn)題都是暫時(shí)的。畢竟手機(jī)瀏覽器進(jìn)步飛快,這些問(wèn)題很快就會(huì)變成歷史。

在本教程中,你將通過(guò)制作一款比較簡(jiǎn)單的游戲來(lái)了解這些基本問(wèn)題以及解決辦法。

[[77091]]

iphone(from smashingmagazine)

這是一款相當(dāng)簡(jiǎn)單的游戲:玩家要做的就是點(diǎn)擊從屏幕底部浮起來(lái)的白色圓形,不要讓它們通過(guò)。你可以把這些白色圓形想象成漂浮上升的泡泡,你要在它們飛上天以前刺破它們。所以,我把這款小游戲叫作《POP》。

我們的制作過(guò)程可以分成如下7個(gè)步驟:

1、設(shè)置視圖,以適合大多數(shù)手機(jī)屏幕的尺寸;

2、使用canvas API在屏幕上繪制圖形;

3、捕捉觸擊事件;

4、制作基本的游戲循環(huán);

5、引入游戲“實(shí)體”;

6、添加碰撞檢測(cè)和一些簡(jiǎn)單的數(shù)學(xué)計(jì)算;

7、修飾外觀,即添加少量特效。

1、設(shè)置視圖

背景故事就隨便了。

正如前面提到的,不同的設(shè)備具有不同的分辨率和像素密度。這意味著我們必須調(diào)整畫布以適應(yīng)不同的視圖。這就可能損失游戲的外觀質(zhì)量,但我們有一個(gè)小技巧,也就是先使用小畫布,然后按比例放大,這么做后的畫面效果就好多了。

我們先寫一段基本的HTML代碼:

  1. <!DOCTYPE HTML> 
  2. <html lang=”en”> 
  3. <head> 
  4. <meta charset=”UTF-8″> 
  5. <meta name=”viewport” content=”width=device-width, 
  6. user-scalable=noinitial-scale=1maximum-scale=1user-scalable=0″ /> 
  7. <meta name=”apple-mobile-web-app-capable” content=”yes” /> 
  8. <meta name=”apple-mobile-web-app-status-bar-style” content=”black-translucent” /> 
  9. <style type=”text/css”> 
  10. body { margin: 0; padding: 0; background: #000;} 
  11. canvas { display: block; margin: 0 auto; background: #fff; } 
  12. </style> 
  13. </head> 
  14. <body> 
  15. <canvas> </canvas> 
  16. <script> 
  17. // all the code goes here 
  18. </script> 
  19. </body> 
  20. </html> 

這個(gè)meta視圖標(biāo)簽的作用是,命令瀏覽器禁止用戶縮放畫面,以及按完全尺寸渲染,而不是收縮頁(yè)面。隨后的帶apple-前綴的meta標(biāo)簽允許游戲被加入書簽。在iPhone上,加入書簽的應(yīng)用不會(huì)顯示頁(yè)面底部的在工具條上,因此節(jié)省了大片空間。

看看以下代碼:

  1. // namespace our game 
  2. var POP = { 
  3. // set up some initial values 
  4. WIDTH: 320, 
  5. HEIGHT:  480, 
  6. // we’ll set the rest of these 
  7. // in the init function 
  8. RATIO:  null, 
  9. currentWidth:  null, 
  10. currentHeight:  null, 
  11. canvas: null, 
  12. ctx:  null, 
  13. init: function() { 
  14. // the proportion of width to height 
  15. POPPOP.RATIO = POP.WIDTH / POP.HEIGHT; 
  16. // these will change when the screen is resized 
  17. POPPOP.currentWidth = POP.WIDTH; 
  18. POPPOP.currentHeight = POP.HEIGHT; 
  19. // this is our canvas element 
  20. POP.canvas = document.getElementsByTagName(‘canvas’)[0]; 
  21. // setting this is important 
  22. // otherwise the browser will 
  23. // default to 320 x 200 
  24. POPPOP.canvas.width = POP.WIDTH; 
  25. POPPOP.canvas.height = POP.HEIGHT; 
  26. // the canvas context enables us to 
  27. // interact with the canvas api 
  28. POPPOP.ctx = POP.canvas.getContext(’2d’); 
  29. // we’re ready to resize 
  30. POP.resize(); 
  31. }, 
  32. resize: function() { 
  33. POP.currentHeight = window.innerHeight; 
  34. // resize the width in proportion 
  35. // to the new height 
  36. POPPOP.currentWidth = POP.currentHeight * POP.RATIO; 
  37. // this will create some extra space on the 
  38. // page, allowing us to scroll past 
  39. // the address bar, thus hiding it. 
  40. if (POP.android || POP.ios) { 
  41. document.body.style.height = (window.innerHeight + 50) + ‘px’; 
  42. // set the new canvas style width and height 
  43. // note: our canvas is still 320 x 480, but 
  44. // we’re essentially scaling it with CSS 
  45. POPPOP.canvas.style.width = POP.currentWidth + ‘px’; 
  46. POPPOP.canvas.style.height = POP.currentHeight + ‘px’; 
  47. // we use a timeout here because some mobile 
  48. // browsers don’t fire if there is not 
  49. // a short delay 
  50. window.setTimeout(function() { 
  51. window.scrollTo(0,1); 
  52. }, 1); 
  53. }; 
  54. window.addEventListener(‘load’, POP.init, false); 
  55. window.addEventListener(‘resize’, POP.resize, false); 

首先,我們要給游戲創(chuàng)建一個(gè)命名空間“POP”。優(yōu)秀的開(kāi)發(fā)者不會(huì)污染整個(gè)命名空間的。比較好的做法是在程序的開(kāi)頭部分就聲明所有變量。大部分變量 是很容易理解的:canvas表示HTML中的canvas元素;ctx使我們可以通過(guò)JavaScript canvas API來(lái)訪問(wèn)它。

在POP.init,我們獲得canvas元素的引用,調(diào)整canvas元素的尺寸為480 × 320。resize函數(shù)會(huì)在調(diào)整大小和加載事件時(shí)啟用,從而按比例調(diào)整canvas的style屬性(游戲邦注:高度和寬度)。實(shí)際上,canvas仍 然是相同的尺寸,只是已經(jīng)通過(guò)CSS放大了。試一試調(diào)整你的瀏覽器大小,看看canvas的縮放效果。

如果你在你的手機(jī)上實(shí)驗(yàn),你會(huì)發(fā)現(xiàn)地址欄仍然可見(jiàn)。解決這個(gè)問(wèn)題的做法是:對(duì)文件添加額外像素,然后向下滾動(dòng)以隱藏地址欄,如下:

  1. // we need to sniff out Android and iOS 
  2. // so that we can hide the address bar in 
  3. // our resize function 
  4. POP.ua = navigator.userAgent.toLowerCase(); 
  5. POPPOP.android = POP.ua.indexOf(‘android’) > -1 ? true : false; 
  6. POP.ios = ( POP.ua.indexOf(‘iphone’) > -1 || POP.ua.indexOf(‘ipad’) > -1  ) ? 
  7. true : false; 

以上代碼搜索userAgent,如果存在則標(biāo)記。在調(diào)用POP.resize()以前,把它添加到POP.init后面。

然后,在resize函數(shù)中,如果android或ios為true,我們就把文件的高度再增加50像素——這就足以隱藏地址欄了。

  1. // this will create some extra space on the 
  2. // page, enabling us to scroll past 
  3. // the address bar, thus hiding it. 
  4. if (POP.android || POP.ios) { 
  5. document.body.style.height = (window.innerHeight + 50) + ‘px’; 

注意,我們的做法只適用于Android和iOS設(shè)備;否則,討厭的滾動(dòng)條就會(huì)出現(xiàn)。另外,我們必須延遲scrollTo,以確保Safari設(shè)備不會(huì)忽略它。

2、在畫布上繪制

我們已經(jīng)根據(jù)視圖調(diào)整好畫布了,接下來(lái)我們?cè)撛谏厦娈孅c(diǎn)什么了。

注:在本教程中,我們只使用基本的幾何形狀。iOS 5和Chrome的Android測(cè)試版可以用很高的幀率處理大量子畫面。在Android 3.2或以下版本中實(shí)驗(yàn)一下,你會(huì)發(fā)現(xiàn)前者的幀率確實(shí)大大提高了。幸運(yùn)地是,繪制圓形并不需要占用太多內(nèi)存,所以我們的游戲中可以大量使用圓形,即使是在 老設(shè)備上,表現(xiàn)也不會(huì)太差。

以下,我們已經(jīng)添加了一個(gè)基本的Draw對(duì)象,使我們可以清除屏幕,繪制矩形和圓形,然后添加文本。

  1. // abstracts various canvas operations into 
  2. // standalone functions 
  3. POP.Draw = { 
  4. clear: function() { 
  5. POP.ctx.clearRect(0, 0, POP.WIDTH, POP.HEIGHT); 
  6. }, 
  7. rect: function(x, y, w, h, col) { 
  8. POP.ctx.fillStyle = col
  9. POP.ctx.fillRect(x, y, w, h); 
  10. }, 
  11. circle: function(x, y, r, col) { 
  12. POP.ctx.fillStyle = col
  13. POP.ctx.beginPath(); 
  14. POP.ctx.arc(x + 5, y + 5, r, 0,  Math.PI * 2, true); 
  15. POP.ctx.closePath(); 
  16. POP.ctx.fill(); 
  17. }, 
  18. text: function(string, x, y, size, col) { 
  19. POP.ctx.font = ‘bold ‘+size+’px Monospace’; 
  20. POP.ctx.fillStyle = col
  21. POP.ctx.fillText(string, x, y); 
  22. }; 

我們的Draw對(duì)象有清除屏幕和繪制矩形、圓形及文本的方法。抽象這些操作的好處是,我們不必記憶確切的canvas API調(diào)用,而且繪制圓形的代碼簡(jiǎn)單到只有一句。

代碼如下:

  1. // include this at the end of POP.init function 
  2. POP.Draw.clear(); 
  3. POP.Draw.rect(120,120,150,150, ‘green’); 
  4. POP.Draw.circle(100, 100, 50, ‘rgba(255,0,0,0.5)’); 
  5. POP.Draw.text(‘Hello World’, 100, 100, 10, ‘#000′); 

把上述代碼放在POP.init函數(shù)之后。你應(yīng)該可以看到畫布上繪制出許多圖形。

3、觸擊事件

與click事件一樣,手機(jī)瀏覽器有捕捉觸擊事件的方法。

以下代碼的重點(diǎn)是touchstart、touchmove和touchend事件。對(duì)于標(biāo)準(zhǔn)的click事件,我們可以從e.pageX的 e.pageY中獲得座標(biāo)。觸擊事件則稍有不同,它們有一個(gè)touches集合,其中的各個(gè)元素都包含觸擊座標(biāo)和其他數(shù)據(jù)。我們只想要第一次觸擊,所以我 們要設(shè)置一個(gè)e.touches[0]。

注:只有版本4以后, Android才支持訪問(wèn)多次觸擊動(dòng)作的JavaScript。

當(dāng)禁用滾動(dòng)、縮放和其他會(huì)中斷游戲的活動(dòng)時(shí),我們還要調(diào)用e.preventDefault(); 。

添加以下代碼到POP.init函數(shù):

  1. // listen for clicks 
  2. window.addEventListener(‘click’, function(e) { 
  3. e.preventDefault(); 
  4. POP.Input.set(e); 
  5. }, false); 
  6. // listen for touches 
  7. window.addEventListener(‘touchstart’, function(e) { 
  8. e.preventDefault(); 
  9. // the event object has an array 
  10. // named touches; we just want 
  11. // the first touch 
  12. POP.Input.set(e.touches[0]); 
  13. }, false); 
  14. window.addEventListener(‘touchmove’, function(e) { 
  15. // we’re not interested in this, 
  16. // but prevent default behaviour 
  17. // so the screen doesn’t scroll 
  18. // or zoom 
  19. e.preventDefault(); 
  20. }, false); 
  21. window.addEventListener(‘touchend’, function(e) { 
  22. // as above 
  23. e.preventDefault(); 
  24. }, false); 
  25. 你可能已注意到,以上代碼把事件數(shù)據(jù)傳輸給Input對(duì)象。但我們現(xiàn)在要先定義一下它: 
  26. // + add this at the bottom of your code, 
  27. // before the window.addEventListeners 
  28. POP.Input = { 
  29. x: 0, 
  30. y: 0, 
  31. tapped :false, 
  32. set: function(data) { 
  33. this.x = data.pageX; 
  34. this.y = data.pageY; 
  35. this.tapped = true
  36. POP.Draw.circle(this.x, this.y, 10, ‘red’); 
  37. }; 

現(xiàn)在,測(cè)試一下。圓形沒(méi)有出現(xiàn)。這是為什么?有了!因?yàn)槲覀円呀?jīng)縮放畫布了,當(dāng)映射觸擊到屏幕的位置時(shí),我們必須考慮到這一點(diǎn)。

首先,我們必須從座標(biāo)中扣除偏移值。

  1. var offsetTop = POP.canvas.offsetTop, 
  2. offsetLeft = POP.canvas.offsetLeft; 
  3. this.x = data.pageX – offsetLeft; 
  4. this.y = data.pageY – offsetTop; 

offset_diagram(from smashingmagazine)

offset_diagram(from smashingmagazine)

然后,考慮到畫布已經(jīng)縮放過(guò)了,我們得計(jì)算一下實(shí)際畫布(游戲邦注:仍然是320 × 480)。

  1. var offsetTop = POP.canvas.offsetTop, 
  2. offsetLeft = POP.canvas.offsetLeft; 
  3. scale = POP.currentWidth / POP.WIDTH; 
  4. this.x = ( data.pageX – offsetLeft ) / scale; 
  5. this.y = ( data.pageY – offsetTop ) / scale; 

scaled_canvas_diagram(from smashingmagazine)

scaled_canvas_diagram(from smashingmagazine)

你開(kāi)始覺(jué)得頭疼了吧?那我就給你舉個(gè)例子。想象一下玩家輕擊500 × 750的畫布上的座標(biāo)400,400。我們必須調(diào)整這個(gè)座標(biāo),因?yàn)楫嫴嫉膶?shí)際尺寸是480 × 320。所以,真正的X座標(biāo)是400除以比例,即400 ÷ 1.56 = 320.5。

我們當(dāng)然不是在每一個(gè)觸擊事件發(fā)生時(shí)計(jì)算,而是在調(diào)整完畫布尺寸后計(jì)算座標(biāo)。在程序的開(kāi)頭部分添加如下代碼,以及其他變量聲明:

  1. // let’s keep track of scale 
  2. // along with all initial declarations 
  3. // at the start of the program 
  4. scale:  1, 
  5. // the position of the canvas 
  6. // in relation to the screen 
  7. offset = {top: 0, left: 0}, 

在我們的調(diào)整大小函數(shù)中,調(diào)整畫布的寬高后,我們要記錄一下當(dāng)前的尺寸和偏移量:

  1. // add this to the resize function. 
  2. POPPOP.scale = POP.currentWidth / POP.WIDTH; 
  3. POPPOP.offset.top = POP.canvas.offsetTop; 
  4. POPPOP.offset.left = POP.canvas.offsetLeft; 

現(xiàn)在,我們可以在POP.Input類的set方法中使用它們了:

  1. this.x = (data.pageX – POP.offset.left) / POP.scale; 
  2. this.y = (data.pageY – POP.offset.top) / POP.scale; 

別走開(kāi),下頁(yè)內(nèi)容更精彩

#p#

4、循環(huán)

典型的游戲循環(huán)如下:

1、用戶輸入,

2、更新和處理碰撞,

3、在屏幕上渲染,

4、重復(fù)。

我們當(dāng)然可以使用setInterval,但在requestAnimationFrame中有一個(gè)新玩意兒。它能保證動(dòng)畫更流暢,并且能節(jié)省電池量。不幸地是,并非所有瀏覽器都支持它。但Paul Irish已經(jīng)想到一個(gè)方便的解決辦法。

我們也可以借鑒他的辦法,代碼如下:

  1. // http://paulirish.com/2011/requestanimationframe-for-smart-animating 
  2. // shim layer with setTimeout fallback 
  3. window.requestAnimFrame = (function(){ 
  4. return  window.requestAnimationFrame       || 
  5. window.webkitRequestAnimationFrame || 
  6. window.mozRequestAnimationFrame    || 
  7. window.oRequestAnimationFrame      || 
  8. window.msRequestAnimationFrame     || 
  9. function( callback ){ 
  10. window.setTimeout(callback, 1000 / 60); 
  11. }; 
  12. })(); 

接著我們來(lái)制作初步的游戲循環(huán):

  1. // Add this at the end of POP.init; 
  2. // it will then repeat continuously 
  3. POP.loop(); 
  4. // Add the following functions after POP.init: 
  5. // this is where all entities will be moved 
  6. // and checked for collisions, etc. 
  7. update: function() { 
  8. }, 
  9. // this is where we draw all the entities 
  10. render: function() { 
  11. POP.Draw.clear(); 
  12. }, 
  13. // the actual loop 
  14. // requests animation frame, 
  15. // then proceeds to update 
  16. // and render 
  17. loop: function() { 
  18. requestAnimFrame( POP.loop ); 
  19. POP.update(); 
  20. POP.render(); 

我們?cè)赑OP.init之后調(diào)用這個(gè)循環(huán)。POP.loop接著調(diào)用我們的POP.update和POP.render方法。 requestAnimFrame保證這個(gè)循環(huán)被再次調(diào)用,最好是以60幀每秒的速度。注意,我們不必?fù)?dān)心查看循環(huán)中的輸入,因?yàn)槲覀円呀?jīng)在注意通過(guò) POP.Input類可以訪問(wèn)到的觸擊和點(diǎn)擊事件。

現(xiàn)在的問(wèn)題是,我們的最后一次觸擊在屏幕上消失得太快了。我們必須想辦法讓屏幕更好地記憶和顯示觸擊位置。

5、觸擊

首先,我們添加一個(gè)實(shí)體集合。這個(gè)集合包含游戲中出現(xiàn)的所有觸擊點(diǎn)、泡泡、粒子和其他動(dòng)態(tài)物品。

// put this at start of program
entities: [],

我們來(lái)做一個(gè)Touch類,它將在接觸點(diǎn)處繪制一個(gè)圓點(diǎn),這個(gè)圓點(diǎn)之后會(huì)慢慢消褪。

  1. POP.Touch = function(x, y) { 
  2. this.type = ‘touch’;    // we’ll need this later 
  3. this.x = x;             // the x coordinate 
  4. this.y = y;             // the y coordinate 
  5. this.r = 5;             // the radius 
  6. this.opacity = 1;       // initial opacity; the dot will fade out 
  7. this.fade = 0.05;       // amount by which to fade on each game tick 
  8. this.remove = false;    // flag for removing this entity. POP.update 
  9. // will take care of this 
  10. this.update = function() { 
  11. // reduce the opacity accordingly 
  12. this.opacity -this.fade; 
  13. // if opacity if 0 or less, flag for removal 
  14. this.remove = (this.opacity < 0) ? true : false; 
  15. }; 
  16. this.render = function() { 
  17. POP.Draw.circle(this.x, this.y, this.r, ‘rgba(255,0,0,’+this.opacity+’)'); 
  18. }; 
  19. }; 

Touch類具有一系列屬性。x和y座標(biāo)是參數(shù),半徑this.r為5像素,初始不透明度為1,觸擊點(diǎn)消裉速率為0.05,remove標(biāo)記告訴主游戲循環(huán)是否將觸擊圓點(diǎn)從該實(shí)體集合中移除。

關(guān)鍵是,這個(gè)類有兩個(gè)主要方法:update和render。我們將從游戲循環(huán)的相應(yīng)部分調(diào)用它們。

我們先在游戲循環(huán)中刷出一個(gè)新的Touch實(shí)例,再通過(guò)update方法移除:

  1. // POP.update function 
  2. update: function() { 
  3. var i; 
  4. // spawn a new instance of Touch 
  5. // if the user has tapped the screen 
  6. if (POP.Input.tapped) { 
  7. POP.entities.push(new POP.Touch(POP.Input.x, POP.Input.y)); 
  8. // set tapped back to false 
  9. // to avoid spawning a new touch 
  10. // in the next cycle 
  11. POP.Input.tapped = false
  12. // cycle through all entities and update as necessary 
  13. for (i = 0; i < POP.entities.length; i += 1) { 
  14. POP.entities[i].update(); 
  15. // delete from array if remove property 
  16. // flag is set to true 
  17. if (POP.entities[i].remove) { 
  18. POP.entities.splice(i, 1); 
  19. }, 

基本上,如果POP.Input.tapped是true,那么我們就添加一個(gè)新的POP.Touch實(shí)例到我們的實(shí)體集合。循環(huán)這個(gè)實(shí)體集合,即調(diào)用各個(gè)實(shí)體的update方法。最后,如果實(shí)體被標(biāo)記為移除,它就會(huì)從該集合中刪除。

接著,我們?cè)赑OP.render函數(shù)中渲染它們。

  1. // POP.render function 
  2. render: function() { 
  3. var i; 
  4. POP.Draw.rect(0, 0, POP.WIDTH, POP.HEIGHT, ‘#036′); 
  5. // cycle through all entities and render to canvas 
  6. for (i = 0; i < POP.entities.length; i += 1) { 
  7. POP.entities[i].render(); 
  8. }, 

類似于update函數(shù),循環(huán)實(shí)體和調(diào)用它們的render方法,在屏幕上繪制它們。

到目前為止,一切進(jìn)展順利?,F(xiàn)在我們要添加Bubble類,它的作用是生產(chǎn)漂浮上升的泡泡。

  1. POP.Bubble = function() { 
  2. this.type = ‘bubble’; 
  3. this.x = 100
  4. this.r = 5;                // the radius of the bubble 
  5. this.y = POP.HEIGHT + 100; // make sure it starts off screen 
  6. this.remove = false
  7. this.update = function() { 
  8. // move up the screen by 1 pixel 
  9. this.y -1
  10. // if off screen, flag for removal 
  11. if (this.y < -10) { 
  12. this.remove = true
  13. }; 
  14. this.render = function() { 
  15. POP.Draw.circle(this.x, this.y, this.r, ‘rgba(255,255,255,1)’); 
  16. }; 
  17. }; 

POP.Bubble類非常接近于Touch類,主要的區(qū)別是它并不是像觸擊點(diǎn)一樣消褪,而是向上移動(dòng)。這個(gè)活動(dòng)是通過(guò)在update函數(shù)中改變y位置即 this.y實(shí)現(xiàn)的。這里,我們也要查看泡泡是否離開(kāi)屏幕;如果是,我們就要把它標(biāo)記為移除。

注:我們已經(jīng)制作了基本Entity類,Touch和Bubble都包含在內(nèi)。但是,我現(xiàn)在不想比較JavaScript原型的繼承和類。

  1. // Add at the start of the program 
  2. // the amount of game ticks until 
  3. // we spawn a bubble 
  4. nextBubble: 100, 
  5. // at the start of POP.update 
  6. // decrease our nextBubble counter 
  7. POP.nextBubble -1
  8. // if the counter is less than zero 
  9. if (POP.nextBubble < 0) { 
  10. // put a new instance of bubble into our entities array 
  11. POP.entities.push(new POP.Bubble()); 
  12. // reset the counter with a random value 
  13. POP.nextBubble = ( Math.random() * 100 ) + 100; 

以上,我們已經(jīng)為游戲循環(huán)添加了隨機(jī)計(jì)時(shí)器。游戲循環(huán)會(huì)在隨機(jī)位置刷出Bubble實(shí)例。在游戲開(kāi)始時(shí),我們?cè)O(shè)置nextBubble(下一個(gè)泡泡)的出現(xiàn)間隔為100,即當(dāng)100減少到0時(shí),游戲就會(huì)刷出新泡泡,并重置nextBubble計(jì)數(shù)器。

6、整合

首先,我們還沒(méi)使用到任何碰撞檢測(cè)。我們可以用簡(jiǎn)單地函數(shù)實(shí)現(xiàn)它。

  1. // this function checks if two circles overlap 
  2. POP.collides = function(a, b) { 
  3. var distance_squared = ( ((a.x – b.x) * (a.x – b.x)) + 
  4. ((a.y – b.y) * (a.y – b.y))); 
  5. var radii_squared = (a.r + b.r) * (a.r + b.r); 
  6. if (distance_squared < radii_squared) { 
  7. return true; 
  8. } else { 
  9. return false; 
  10. }; 
  11. // at the start of POP.update, we set a flag for checking collisions 
  12. var i, 
  13. checkCollision = false; // we only need to check for a collision 
  14. // if the user tapped on this game tick 
  15. // and then incorporate into the main logic 
  16. if (POP.Input.tapped) { 
  17. POP.entities.push(new POP.Touch(POP.Input.x, POP.Input.y)); 
  18. // set tapped back to false 
  19. // to avoid spawning a new touch 
  20. // in the next cycle 
  21. POP.Input.tapped = false
  22. checkCollision = true
  23. // cycle through all entities and update as necessary 
  24. for (i = 0; i < POP.entities.length; i += 1) { 
  25. POP.entities[i].update(); 
  26. if (POP.entities[i].type === ‘bubble’ && checkCollision) { 
  27. hit = POP.collides(POP.entities[i], 
  28. {x: POP.Input.x, y: POP.Input.y, r: 7}); 
  29. POP.entities[i].remove = hit
  30. // delete from array if remove property 
  31. // is set to true 
  32. if (POP.entities[i].remove) { 
  33. POP.entities.splice(i, 1); 

現(xiàn)在的泡泡比較無(wú)趣,移動(dòng)速度和軌跡都一樣。我們可以通過(guò)下面這段簡(jiǎn)單的代碼來(lái)隨機(jī)化泡泡的運(yùn)動(dòng):

  1. POP.Bubble = function() { 
  2. this.type = ‘bubble’; 
  3. this.r = (Math.random() * 20) + 10; 
  4. this.speed = (Math.random() * 3) + 1; 
  5. this.x = (Math.random() * (POP.WIDTH) – this.r); 
  6. this.y = POP.HEIGHT + (Math.random() * 100) + 100; 
  7. this.remove = false
  8. this.update = function() { 
  9. this.y -this.speed; 
  10. // the rest of the class is unchanged 

我們要讓泡泡左右擺,使玩家更難觸擊到它們:

  1. // the amount by which the bubble 
  2. // will move from side to side 
  3. this.waveSize = 5 + this.r; 
  4. // we need to remember the original 
  5. // x position for our sine wave calculation 
  6. thisthis.xConstant = this.x; 
  7. this.remove = false
  8. this.update = function() { 
  9. // a sine wave is commonly a function of time 
  10. var time = new Date().getTime() * 0.002; 
  11. this.y -this.speed; 
  12. // the x coordinate to follow a sine wave 
  13. thisthis.x = this.waveSize * Math.sin(time) + this.xConstant; 
  14. // the rest of the class is unchanged 

我們使用一些基本的幾何學(xué)知識(shí)就能達(dá)到這個(gè)效果,也就是正弦波。做游戲不一定要精通數(shù)學(xué),基本的知識(shí)就非常夠用了。

游戲屏幕上還應(yīng)該顯示計(jì)數(shù)。為此,我們要追蹤游戲過(guò)程的各種活動(dòng)。

將以下代碼與所有其他變量聲明一起放在程序的開(kāi)頭部分:

  1. // this goes at the start of the program 
  2. // to track players’s progress 
  3. POP.score = { 
  4. taps: 0, 
  5. hit: 0, 
  6. escaped: 0, 
  7. accuracy: 0 
  8. }, 

現(xiàn)在,在Bubble類,當(dāng)泡泡離開(kāi)屏幕,我們可以用POP.score.escaped記錄。

  1. // in the bubble class, when a bubble makes it to 
  2. // the top of the screen 
  3. if (this.y < -10) { 
  4. POP.score.escaped += 1; // update score 
  5. this.remove = true

在主要更新循環(huán)中,我們相應(yīng)地增加POP.score.hit:

  1. // in the update loop 
  2. if (POP.entities[i].type === ‘bubble’ && checkCollision) { 
  3. hit = POP.collides(POP.entities[i], 
  4. {x: POP.Input.x, y: POP.Input.y, r: 7}); 
  5. if (hit) { 
  6. POP.score.hit += 1; 
  7. POP.entities[i].remove = hit

為了得出命中率,我們必須記錄玩家的所有觸擊動(dòng)作:

  1. // and record all taps 
  2. if (POP.Input.tapped) { 
  3. // keep track of taps; needed to 
  4. // calculate accuracy 
  5. POP.score.taps += 1; 

命中率的算法就是,觸擊數(shù)乘上100。注意,~~(POP.score.accuracy)把約數(shù)變成整數(shù)。

  1. // Add at the end of the update loop 
  2. // to calculate accuracy 
  3. POP.score.accuracy = (POP.score.hit / POP.score.taps) * 100; 
  4. POP.score.accuracy = isNaN(POP.score.accuracy) ? 
  5. 0 : 
  6. ~~(POP.score.accuracy); // a handy way to round floats 

最后,我們使用POP.Draw.text來(lái)顯示得分。

  1. // and finally in the draw function 
  2. POP.Draw.text(‘Hit: ‘ + POP.score.hit, 20, 30, 14, ‘#fff’); 
  3. POP.Draw.text(‘Escaped: ‘ + POP.score.escaped, 20, 50, 14, ‘#fff’); 
  4. POP.Draw.text(‘Accuracy: ‘ + POP.score.accuracy + ‘%’, 20, 70, 14, ‘#fff’); 

7、修飾

我們都知道,制作一個(gè)可玩的demo只需要若干小時(shí),但一款漂亮的游戲卻要耗費(fèi)數(shù)天、數(shù)月甚至數(shù)年!

我們可以通過(guò)以下做法增加這款小游戲的視覺(jué)吸引力。

顆粒效果

大多數(shù)游戲都會(huì)使用顆粒效果,特別是對(duì)于爆炸。當(dāng)玩家觸擊泡泡時(shí),泡泡就碎成若干小泡泡,而不是立即消失,效果會(huì)不會(huì)更好呢?

看看我們的Particle類:

  1. POP.Particle = function(x, y,r, col) { 
  2. this.x = x; 
  3. this.y = y; 
  4. this.r = r; 
  5. this.col = col; 
  6. // determines whether particle will 
  7. // travel to the right of left 
  8. // 50% chance of either happening 
  9. this.dir = (Math.random() * 2 > 1) ? 1 : -1; 
  10. // random values so particles do not 
  11. // travel at the same speeds 
  12. this.vx = ~~(Math.random() * 4) * this.dir; 
  13. this.vy = ~~(Math.random() * 7); 
  14. this.remove = false
  15. this.update = function() { 
  16. // update coordinates 
  17. this.x += this.vx; 
  18. this.y += this.vy; 
  19. // increase velocity so particle 
  20. // accelerates off screen 
  21. this.vx *= 0.99; 
  22. this.vy *= 0.99; 
  23. // adding this negative amount to the 
  24. // y velocity exerts an upward pull on 
  25. // the particle, as if drawn to the 
  26. // surface 
  27. this.vy -0.25; 
  28. // off screen 
  29. if (this.y < 0) { 
  30. this.remove = true
  31. }; 
  32. this.render = function() { 
  33. POP.Draw.circle(this.x, this.y, this.r, this.col); 
  34. }; 
  35. }; 

以上代碼的作用顯而易見(jiàn)。當(dāng)泡泡被擊中時(shí),它會(huì)碎成若干加速上升到水面的顆粒。不過(guò),本文不會(huì)探討這個(gè)效果的算術(shù)和物理學(xué)。

為了制作顆粒效果,我們我們要在entities集合中添加若干泡泡被擊中時(shí)會(huì)出現(xiàn)的顆粒:

  1. // modify the main update function like so: 
  2. if (hit) { 
  3. // spawn an explosion 
  4. for (var n = 0; n < 5; n +=1 ) { 
  5. POP.entities.push(new POP.Particle( 
  6. POP.entities[i].x, 
  7. POP.entities[i].y, 
  8. 2, 
  9. // random opacity to spice it up a bit 
  10. ‘rgba(255,255,255,’+Math.random()*1+’)’ 
  11. )); 
  12. POP.score.hit += 1; 

水波

考慮到游戲發(fā)生在水下,有必要在屏幕頂部添加水波效果。我們可以通過(guò)繪制大量重疊的圓形來(lái)制造水波的錯(cuò)覺(jué):

  1. // set up our wave effect; 
  2. // basically, a series of overlapping circles 
  3. // across the top of screen 
  4. POP.wave = { 
  5. x: -25, // x coordinate of first circle 
  6. y: -40, // y coordinate of first circle 
  7. r: 50, // circle radius 
  8. time: 0, // we’ll use this in calculating the sine wave 
  9. offset: 0 // this will be the sine wave offset 
  10. }; 
  11. // calculate how many circles we need to 
  12. // cover the screen’s width 
  13. POP.wave.total = Math.ceil(POP.WIDTH / POP.wave.r) + 1; 

把以上代碼添加到POP.init函數(shù)前面。POP.wave有許多值。

添加以下代碼到主要更新函數(shù)中。它作用正統(tǒng)波來(lái)調(diào)整水波的位置,從而產(chǎn)生水面運(yùn)動(dòng)的錯(cuò)覺(jué)。

  1. // update wave offset 
  2. // feel free to play with these values for 
  3. // either slower or faster waves 
  4. POP.wave.time = new Date().getTime() * 0.002; 
  5. POP.wave.offset = Math.sin(POP.wave.time * 0.8) * 5; 

最后要做的就是讓render函數(shù)繪制水波:

  1. // display snazzy wave effect 
  2. for (i = 0; i < POP.wave.total; i++) { 
  3. POP.Draw.circle( 
  4. POP.wave.x + POP.wave.offset +  (i * POP.wave.r), 
  5. POP.wave.y, 
  6. POP.wave.r, 
  7. ‘#fff’); 

這里,我們對(duì)泡泡重復(fù)使用正弦波,使水波活動(dòng)更加溫和。

結(jié)語(yǔ)

終于完工了。希望你通過(guò)這個(gè)粗糙的教程能學(xué)習(xí)到一些制作HTML5游戲的技巧。我們已經(jīng)制作了一款非常簡(jiǎn)單的游戲,它可以在大多數(shù)智能手機(jī)和瀏覽器上運(yùn)行。你還可以考慮從以下幾個(gè)方面進(jìn)一步改進(jìn)游戲:

1、使用本地存儲(chǔ)器保存最高得分。

2、添加載入畫面和結(jié)束畫面。

3、添加增益道具。

4、添加聲音。與我在本文開(kāi)頭部分所說(shuō)的相反,這并非不可能,只是會(huì)有一點(diǎn)麻煩。技術(shù)之一是使用聲音精靈(相當(dāng)于CSS中的圖像精靈)。

5、盡情發(fā)揮你的想像力!

如果你有興趣進(jìn)一步探索手機(jī)HTML5游戲的潛力,我建議你多多測(cè)試框架,看看什么對(duì)你有用。如果你愿意花一點(diǎn)錢,Impact引擎是一個(gè)好起點(diǎn),它附帶詳盡的說(shuō)明文件和實(shí)用的論壇?!禭-Type》效果不錯(cuò)吧?

責(zé)任編輯:閆佳明 來(lái)源: gamerboom
相關(guān)推薦

2011-03-03 17:03:53

HTML5手機(jī)游戲Flash

2019-12-26 15:12:14

Html5框架Web

2012-09-12 09:28:50

2012-03-29 09:18:47

HTML5WEB

2014-12-30 17:13:51

HTML5

2011-06-28 08:58:29

2013-10-21 15:24:49

html5游戲

2013-07-02 09:17:57

html5組件

2015-10-08 08:48:44

HTML5canvas動(dòng)畫

2015-12-03 15:22:01

HTML5游戲建議

2016-01-20 10:11:56

華麗CanvasHTML5

2010-03-16 17:56:32

PythonS60手機(jī)

2011-11-30 15:14:32

HTML 5

2013-06-24 14:55:30

HTML5

2015-07-08 16:38:10

Cocos游戲引擎

2013-04-19 02:53:58

手機(jī)游戲手機(jī)游戲引擎HTML5

2016-04-18 16:20:55

2011-12-09 20:25:16

HTML5

2012-03-02 09:59:06

HTML 5

2012-04-01 10:02:00

HTML5
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)