JS/HTML5游戲常用算法之碰撞檢測 像素檢測算法實(shí)例詳解
本文實(shí)例講述了JS/HTML5游戲常用算法之碰撞檢測 像素檢測算法。分享給大家供大家參考,具體如下:
使用像素碰撞檢測法算是最精確的算法了,當(dāng)然,帶來的代價也是比較明顯的,那就是效率上的低下。除非是在極為特殊的情況下,要求使用非常精確的碰撞,否則,一般情況下在游戲中是不建議使用這種算法,特別是在運(yùn)行效率不太高的HTML5游戲中。
一般來說在使用像素碰撞檢測之前會使用AABB矩形包圍盒先檢測兩個精靈是否有碰撞,如果AABB包圍盒檢測沒有碰撞,那一定是沒有碰撞到,反之,則不一定,需要進(jìn)一步進(jìn)行像素檢測。如下圖所示,很明顯,雖然兩個精靈的包圍盒發(fā)生了碰撞,但兩個精靈本身沒有發(fā)生碰撞,所以在這種精靈的形狀極為不規(guī)則的情況下,除非使用多邊形包圍盒,并且需要多邊形和精靈的形狀極為接近,才能夠獲取好的效果,而且,別忘了,多邊形只適合凸多邊形的情況。

這樣,我們就只能采用像素檢測算法達(dá)到精確檢測的目的。接下來,先來看看像素碰撞的原理,首先,我們知道所有的精靈都是由像素點(diǎn)組成,而在canvas的像素數(shù)據(jù)中每個點(diǎn)都是由RGBA四個數(shù)據(jù)組成。如果某個點(diǎn)的A(alpha值,透明度)為0則表示該點(diǎn)是透明的,比如在圖6-19中兩個精靈的空白部分的點(diǎn)的A值為0。如果兩個精靈發(fā)生碰撞,則表示兩個精靈有像素點(diǎn)發(fā)生了重疊,即兩個精靈的像素點(diǎn)坐標(biāo)相同,如下圖所示。

根據(jù)這個原理,基本上我們可以采取以下步驟來進(jìn)行檢測。
(1)選擇需要檢測的兩個精靈。
(2)先檢測兩個精靈是否發(fā)生包圍盒碰撞,如果沒有則退出,否則獲取兩個矩形的相交區(qū)域,并繼續(xù)。
(3)把一個精靈繪制到和游戲屏幕等大的空白的后臺緩沖區(qū)中,獲取緩沖區(qū)中在相交區(qū)域的像素數(shù)據(jù)。
(4)清除后臺緩沖區(qū)。
(5)對另一個精靈進(jìn)行步驟3的操作。
(6)得到兩個精靈在同一個相交矩形的像素數(shù)據(jù)后,循環(huán)比較每一個像素點(diǎn),如果兩個精靈在同一位置的透明度不都是0,則表示兩個精靈有相交,退出循環(huán),返回真。
需要注意的一點(diǎn)是,在第3步獲取相交區(qū)域的像素數(shù)據(jù)中,需要在后臺另外的一個和游戲屏幕等大的空白區(qū)域中繪制,而不能直接獲取游戲畫面中的相交區(qū)域,因?yàn)閮蓚€精靈在相交區(qū)域中的像素已經(jīng)發(fā)生了重疊【包圍盒】。
由以上的算法可以看出,在進(jìn)行像素檢測的時候,需要另外一個緩沖區(qū),把需要檢測的精靈繪制一次,需要清空緩沖區(qū),最后還要使用一個for循環(huán)檢測像素。如果相交區(qū)域?yàn)?00×100個像素點(diǎn),則最壞的情況需要循環(huán)10 000 次,由此看來,這真不是一個省時的工作。
<!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>盒包圍碰撞算法-像素檢測算法</title>
<style>
#stage {
border: 1px solid lightgray;
}
</style>
</head>
<body>
<h1>包圍盒是否碰撞:<span class="hitTestBox">否</span></h1>
<h1>像素檢測是否碰撞:<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)建后臺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);
}
//緩存畫布繪制方法
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();
}
//獲取兩個矩形相交區(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ù)組碰撞將測
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)
//如果沒有相交則退出
if(rect[0]>=rect[2]||rect[1]>=rect[3]) {
} else{
document.querySelector('.hitTestBox').innerHTML = "是";
giraffeImg.data = null;
penguinImg.data = null;
//獲取精靈在相交矩形像素數(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測試運(yùn)行上述代碼,觀察運(yùn)行效果。
github地址:https://github.com/krapnikkk/JS-gameMathematics
更多關(guān)于JavaScript相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript數(shù)組操作技巧總結(jié)》、《JavaScript排序算法總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》、《JavaScript查找算法技巧總結(jié)》及《JavaScript錯誤與調(diào)試技巧總結(jié)》
希望本文所述對大家JavaScript程序設(shè)計有所幫助。
相關(guān)文章
基于javascript實(shí)現(xiàn)簡單的抽獎系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了基于javascript實(shí)現(xiàn)簡單的抽獎系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-03-03
微信小程序h5頁面跳轉(zhuǎn)小程序的超詳細(xì)講解
開發(fā)中涉及到一個需求,就是從一個預(yù)約票購買的頁面需要跳轉(zhuǎn)到?小程序,下面這篇文章主要給大家介紹了關(guān)于微信小程序h5頁面跳轉(zhuǎn)小程序的超詳細(xì)講解,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
基于BootStrap柵格欄系統(tǒng)完成網(wǎng)站底部版權(quán)信息區(qū)
網(wǎng)站底部版權(quán)信息區(qū)可以用bootstrap的“柵格系統(tǒng)”完成,下面給大家分享一個未經(jīng)處理的底部版權(quán)信息區(qū)的樣式,大家可以做個參考2016-12-12

