JS/HTML5游戲常用算法之碰撞檢測(cè) 像素檢測(cè)算法實(shí)例詳解
本文實(shí)例講述了JS/HTML5游戲常用算法之碰撞檢測(cè) 像素檢測(cè)算法。分享給大家供大家參考,具體如下:
使用像素碰撞檢測(cè)法算是最精確的算法了,當(dāng)然,帶來(lái)的代價(jià)也是比較明顯的,那就是效率上的低下。除非是在極為特殊的情況下,要求使用非常精確的碰撞,否則,一般情況下在游戲中是不建議使用這種算法,特別是在運(yùn)行效率不太高的HTML5游戲中。
一般來(lái)說(shuō)在使用像素碰撞檢測(cè)之前會(huì)使用AABB矩形包圍盒先檢測(cè)兩個(gè)精靈是否有碰撞,如果AABB包圍盒檢測(cè)沒(méi)有碰撞,那一定是沒(méi)有碰撞到,反之,則不一定,需要進(jìn)一步進(jìn)行像素檢測(cè)。如下圖所示,很明顯,雖然兩個(gè)精靈的包圍盒發(fā)生了碰撞,但兩個(gè)精靈本身沒(méi)有發(fā)生碰撞,所以在這種精靈的形狀極為不規(guī)則的情況下,除非使用多邊形包圍盒,并且需要多邊形和精靈的形狀極為接近,才能夠獲取好的效果,而且,別忘了,多邊形只適合凸多邊形的情況。
這樣,我們就只能采用像素檢測(cè)算法達(dá)到精確檢測(cè)的目的。接下來(lái),先來(lái)看看像素碰撞的原理,首先,我們知道所有的精靈都是由像素點(diǎn)組成,而在canvas的像素?cái)?shù)據(jù)中每個(gè)點(diǎn)都是由RGBA四個(gè)數(shù)據(jù)組成。如果某個(gè)點(diǎn)的A(alpha值,透明度)為0則表示該點(diǎn)是透明的,比如在圖6-19中兩個(gè)精靈的空白部分的點(diǎn)的A值為0。如果兩個(gè)精靈發(fā)生碰撞,則表示兩個(gè)精靈有像素點(diǎn)發(fā)生了重疊,即兩個(gè)精靈的像素點(diǎn)坐標(biāo)相同,如下圖所示。
根據(jù)這個(gè)原理,基本上我們可以采取以下步驟來(lái)進(jìn)行檢測(cè)。
(1)選擇需要檢測(cè)的兩個(gè)精靈。
(2)先檢測(cè)兩個(gè)精靈是否發(fā)生包圍盒碰撞,如果沒(méi)有則退出,否則獲取兩個(gè)矩形的相交區(qū)域,并繼續(xù)。
(3)把一個(gè)精靈繪制到和游戲屏幕等大的空白的后臺(tái)緩沖區(qū)中,獲取緩沖區(qū)中在相交區(qū)域的像素?cái)?shù)據(jù)。
(4)清除后臺(tái)緩沖區(qū)。
(5)對(duì)另一個(gè)精靈進(jìn)行步驟3的操作。
(6)得到兩個(gè)精靈在同一個(gè)相交矩形的像素?cái)?shù)據(jù)后,循環(huán)比較每一個(gè)像素點(diǎn),如果兩個(gè)精靈在同一位置的透明度不都是0,則表示兩個(gè)精靈有相交,退出循環(huán),返回真。
需要注意的一點(diǎn)是,在第3步獲取相交區(qū)域的像素?cái)?shù)據(jù)中,需要在后臺(tái)另外的一個(gè)和游戲屏幕等大的空白區(qū)域中繪制,而不能直接獲取游戲畫(huà)面中的相交區(qū)域,因?yàn)閮蓚€(gè)精靈在相交區(qū)域中的像素已經(jīng)發(fā)生了重疊【包圍盒】。
由以上的算法可以看出,在進(jìn)行像素檢測(cè)的時(shí)候,需要另外一個(gè)緩沖區(qū),把需要檢測(cè)的精靈繪制一次,需要清空緩沖區(qū),最后還要使用一個(gè)for循環(huán)檢測(cè)像素。如果相交區(qū)域?yàn)?00×100個(gè)像素點(diǎn),則最壞的情況需要循環(huán)10 000 次,由此看來(lái),這真不是一個(gè)省時(shí)的工作。
<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta charset="UTF-8"> <title>盒包圍碰撞算法-像素檢測(cè)算法</title> <style> #stage { border: 1px solid lightgray; } </style> </head> <body> <h1>包圍盒是否碰撞:<span class="hitTestBox">否</span></h1> <h1>像素檢測(cè)是否碰撞:<span class="hitTestPixel">否</span></h1> <canvas id="stage"></canvas> <img src="./images/penguin.png" alt="penguinImg" id="penguinImg" style="display:none" /> <img src="./images/giraffe.png" alt="giraffeImg" id="giraffeImg" style="display:none" /> </body> <script> window.onload = function () { var stage = document.querySelector('#stage'), ctx = stage.getContext('2d'); stage.width = 600; stage.height = 600; //創(chuàng)建后臺(tái)canvas var bC = document.createElement("canvas"); bC.width = stage.width; bC.height = stage.height; var backBuf = bC.getContext("2d"); var penguin = document.querySelector('#penguinImg'),giraffe = document.querySelector('#giraffeImg'); var penguinImg = { x:stage.width/2, y:stage.height/2, r:128, data:null },giraffeImg = { x:100, y:100, r:128, data:null }; function drawImageBox(img,x,y,width,height){ ctx.beginPath(); ctx.rect(x,y,width,height); ctx.stroke(); ctx.drawImage(img,x,y,width,height); } //緩存畫(huà)布繪制方法 function drawImageBC(img,x,y,width,height){ backBuf.clearRect(0,0,stage.width,stage.height); backBuf.save(); backBuf.drawImage(img,x,y,width,height); backBuf.restore(); } //獲取兩個(gè)矩形相交區(qū)域 function getInRect(x1,y1,x2,y2,x3,y3,x4,y4) { return [Math.max(x1,x3),Math.max(y1,y3),Math.min(x2,x4),Math.min(y2,y4)]; } document.onkeydown = function (event) { var e = event || window.event || arguments.callee.caller.arguments[0]; //根據(jù)地圖數(shù)組碰撞將測(cè) switch (e.keyCode) { case 37: console.log("Left"); if (penguinImg.x > 0) { penguinImg.x -= 2; } break; case 38: console.log("Top"); if (penguinImg.y > 0) { penguinImg.y -= 2; } break; case 39: console.log("Right"); if (penguinImg.x < stage.width) { penguinImg.x += 2; } break; case 40: console.log("Bottom"); if (penguinImg.y < stage.height) { penguinImg.y += 2; } break; default: return false; } }; stage.addEventListener('click', function (event) { var x = event.clientX - stage.getBoundingClientRect().left; var y = event.clientY - stage.getBoundingClientRect().top; penguinImg.x = x; penguinImg.y = y; }); function update() { ctx.clearRect(0, 0, 600, 600); drawImageBox(giraffe,giraffeImg.x,giraffeImg.y,giraffeImg.r,giraffeImg.r); drawImageBox(penguin,penguinImg.x,penguinImg.y,penguinImg.r,penguinImg.r); document.querySelector('.hitTestBox').innerHTML = "否"; document.querySelector('.hitTestPixel').innerHTML = "否"; var rect = getInRect(giraffeImg.x,giraffeImg.y,giraffeImg.x+giraffeImg.r,giraffeImg.y+giraffeImg.r,penguinImg.x,penguinImg.y,penguinImg.x+penguinImg.r,penguinImg.y+penguinImg.r) //如果沒(méi)有相交則退出 if(rect[0]>=rect[2]||rect[1]>=rect[3]) { } else{ document.querySelector('.hitTestBox').innerHTML = "是"; giraffeImg.data = null; penguinImg.data = null; //獲取精靈在相交矩形像素?cái)?shù)據(jù) drawImageBC(giraffe,giraffeImg.x,giraffeImg.y,giraffeImg.r,giraffeImg.r); giraffeImg.data = backBuf.getImageData(rect[0],rect[1],rect[2],rect[3]).data; drawImageBC(penguin,penguinImg.x,penguinImg.y,penguinImg.r,penguinImg.r); penguinImg.data = backBuf.getImageData(rect[0],rect[1],rect[2],rect[3]).data; for(var i=3;i<giraffeImg.data.length;i+=4) { if(giraffeImg.data[i]>0&&penguinImg.data[i]>0){ document.querySelector('.hitTestPixel').innerHTML = "是"; } } } requestAnimationFrame(update); } update(); }; </script> </html>
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測(cè)試運(yùn)行上述代碼,觀察運(yùn)行效果。
github地址:https://github.com/krapnikkk/JS-gameMathematics
更多關(guān)于JavaScript相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript數(shù)組操作技巧總結(jié)》、《JavaScript排序算法總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》、《JavaScript查找算法技巧總結(jié)》及《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》
希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。
相關(guān)文章
JavaScript實(shí)現(xiàn)點(diǎn)贊功能的示例
本文主要介紹了JavaScript實(shí)現(xiàn)點(diǎn)贊功能的示例,分享給大家2014-04-04Javascript:為input設(shè)置readOnly屬性(示例講解)
本篇文章主要是對(duì)Javascript中為input設(shè)置readOnly屬性的示例代碼進(jìn)行了介紹。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12實(shí)現(xiàn)非常簡(jiǎn)單的js雙向數(shù)據(jù)綁定
Angular實(shí)現(xiàn)了雙向綁定機(jī)制。所謂的雙向綁定,無(wú)非是從界面的操作能實(shí)時(shí)反映到數(shù)據(jù),數(shù)據(jù)的變更能實(shí)時(shí)展現(xiàn)到界面。本文給大家詳細(xì)介紹js雙向數(shù)據(jù)綁定,感興趣的朋友參考下2015-11-11使用javascript將時(shí)間轉(zhuǎn)換成今天,昨天,前天等格式
這篇文章主要介紹了使用javascript將時(shí)間轉(zhuǎn)換成今天,昨天,前天等格式的相關(guān)資料,需要的朋友可以參考下2015-06-06Javascript? Constructor構(gòu)造器模式與Module模塊模式
這篇文章主要介紹了Javascript? Constructor構(gòu)造器模式與Module模塊模式,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08淺析$.getJSON異步請(qǐng)求和同步請(qǐng)求
下面小編就為大家?guī)?lái)一篇淺析$.getJSON異步請(qǐng)求和同步請(qǐng)求。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06JS操作時(shí)間 - UNIX時(shí)間戳的簡(jiǎn)單介紹(必看篇)
下面小編就為大家?guī)?lái)一篇JS操作時(shí)間 - UNIX時(shí)間戳的簡(jiǎn)單介紹(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08js字符串轉(zhuǎn)json的三種簡(jiǎn)單方法舉例
在很多時(shí)候我們都需要將數(shù)據(jù)轉(zhuǎn)換為JSON的格式去進(jìn)行請(qǐng)求,下面這篇文章主要給大家介紹了關(guān)于js字符串轉(zhuǎn)json的三種簡(jiǎn)單方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04