學習 PixiJS — 碰撞檢測

FEWY發表於2019-02-25

說明

碰撞檢測,用來檢查兩個精靈是否接觸。

Pixi 沒有內建的碰撞檢測系統, 所以這裡我們使用一個名為 Bump 的庫,Bump 是一個易於使用的2D碰撞方法的輕量級庫,可與 Pixi 渲染引擎一起使用。它提供了製作大多數2D動作遊戲所需的所有碰撞工具。

使用 Bump

要開始使用 Bump,首先直接用 script 標籤,引入 js 檔案

<script src="https://www.kkkk1000.com/js/bump.js"></script>
複製程式碼

然後建立它的例項

let b = new Bump(PIXI);
複製程式碼

變數 b 現在代表 Bump 例項。可以使用它來訪問 Bump 的所有碰撞方法。

使用 Bump 的碰撞方法

hit

hit 方法是一種通用碰撞檢測功能。它會自動檢測碰撞中使用的精靈種類,並選擇適當的碰撞方法。這意味著你不必記住要使用 Bump 庫中的許多碰撞方法的哪一個,你只需要記住一個 hit 。但是為了避免 hit 方法最後產生的效果和你想象的不一樣,最好還是要了解一下 Bump 庫中其他的方法。

以下是 hit 方法最簡單的使用形式:

b.hit(sprite1, sprite2);
複製程式碼

如果兩個精靈碰撞到了,就返回 true,沒有碰撞到,則返回 false。

檢視示例

在碰撞檢測時,Bump 的方法預設精靈是矩形的,使用矩形碰撞檢測的演算法,如果你想讓方法把一個精靈當做圓形,使用圓形碰撞檢測的演算法,需要將精靈的 circular 屬性設定為 true 。

anySprite.circular = true;
複製程式碼

如果你使用 hit 方法檢測兩個圓形精靈是否碰撞,你還需要將兩個精靈的 diameter 屬性設定為 true 。

檢視示例

如果你希望精靈對碰撞作出反應,使它們不重疊,請將第三個引數設定為 true 。

b.hit(sprite1, sprite2, true);
複製程式碼

這個防止重疊的功能,對於製作牆壁,地板或任何其他型別的邊界非常有用。

檢視示例

如果你想讓精靈碰撞後反彈,請將第四個引數設定為 true。

b.hit(sprite1, sprite2, true, true);
複製程式碼

注意:
如果需要精靈反彈,精靈還必須有速度屬性,也就是 vx 和 vy 屬性。

檢視示例

設定第五個引數為 true 使 hit 方法使用精靈的全域性座標。在檢測不同父容器的精靈之間的碰撞時,這很有用。

b.hit(sprite1, sprite2, true, true, true);
複製程式碼

精靈的全域性座標是相對於畫布左上角的位置。 精靈的區域性座標是相對於其父容器的左上角的位置。

如果要檢查點物件是否與精靈碰撞,將點物件作為第一個引數,如下所示:

b.hit({x: 200, y:120}, sprite);
複製程式碼

點物件是一個具有 x 和 y 兩個屬性的物件,x 和 y 表示了畫布中一個點的座標。

檢視示例

hit 方法還允許你檢查精靈和精靈組之間的碰撞。只需將精靈組作為第二個引數即可。在此示例中,精靈組是 spriteArray。

b.hit(sprite, spriteArray, true, true, true);
複製程式碼

你將看到 hit 方法自動遍歷精靈組中的所有精靈,並根據引數中的第一個精靈檢測它們。這意味著你不必自己編寫 for 迴圈或 forEach 迴圈。

檢視示例

你還可以使用回撥函式作為第六個引數。這對於檢查單個精靈和精靈組之間的碰撞特別有用。如果發生碰撞,回撥函式將執行,你可以訪問碰撞返回值和碰撞中涉及的精靈。下面是如何使用這個特性來檢測一個名為 sprite 的精靈和一個名為 spriteArray 的精靈組之間的碰撞。

b.hit(
    sprite,
    spriteArray,
    true, true, true,
    function (collision, platform) {
        //collision 表示 sprite 的哪一邊發生碰撞
        //platform 表示 sprite 正在碰撞的精靈組中的精靈
        console.log(collision);
        console.log(platform);
    }
);
複製程式碼

這是一種執行復雜碰撞檢測的簡潔方式,可以為你提供大量資訊和低階控制,但不必手動遍歷陣列中的所有精靈。

檢視示例

hit 方法的返回值會與你正在檢查的精靈的種類相匹配。例如,如果兩個精靈都是矩形,並且 hit 方法的第三個引數是 true,碰撞後,返回值表示引數中第一個矩形發生碰撞的一側,如果沒有發生碰撞,返回值就是 undefined 。

示例:

let collision = b.hit(rectangleOne, rectangleTwo, true);
message.text = "引數中第一個矩形的碰撞側是: " + collision;
複製程式碼

檢視示例

hit 方法只是 Bump 的許多低階碰撞方法的高階包裝器。如果你更喜歡使用較低階別的方法,接下來會列出所有的這些方法。

hitTestPoint

最基本的碰撞檢測是檢查點物件是否與精靈碰撞。hitTestPoint 方法將幫助你解決這個問題。
hitTestPoint 方法需要兩個引數:

名稱 描述
point 具有 x 和 y 屬性的點物件,x 和 y 表示了畫布中一個點的座標
sprite 精靈

示例:

let collision = b.hitTestPoint(
    { x: 180, y: 128 },  //具有 x 和 y 屬性的點物件 
    sprite          //需要檢測的精靈
)
複製程式碼

如果點物件與精靈碰撞,hitTestPoint 方法返回 true,否則返回 false。

檢視示例

上面示例中的精靈被當作是矩形的,但 hitTestPoint 方法同樣適用於圓形精靈。如果精靈具有 radius 屬性,則 hitTestPoint 方法假定精靈是圓形的並且對它應用圓形碰撞檢測演算法。如果精靈沒有 radius 屬性,則該方法假定它是矩形。你可以給任何精靈一個 radius 屬性。而一個更簡單的方法是給精靈一個 circular 屬性並將其設定為 true 。

anySprite.circular = true;
複製程式碼

這樣精靈就會應用圓形碰撞檢測演算法,並具有一個 radius 屬性,該屬性的值等於精靈寬度的一半。

檢視示例

hitTestCircle

hitTestCircle 方法用來檢測兩個圓形精靈之間的碰撞。

b.hitTestCircle(sprite1,sprite2)
複製程式碼

作為引數傳入 hitTestCircle 方法的精靈需要有 radius 屬性,如果精靈碰撞則返回 true,因此你可以將其與 if 語句一起使用來檢測碰撞,如下所示:

if(b.hitTestCircle(sprite1,sprite2)){
	message.text = "碰撞到了!";
	//碰撞到後,將 vx 設定為0,停止移動
	sprite1.vx=0;
}
複製程式碼

檢視示例

circleCollision

當移動的圓形精靈碰到沒有移動的圓形精靈時,你可以使用 circleCollision 方法建立碰撞反應。

引數:

名稱 預設值 描述
circle1 移動的圓形精靈
circle2 沒有移動的圓形精靈
bounce false 用於確定第一個精靈碰撞到第二個精靈時是否應該反彈
global false 是否使用精靈的全域性座標。如果要檢測具有不同父容器的精靈之間的碰撞 ,這很有用

注意: 如果你希望引數中第一個精靈碰撞到第二個精靈時反彈,那第一個精靈必須有速度屬性,也就是 vx 和 vy 屬性。

檢視示例

movingCircleCollision

movingCircleCollision 方法可以讓兩個移動的圓形精靈在碰撞時彈開,它們會以一種非常逼真的方式將速度傳遞給對方,從而使它們彈開。

引數:

名稱 預設值 描述
circle1 移動的圓形精靈
circle2 移動的圓形精靈
global false 是否使用精靈的全域性座標。如果要檢測具有不同父容器的精靈之間的碰撞 ,
b.movingCircleCollision(circle1, circle2)
複製程式碼

如果圓形精靈具有 mass 屬性,則該值將用於幫助確定圓形精靈應該相互反彈的力。

檢視示例

如果你有一堆移動的圓形精靈,你希望這些精靈都在碰撞後進行反彈,這個時候你需要把這些精靈進行兩兩檢查,判斷它們是否碰撞,這需要把這些精靈放在一個陣列中,使用兩層 for 迴圈,並且內層 for 迴圈的計數器比外層的 for 迴圈大1,這樣就可以檢測所有圓形精靈的碰撞情況。

for (let i = 0; i < container.children.length; i++) {
  //碰撞檢查中使用的第一個圓形精靈
  var c1 = container.children[i];
  for (let j = i + 1; j < container.children.length; j++) {
	//碰撞檢查中使用的第二個圓形精靈
	let c2 = container.children[j];
	//檢查碰撞情況,如果精靈發生碰撞,將精靈彈開
	b.movingCircleCollision(c1, c2);
  }
}
複製程式碼

你可以看到內層 for 迴圈的計數器開始就是一個大於外層 for 迴圈的數字:

let j = i + 1
複製程式碼

這可以防止對任何一對精靈進行多次碰撞檢測。

Bump 庫還有一個方便的方法 multipleCircleCollision,使用這個方法可以替代 for 迴圈的方式。這個方法會對每對精靈自動呼叫 movingCircleCollision,使它們互相反彈。 你可以在遊戲迴圈中使用它來檢查陣列中的所有精靈,但是要注意陣列中的精靈是不能重複的。

示例:

b.multipleCircleCollision(container.children);
複製程式碼

檢視示例

hitTestRectangle

要確定兩個矩形精靈是否碰撞,請使用 hitTestRectangle 方法:

b.hitTestRectangle(rectangle1, rectangle2)
複製程式碼

如果矩形精靈碰撞,hitTestRectangletrue 方法返回 true,沒有碰撞則返回 false。

示例:

if(b.hitTestRectangle(sprite1,sprite2)){
	message.text = "碰撞到了!";
}else{
	message.text = "沒有碰到";
}
複製程式碼

檢視示例

rectangleCollision

rectangleCollision 方法使矩形精靈表現得好像它們有質量。它可以防止引數中的兩個矩形精靈重疊。

引數:

名稱 預設值 描述
rectangle1 矩形精靈
rectangle2 矩形精靈
bounce false 用於確定第一個精靈是否應該從第二個精靈反彈
global true 是否使用精靈的全域性座標

返回值:

如果精靈碰撞到了,rectangleCollision 方法返回一個字串值,告訴你第一個矩形精靈的哪一側碰到了第二個矩形精靈。其值可能是 leftrighttopbottom 。如果沒有碰撞到返回值就是 undefined

示例:

let collision = b.rectangleCollision(sprite2, sprite1);

//碰撞發生在矩形1(第一個引數)的哪一側
switch (collision) {
    case "left":
        message.text = "引數中的第一個精靈的 左側 發生碰撞";
        break;
    case "right":
        message.text = "引數中的第一個精靈的 右側 發生碰撞";
        break;
    case "top":
        message.text = "引數中的第一個精靈的 上方 發生碰撞";
        break;
    case "bottom":
        message.text = "引數中的第一個精靈的 下方 發生碰撞";
        break;
    default:
        message.text = "沒有發生碰撞";
}
複製程式碼

此示例程式碼將阻止矩形重疊,並在名為 message 的文字精靈中顯示碰撞側。

rectangleCollision 方法具有非常有用的副作用。引數中的第二個精靈能夠將第一個精靈推走。如果你需要類似於推箱子游戲中的那種功能,這會很有用。

檢視示例

hitTestCircleRectangle

hitTestCircleRectangle 方法可以檢查圓形和矩形精靈之間的碰撞。

引數:

名稱 預設值 描述
circle 圓形精靈
rectangle 矩形精靈
global false 是否使用精靈的全域性座標

返回值:

如果精靈碰撞到了,hitTestCircleRectangle 方法同樣返回一個字串值,告訴你圓形精靈在哪裡碰到了矩形精靈。其值可能是 topLefttopMiddletopRightleftMiddlerightMiddlebottomLeftbottomMiddlebottomRight 。如果沒有碰撞到返回值就是 undefined

示例:

let collision = b.hitTestCircleRectangle(circle, rectangle);
if (collision) {
    message.text = "圓形精靈的 " + collision + " 側,發生碰撞";
} else {
    message.text = "沒有發生碰撞";
}
複製程式碼

檢視示例

circleRectangleCollision

使用 circleRectangleCollision 方法讓一個圓形精靈從矩形精靈的側面或角反彈。

引數:

名稱 預設值 描述
circle 圓形精靈
rectangle 矩形精靈
bounce false 是否使使精靈反彈
global false 是否使用精靈的全域性座標

示例:

b.circleRectangleCollision(circle, rectangle, true);
複製程式碼

檢視示例

contain

contain 方法可以將精靈限制在一定矩形區域內。

引數:

名稱 預設值 描述
sprite 精靈
container 容器,這是一個物件,具有 x、y、width 和 height 屬性,表示一個矩形區域。
bounce false 確定精靈在碰到容器邊界時是否應該反彈。
callbackFunction 回撥函式,當精靈碰撞到容器邊界時會呼叫它,並且會將 contain 方法的返回值作為引數傳入這個回撥函式。

返回值:

如果精靈碰撞到容器邊界,contain 方法將返回一個 Set 物件,告訴你精靈撞到了哪一側,它的值可能有 leftrighttopbottom ,如果精靈沒有碰撞到容器邊界, 返回值就是 undefined

示例:

let collision = b.contain(sprite, { x: 0, y: 0, width: 512, height: 512 }, true, callbackFunction);

//發生碰撞時的回撥函式
function callbackFunction(collision) {
    console.log("collision", collision);
}

//如果發生碰撞,顯示哪邊的邊界發生碰撞
if (collision) {
    if (collision.has("left")) {
        message.text = "邊界 左側 發生碰撞";
    };
    if (collision.has("right")) {
        message.text = "邊界 右側 發生碰撞";
    };
    if (collision.has("top")) {
        message.text = "邊界 上方 發生碰撞";
    };
    if (collision.has("bottom")) {
        message.text = "邊界 下方 發生碰撞";
    };
}
複製程式碼

上面的程式碼會將精靈限制在物件定義的512 x 512畫素區域內。如果精靈碰撞到容器的邊界,它將會反彈, 並且顯示碰到了哪邊的邊界,callbackFunction(第四個引數)也將執行。

檢視示例

contain 方法的另一個特點是,如果精靈具有 mass 屬性,該值將用於以非常自然的方式抑制精靈的反彈。

注意:

使用 Bump 庫時,最好給精靈設定上速度屬性(vx,vy),因為 Bump 庫中許多方法實現效果時,都需要用到這個兩個屬性。

上一篇 學習 PixiJS — 補間動畫

下一篇 學習 PixiJS — 互動工具

相關文章