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

使用HTML5開發(fā)體感游戲:VeloMaze的開發(fā)案例

移動(dòng)開發(fā)
HTML5現(xiàn)在越來越像一個(gè)游戲開發(fā)平臺(tái)。但有時(shí)候,游戲領(lǐng)域?qū)τ谌绾螒?yīng)用HTML5的特性設(shè)置了相當(dāng)多的限制條件,尤其是對那些訪問硬件設(shè)備的接口更是如此。在2012年11月初,我加入了copypastel小組,并決定分享我在第三屆年度NodeKO競賽中開發(fā)游戲的經(jīng)歷。盡管由于時(shí)間限制無法詳解全項(xiàng)目,我相信結(jié)果仍舊值得與愛好游戲相關(guān)技術(shù)的讀者們分享。

我打算公開該游戲的技術(shù)背景,及其如何在多種網(wǎng)絡(luò)技術(shù)基礎(chǔ)之上構(gòu)建整個(gè)項(xiàng)目。應(yīng)用在該游戲中的技術(shù)有:Node.jsexpress(靜態(tài)內(nèi)容服務(wù)),Socket.io(處理客戶端和服務(wù)器端關(guān)于小球往復(fù)運(yùn)動(dòng)的通訊),Sylvester.js(物理引擎的矢量庫)和jQuery

那什么是VeloMaze呢?VeloMaze是被許多點(diǎn)狀恐龍(迅猛龍)占據(jù)的迷宮。迅猛龍希望小球能一直在迷宮中移動(dòng)。由于迷宮的連續(xù)性,它可 以說是沒有終點(diǎn)的。但是每當(dāng)你通過一級關(guān)卡,就會(huì)給你之后的玩家造成更多麻煩,因?yàn)樗ㄋ?huì)獲得另一個(gè)小球!是不是很有趣?這就是迷宮中的生活。

這個(gè)游戲非常適合那些在同一個(gè)地方,而且每個(gè)人都有手機(jī)的團(tuán)隊(duì)。這在當(dāng)今是很常見的。這里還有一段解說游戲系統(tǒng)要求的視頻。

系統(tǒng)運(yùn)行最重要的條件就是加速計(jì)。加速計(jì)是測量加速度的設(shè)備。帶有加速計(jì)的設(shè)備通常返回重力的角度或者重力的矢量數(shù)據(jù)。這在某些瀏覽器中有可能做到,比如在下列網(wǎng)貼中所提及的:

從描述系統(tǒng)要求的視頻中可以注意到,某些筆記本電腦中也配有加速計(jì)。相當(dāng)多新式的MacBook Pro筆記本為防止跌落時(shí)造成硬件損傷也安裝了加速度計(jì)(我那臺(tái)2009年買的筆記本中就安裝著一個(gè))。我覺得以筆記本旋轉(zhuǎn)為基礎(chǔ)的游戲開發(fā)領(lǐng)域目前還是 少有人涉足的地帶!下面的圖表演示了應(yīng)用程序架構(gòu)在上層是如何搭建的。

 

游戲本身的開發(fā)相當(dāng)容易,但全面支持所有的瀏覽器和加速度計(jì)組合需要做更多的工作,而我們的小組只擁有48小時(shí)的時(shí)間。因此,有些測試我們是沒有做的,比如對最新版Android系統(tǒng)的測試;但是我驚喜的發(fā)現(xiàn),我們的游戲在其中卻運(yùn)行的非常好!然而運(yùn)氣只是成功的一部分。在下面的篇幅中,我打算解析游戲玩法的編寫,并解釋究竟怎樣使該游戲具有可玩性。

讀取加速度計(jì)數(shù)據(jù)非常簡單,不過標(biāo)準(zhǔn)的缺失使得該過程比預(yù)想的更加難以實(shí)現(xiàn)。首先,我們快速調(diào)查了小組內(nèi)現(xiàn)有的各種不同的平臺(tái)和瀏覽器組合,為適應(yīng)各種組合方式,編寫了如下代碼:

  1. /* 這里檢查游覽器是否支持DeviceOrientationEvent事件(鏈接到W3C)。*/ 
  2. if (window.DeviceOrientationEvent) { 
  3.     window.addEventListener('deviceorientation'function(e) { 
  4.       // 我們從事件“e”中獲取角度值并轉(zhuǎn)化成弧度值。 
  5.       leftRightAngle = e.gamma /90.0*Math.PI/2; 
  6.       frontBackAngle = e.beta /90.0*Math.PI/2; 
  7.     }, false); 
  8. else if (window.OrientationEvent) { //另一個(gè)選項(xiàng)是Mozilla版本同樣的東西 
  9.     window.addEventListener('MozOrientation'function(e) { 
  10.       //在這里將長度值當(dāng)做一個(gè)單位,并轉(zhuǎn)換成角度值,看起來運(yùn)行的不錯(cuò)。 
  11.       leftRightAngle = e.x * Math.PI/2; 
  12.       frontBackAngle = e.y * Math.PI/2; 
  13.     }, false); 
  14. else { 
  15.     // 自然地,沒有瀏覽器支持的大多數(shù)人會(huì)獲取這個(gè)。 
  16.     setStatus('Your device does not support orientation reading. Please use Android 4.0 or later, iOS (MBP laptop  
  17. is fine) or similar platform.'); 

結(jié)果是,代碼可以在版本較新的Chrome中正常運(yùn)行,也有人反饋說說它也可以運(yùn)行在較新版本的iOS上的Safari瀏覽器當(dāng)中(但是我手頭上的 Safari并不支持)。我決定不再試圖尋找那種能讀取所有可能用的瀏覽器中加速度計(jì)數(shù)據(jù)的普適性解決方案,因?yàn)楝F(xiàn)實(shí)是我們在Node淘汰賽的編碼環(huán)節(jié)中 個(gè)只有48小時(shí)的時(shí)間,而當(dāng)時(shí)游戲的架構(gòu)還沒有完成。

我決定使用Sylvester,它是一個(gè)碰撞檢測的向量和矩陣數(shù)學(xué)庫。其實(shí)我也可以使用Box2D JS來節(jié)省時(shí)間,但是由于有過Sylvester的使用經(jīng)驗(yàn),并且所需的碰撞檢測比較簡單,我還是決定使用Sylvester。檢查小球是否落到洞里去的代碼如下所示:

  1. function checkBallHole(ball, hole, dropped) { 
  2.     // 用Sylvester定義洞和求的位置為矢量對象 
  3.     var holeVector = $V([hole.x, hole.y]); 
  4.     var ballVector = $V([ball.x, ball.y]); 
  5.     // 在Sylvester中用向量簡單的計(jì)算距離 
  6.     if (ballVector.distanceFrom(holeVector) < hole.r) { 
  7.       // 用球的位置作為變量執(zhí)行回調(diào)函數(shù) 
  8.       dropped(ballVector); 
  9.     } 

所以事實(shí)上這里沒有什么復(fù)雜的:如果你的小球的中心位于洞內(nèi),那么就會(huì)觸發(fā)“dropped”的函數(shù)。這段代碼在每幀運(yùn)行一次,那么以前開發(fā)過游戲 的朋友都知道,這種實(shí)現(xiàn)方式可能會(huì)造成小球在這一幀內(nèi)飛躍洞穴而沒有掉進(jìn)去。然而,在日常生活中我們知道,如果你用足夠快的速度將小球推向洞穴,它是可以 滑過而不掉落的,所以這不是個(gè)問題。

這個(gè)游戲中也有墻體,所以碰撞檢測也是必須要做的。Sylvester提供了一種目標(biāo)與計(jì)算線狀對象的放發(fā),我用的就是這個(gè)。簡單的代碼如下:

  1. // 計(jì)算球和墻壁碰撞時(shí)的沖擊矢量數(shù)據(jù) 
  2. function impactBallByWall(ball, wall) { 
  3.     var ballVector = $V([ball.x, ball.y]); 
  4.     // 定義墻體為線段(x1,y1) (x2,y2) 
  5.     var wallSegment = Line.Segment.create( 
  6.                   $V([wall.sx, wall.sy]), 
  7.                   $V([wall.dx, wall.dy])); 
  8.      
  9.     // 計(jì)算墻與球的最近點(diǎn)(幾乎就要撞上的那個(gè)位置) 
  10.     var collisionPoint = wallSegment.pointClosestTo(ballVector) 
  11.                   .to2D(); // needed by sylvester to convert 3D to 2D vector 
  12.      
  13.     //sylvester將矢量數(shù)據(jù)從3D轉(zhuǎn)化成2D所需的變量,然后看這個(gè)距離在當(dāng)前框架內(nèi)為多少(并不是在兩個(gè)框架之間差距多少) 
  14.     var dist = collisionPoint.distanceFrom(ballVector); 
  15.      
  16.     //天真的假設(shè)碰撞只發(fā)生在球和墻的距離小于球的半徑的情況下 
  17.     if (dist < ball.r) { 
  18.         //調(diào)整到一個(gè)合適的值。較大的逆質(zhì)量值意味著更大的影響(和較小的質(zhì)量) 
  19.     var inverseMassSum = 1/100.0; 
  20.     //從球心到碰撞點(diǎn)的向量 
  21.     var differenceVector = collisionPoint.subtract(ballVector); 
  22.     var collisionNormal = differenceVector.multiply(1.0/dist); 
  23.     // 球陷下去的部分相當(dāng)于在墻內(nèi) 
  24.     var penetrationDistance = ball.r-dist; 
  25.     //碰撞時(shí)球的速率 
  26.     var collisionVelocity = $V([ball.vx, ball.vy]); 
  27.         // 從點(diǎn)屬性中我們獲得沖擊速度 
  28.     var impactSpeed = collisionVelocity.dot(collisionNormal); 
  29.      
  30.     if (impactSpeed >= 0) { 
  31.         // 計(jì)算沖擊量。運(yùn)動(dòng)能量在每次碰撞是以2-1-0.4=0.6的倍率遞減 
  32.         var impulse = collisionNormal.multiply( 
  33.                    (-1.4)*impactSpeed/(inverseMassSum)); 
  34.         //沖擊只會(huì)作用在球上,因?yàn)閴Ρ辉O(shè)計(jì)為固定的 
  35.         var newBallVelocity = $V([ball.vx, ball.vy]).add( 
  36.                    impulse.multiply(inverseMassSum)); 
  37.         //把值傳回原來的對象 
  38.         ball.vx = newBallVelocity.e(1); 
  39.         ball.vy = newBallVelocity.e(2); 
  40.        } 
  41.     } 

在實(shí)現(xiàn)小球和墻體的碰撞過程時(shí)我做了許多并非真實(shí)的假設(shè)(但是跟現(xiàn)實(shí)足夠接近)。首先,墻體的厚度為零(而不是實(shí)際上的5像素),而且,我沒有計(jì)算兩幀之 間發(fā)生了什么。很明顯,這會(huì)導(dǎo)致游戲中球體有能力穿越墻體。通過創(chuàng)建球體在不同幀之間的運(yùn)動(dòng)線段并找出球體三角與墻體之間是否有交叉,就很可以容易的測試 到是否會(huì)發(fā)生碰撞。那么我們就必須要計(jì)算小球和墻體發(fā)生碰撞的位置。在上文的代碼段中,這個(gè)位置數(shù)據(jù)就存在變量“collisionPoint”內(nèi)(見下圖)。

我很喜歡Ganvas和WebGL,但是我們計(jì)劃使用DOM和jQuery來做渲染,因?yàn)槲覀兂酥谱髑蝮w滾動(dòng)之外,不需要任何Ganvas和WebGL 的特效(如果這樣實(shí)現(xiàn),其實(shí)是很優(yōu)雅的,真可惜)。使用DOM渲染的場景在縮放時(shí)有點(diǎn)生硬,但它很容易實(shí)現(xiàn)。我寫了下面的函數(shù)用于繪制游戲中的子畫面。

  1.    //設(shè)置DOM元素屬性以反映sprite對象 
  2.    setElementPosition: function(element, sprite) { 
  3.        // 同步sprite維數(shù) 
  4. sprite.width = (maze.getSquareWidth() * sprite.r * 2); 
  5. sprite.height = (maze.getSquareHeigth() * sprite.r * 2); 
  6. var x = sprite.x; 
  7. var y = sprite.y; 
  8. /* 在絕對定位中計(jì)算樣式屬性left和top的值 
  9. * 從而確保點(diǎn)(x,y)在sprire的中心位置(使距離計(jì)算更加簡單) 
  10. */ 
  11. var newLeft = (x * maze.getSquareWidth()  - element.width() / 2.0); 
  12. var newTop = (y * maze.getSquareHeigth()  - element.height() / 2.0); 
  13. // 避免sprite因?yàn)槭艿絺鞲衅鞒掷m(xù)輸入的影響而產(chǎn)生的顫抖 
  14. // 通過一個(gè)閾值判斷是否顯示球在屏幕上的移動(dòng)。 
  15. // 這是一個(gè)相當(dāng)大的閾值,對于某些設(shè)備來說應(yīng)該選擇較小的值。 
  16. if (thresholded(element.css('left') - newLeft, 5) !== 0) { 
  17.     //設(shè)置DOM元素的x坐標(biāo)位置 
  18.     element.css('left', parseInt(newLeft) + 'px'); 
  19. if (thresholded(element.css('top') - newTop, 5) !== 0) { 
  20.     //設(shè)置DOM元素的y坐標(biāo)位置 
  21.     element.css('top', parseInt(newTop) + 'px'); 
  22. //設(shè)置DOM元素的大小。 
  23. element.css('width', sprite.width + 'px'); 
  24. element.css('height', sprite.height + 'px'); 
  25. // 球狀 DOM元素包含許多層(所有的div),所以重置所有層。 
  26. element.find('div').each(function () { 
  27. $(this).css('width', sprite.width + 'px'); 
  28. $(this).css('height', sprite.height + 'px'); 
  29. }); 
  30. // sprite位置的調(diào)試信息。通過點(diǎn)擊‘enter’顯示調(diào)試信息。 
  31. element.find('.location').html('('+parseInt(sprite.x*10)/10.0+','+parseInt(sprite.y*10)/10.0+')'); 
  32.    }, 

我做了一個(gè)根據(jù)視角實(shí)時(shí)縮放的功能,因此在每個(gè)框架中的寬度和高度都是計(jì)算得到的。很不幸在游戲中沒有體現(xiàn)出這點(diǎn),因?yàn)槲覀儑L試編程控制瀏覽器旋轉(zhuǎn)失敗了(沒有用于此項(xiàng)功能的接口,所以這還需要破解)。所以我們最后決定,通知用戶關(guān)閉手機(jī)瀏覽器的旋轉(zhuǎn)功能,如下圖所示:

所有的加速度計(jì)數(shù)據(jù)的讀取,物理引擎的運(yùn)行和DOM渲染都被歸攏到一個(gè)主循環(huán)中了。我將所有的主循環(huán)的代碼放置到函數(shù)“update”中并且每100毫秒運(yùn)行一次(我知道這不夠頻繁,但是它在我的設(shè)備上運(yùn)行的很好,所以就暫時(shí)忽略這個(gè)設(shè)定值吧),像這樣:

  1. window.setInterval(function() { update(); }, 100); 

客戶端的所有源代碼可以點(diǎn)擊這里獲取。

順便提一句,我對于新式的視網(wǎng)膜MacBook Pros非常失望,它沒有加速計(jì)(就像我們某位玩家提到的),因?yàn)樗鼈兊腟SD驅(qū)動(dòng)器沒有可以移動(dòng)的部件!所以也許以筆記本旋轉(zhuǎn)為基礎(chǔ)的游戲看起來要到此為止了。

責(zé)任編輯:徐川 來源: InfoQ
相關(guān)推薦

2015-07-08 16:38:10

Cocos游戲引擎

2015-09-06 09:51:02

html5開發(fā)經(jīng)驗(yàn)開發(fā)工具

2015-09-06 13:26:41

HTML5游戲開發(fā)工具手游開發(fā)

2015-09-07 14:17:44

HTML5游戲開發(fā)工具

2013-03-27 09:50:53

HTML5游戲Android開發(fā)

2012-03-29 09:18:47

HTML5WEB

2015-10-23 13:44:14

巴巴獵

2015-07-10 10:27:21

Cocos游戲開發(fā)引擎

2013-01-08 11:00:20

IBMdW

2012-05-03 14:29:53

HTML5

2016-01-05 09:39:32

HTML5游戲開發(fā)工具

2014-11-12 16:00:12

火舞游戲

2012-04-12 11:11:15

HTML5APIWEB

2012-02-14 13:50:21

ibmdw

2012-05-09 09:41:58

HTML5

2012-05-10 09:45:14

HTML5

2012-11-07 09:43:58

IBMdw

2015-07-24 17:08:24

2012-07-06 10:19:07

HTML5

2012-01-06 09:45:23

HTML5游戲開發(fā)貨幣化
點(diǎn)贊
收藏

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