2016-03-15 40 views
0

所以基本上我做出這樣的決定,當屏幕上的兩個字符觸摸,並有人按下按鈕,它會帶走他們的健康。我不知道如何做的唯一的事情是如何檢測他們什麼時候觸摸。如何做像素完美的碰撞檢測兩個部分透明的圖像

$(document).ready(function(){ 


var canvas = document.createElement("canvas"); 
var context = canvas.getContext("2d"); 
canvas.width = 1000; 
canvas.height = 600; 
document.body.appendChild(canvas); 

var kGroundHeight = 500; 

/*var upKey = 38; 
var downKey = 40; 
var leftKey = 37; 
var rightKey = 39; 
*/ 


var render = function() { 
    gravity(); 
    gravity1(); 



    context.clearRect(0, 0, canvas.width, canvas.height); 

    context.fillRect(0,kGroundHeight,canvas.width,10); 

    context.drawImage(kirby, kirbyObject.x, kirbyObject.y); 

    context.drawImage(link, linkObject.x, linkObject.y); 

}; 




var main = function() { 

    render(); 
    window.requestAnimationFrame(main); 
}; 

main(); 
}); 




var linkReady = false; 
var link = new Image(); 
link.onLoad = function() { 

    linkReady = true; 
}; 


linkObject = {}; 
link.src= "https://vignette1.wikia.nocookie.net/zelda/images/1/18/Link_(Sprite)_The_Legend_of_Zelda.png/revision/latest?cb=20130117162823"; 

linkObject.x = 200; 
linkObject.y = 200; 






var keys = {}; 




$(document).keydown(function(e) { 
    console.log(e); 

    move1(e.keyCode); 

    if (keys[87] && keys[65]) { 
     linkObject.y -=50; 
     linkObject.x -=50; 


    }else if(keys[87] && keys[68]){ 

     linkObject.y -=50; 
     linkObject.x +=50; 


    }else if(keys[83] && keys[68]){ 

     linkObject.y +=50; 
     linkObject.x +=50; 


    }else if(keys[83] && keys[65]){ 

     linkObject.y +=50; 
     linkObject.x -=50; 
    } 

    keys[e.keyCode] = true; 
}).keyup(function(e) { 

    keys[e.keyCode] = false; 



}); 

var upKey1 = 87; 
var downKey1 = 83; 
var leftKey1 = 65; 
var rightKey1 = 68; 
var attackKey1 = 75; 


var move1 = function(key) { 

    if (key == upKey1) { 
    linkObject.y -= 50; 
    }else if(key == downKey1){ 

     var kGroundHeight = 500; 

    if(linkObject.y + link.height == kGroundHeight){ 

     linkObject.y +=0; 
    }else{ 

     linkObject.y +=50; 

    } 


    //console.log("down"); 
     console.log(linkObject.y); 


    }else if(key == leftKey1){ 

    linkObject.x -=50; 
    }else if(key == rightKey1){ 

    linkObject.x +=50; 
    } 
    // MORE DIRECTIONS!!! 
}; 




var gravity1 = function() { 
    var kGravityScale = 1; 
    var kGroundHeight = 500; 

    if (linkObject.y + link.height == kGroundHeight) { 

    linkObject.y += 0; 

    }else{ 
     linkObject.y += kGravityScale; 
//console.log(link.width); 
    } 
}; 


var attack = function(a){ 



}; 



var kGroundHeight = 500; 


var keys = {}; 




$(document).keydown(function(e) { 
    console.log(e); 

    move(e.keyCode); 

    if (keys[38] && keys[37]) { 
     kirbyObject.y -=50; 
     kirbyObject.x -=50; 


    }else if(keys[38] && keys[39]){ 

     kirbyObject.y -=50; 
     kirbyObject.x +=50; 


    }else if(keys[40] && keys[39]){ 

     kirbyObject.y +=50; 
     kirbyObject.x +=50; 


    }else if(keys[40] && keys[37]){ 

     kirbyObject.y +=50; 
     kirbyObject.x -=50; 
    } 

    keys[e.keyCode] = true; 
}).keyup(function(e) { 

    keys[e.keyCode] = false; 



}); 


var kirbyReady = false; 
var kirby = new Image(); 
kirby.onLoad = function() { 
    kirbyReady = true; 
}; 

kirbyObject = {}; 
kirby.src = "https://vignette3.wikia.nocookie.net/spritechronicles/images/5/5c/Kirby.png/revision/latest?cb=20101010225540"; 




kirbyObject.x = 300; 
kirbyObject.y = 100; 



var upKey = 38; 
var downKey = 40; 
var leftKey = 37; 
var rightKey = 39; 
var attackKey = 32; 



var move = function(key) { 

    if (key == upKey) { 
    kirbyObject.y -= 50; 
    }else if(key == downKey){ 

     var kGroundHeight = 500; 

    if(kirbyObject.y + kirby.height == kGroundHeight){ 

     kirbyObject.y +=0; 
    }else{ 

     kirbyObject.y +=50; 

    } 


    //console.log("down"); 
     console.log(kirbyObject.y); 


    }else if(key == leftKey){ 

    kirbyObject.x -=50; 
    }else if(key == rightKey){ 

    kirbyObject.x +=50; 
    } 
    // MORE DIRECTIONS!!! 
}; 




var gravity = function() { 
    var kGravityScale = 1; 
    var kGroundHeight = 500; 

    if (kirbyObject.y + kirby.height == kGroundHeight) { 

    kirbyObject.y += 0; 

    }else{ 
     kirbyObject.y += kGravityScale; 

    } 
}; 
+0

它們是正方形/長方形的字符,還是需要檢查不規則形狀的重疊? – Archer

+0

[Javascript碰撞檢測]的可能重複(http://stackoverflow.com/questions/7047928/javascript-collision-detection) –

+1

其他人也問過類似的問題[here](http://stackoverflow.com/questions/4871669/碰撞檢測在JavaScript的遊戲?rq = 1),[這裏](http://stackoverflow.com/questions/7047928/javascript-collision-detection?rq=1),和[這裏](http:///stackoverflow.com/questions/2440377/javascript-collision-detection)。這應該讓你開始。 –

回答

3

徑向周長測試

快速幾乎像素完美碰撞凸輪通過與一組極性協調的限定每個子畫面的形狀來實現。每個座標描述距離中心的距離(中心是任意的,但必須在精靈內部),並且從最遠的像素的中心沿着該方向描述中心的方向。座標的數量(n)由最外像素的周長確定。我不知道這是否已經被描述過,因爲我現在纔想到它,所以它的實際穩健性需要測試。

概念

下圖顯示的基本概念。

Polar collision

子畫面(1)疊加的極座標和外外接圓(2)索引,以檢查從碰撞所需的0-15(3)提取的信息的每個座標。綠色(a)是每個精靈的角度原點,黃色線P是A和B之間的矢量,標記爲角度原點(4.)

要獲得座標,您需要訪問像素信息,這可以在生產過程中完成並添加爲代碼或在遊戲設置過程中添加。這將導致數據結構類似於

var sprite = { 
    ... 
    collisionData : { 
     maxRadius : 128, 
     minRadius : 20, 
     coords : [128,30,50, ... ], 
    } 
} 

片斷1.

現在你所描述的每個精靈爲一組的極座標,你需要做的測試。

假設精靈將有一個位置(x,y以像素爲單位)一個旋轉(r以弧度表示)和一個縮放(s以方形表示)。

positionData = { // position data structure 
    x : 100,   // x pos 
    y : 100,   // y pos 
    r : Math.PI * 1.2, // rotation 
    s : 1,    // scale 
} 

片斷2.

碰撞測試

對於兩個精靈其中pA,和pB參考子畫面positionData(片段2),並cAcB參考每個精靈的collisionData (片段1)我們首先進行距離測試,檢查最大半徑和最小半徑。如果發生碰撞,代碼將返回true。

const TAU = Math.PI * 2; // use this alot so make it a constant 
var xd = pA.x - pB.x;   // get x distance 
var yd = pA.y - pB.y;   // get y distance 
var dist = Math.hypot(xd,yd); // get the distance between sprites 
           // Please note that legacy browsers will not 
           // support hypot 
// now scale the max radius of each sprite and test if the distance is less 
// than the sum of both. 
if (dist <= cA.maxRadius * pA.s + cB.maxRadius * pB.s){ 
    // passed first test sprites may be touching 
    // now check the min radius scaled 
    if (dist <= Math.min(cA.minRadius * pA.s, cB.minRadius * pB.s) * 2){ 
      // the sprites are closer than the smallest of the two's min 
      // radius scaled so must be touching 
      return true; // all done return true 
    } 

片段3.

現在,你需要做的極地測試。您需要從每個精靈獲取方向,然後調整該方向以匹配精靈旋轉,然後將方向標準化爲存儲在collisionData中的極座標數量。

// on from snippet 3. 
    var dir = Math.atan2(yd, xd); // get the direction from A to B in radians 
            // please note that y comes first in atan2 

    // now subtract the rotation of each sprite from the directions 
    var dirA = dir - pA.r; 
    var dirB = dir + Math.PI - pB.r; // B's direction is opposite 

    // now normalise the directions so they are in the range 0 - 1; 
    dirA = (((dirA % TAU) + TAU) % TAU)/TAU;     
    dirB = (((dirB % TAU) + TAU) % TAU)/TAU;   

下一步的歸一化相對方向轉換到正確的索引極性陣列中需要考慮到每個極座標的角寬度。在圖中,每個極座標頂部的扁平位是角寬度。要做到這一點,我們使用Math.round時,從0-1擴展到座標數。由於接近1的值將舍入到錯誤的索引,所以您還必須使用模%來確保它不會超出範圍。

var indexA = Math.round(dirA * cA.coords.length) % cA.coords.length; 
    var indexB = Math.round(dirB * cB.coords.length) % cB.coords.length; 

    // now we can get the length of the coordinates. 
    // also scale them at the same time 
    var la = cA.coords[indexA] * pA.s; 
    var lb = cB.coords[indexB] * pB.s; 

    // now test if the distance between the sprites is less than the sum 
    // of the two length 
    if(dist <= la + lb){ 
     // yes the two are touching 
     return true; 
    } 
} 

注意事項

所以這是它。與其他方法相比,它相對較快,但它不是像素完美的,因爲它只考慮精靈的周長,並且如果精靈周長不是凸起的,則可能會出現算法返回的結果不正確的情況。

如果精靈非常粗糙,可能會出現碰撞未能檢測到碰撞的情況,這同樣適用於對座標數進行過採樣。該圖顯示了一個接近128乘128的精靈的16個座標,這似乎是一個好數字。大精靈將需要更小的少,但是在8

不去改進

在圖中圖4顯示了一個小姐,但如果精靈的B,從它們之間的方向的座標15(一個順時針)稍微長一些,會有一個未被發現的命中。爲了改進算法,您可以測試兩邊的索引座標與距離,但是您需要減小極座標距離,以考慮它們不指向另一個精靈的中心。通過將極距乘以偏移角的cos來做到這一點。

// get the angle step for A and B 
var angleStepA = TAU/cA.coord.length; 
var angleStepB = TAU/cB.coord.length; 

// the number of coordinates to offset 
var offCount = 1; 

// get next coord clockwise from A and scale it 
var lengOffA = cA.coord[(index + offCount) % cA.coord.length] * pA.s; 

// get next coordinate counter clockwise from B and scale it 
var lengOffB = cB.coord[(index + cB.coord.length - offCount) % cB.coord.length] * pB.s; 

// Now correct for the offest angle 
lengOffA *= Math.cos(offCount * angleStepA); 
lengOffB *= Math.cos(offCount * angleStepB); 

// Note that as you move away the length will end up being negative because 
// the coord will point away. 

if(dist < lengOffA + lengOffB){ 
    // yes a hit 
    return true; 
} 

這增加了一點處理的算法,但並不多,只能是儘可能的相比B.

您也不妨作之間的區域一半的角大小極座標爲線性斜率並插值第一次測試的極距。

還有更多的在做測試,並且使用將取決於需求的方法途徑。如果你只有幾個精靈,那麼可以使用更復雜的算法來提供更好的結果,如果你有很多精靈,那麼使用更簡單的測試。

不要過度做飯。

請記住,像素完美測試是程序員想要的,但玩家幾乎不能辨別像素(現代顯示器的像素小於人眼的徑向分辨率)。如果兩個幾乎看不見的像素碰觸1/60秒,誰會看到這是一個打擊?

+0

還沒有檢查代碼,但這是我會建議的方法。 –

+0

嗯......在樣本角度計算徑向極值(距離原點最遠的一個填充像素),並使用這些來測試碰撞。您已經提出了一個非常有趣的解決方案!有趣的是,它允許轉換,也允許非中心的起源。熱心的Upvote!你打算做一個概念驗證,你會檢查限制邊緣情況嗎?如果是,請告知我們。再一次...好的大腦工作! – markE

+1

@markE謝謝,當我有機會時,我會有一個更詳細的外觀,一個重要的mod可以適應非統一的尺度和動畫精靈。完成後肯定會做一個自己的答案。 – Blindman67

0

您可以通過兩個步驟完成此操作。

  1. 檢查重疊的邊界圓。

  2. 如果圓圈重疊,請檢查(更精確)邊界矩形重疊。

如果您有兩張圖片,那麼每張圖片的中心等於(x + w/2), (y + h/2)。它們各自的半徑等於sqrt((w/2)^2 + (h/2)^2)

和兩個圓圈重疊時它們之間的距離(sqrt((x1-x2)^2 + (y1-y2)^2))小於max(radius1, radius2)

邊界矩形碰撞檢測是直觀的,但計算可能會更困難(可能會影響大量的對象)。