1. 程式人生 > 實用技巧 >HTML5 GAME TUTORIAL(六): Collision detection and physics(譯)

HTML5 GAME TUTORIAL(六): Collision detection and physics(譯)

原文地址:Collision detection and physics

使用JavaScript執行碰撞檢測併產生物理反應。檢查圖形之間是否重疊,應用Hitbox並計算新速度。通過物體的質量,重力和恢復力使其更自然。 在本教程結束時,您將在遊戲中執行基本的物理模擬。

建立一些移動的物件

如果您已經知道如何建立運動物件,並且只對檢測碰撞或物理感興趣,請向下滾動至下一部分

在檢測運動物體之間的碰撞之前,首先需要一些物體。 在上一教程中,您學習瞭如何移動單個矩形。讓我們擴充套件這種邏輯,並建立一堆移動物件來填充遊戲。首先,定義一種新型的遊戲物件。這將是一個簡單的正方形。

class Square extends GameObject
{
    // Set default width and height
    static width = 50;
    static height = 50;

    constructor (context, x, y, vx, vy){
        super(context, x, y, vx, vy);
    }

    draw(){
        // Draw a simple square
        this.context.fillStyle = this.isColliding?'#ff8080':'#0099b0';
        this.context.fillRect(this.x, this.y, Square.width, Square.height);
    }

    update(secondsPassed){
        // Move with set velocity
        this.x += this.vx * secondsPassed;
        this.y += this.vy * secondsPassed;
    }
}

這段程式碼可能看起來有點熟悉。 就像上一教程中一樣,有一個draw()和update()函式。這次,它被提取到一個單獨的正方形class中。這樣,您可以建立一個正方形的許多例項,並且它們都使用相同的邏輯進行繪製和更新。您將很容易管理正方形的行為和外觀。

對該新類中的fillStyle進行了一些調整。當該物件碰撞時,它將顏色從藍色更改為紅色。當檢測到第一個碰撞時,您將看到此動作。目前,所有方塊均為藍色。

所有方塊都繼承自GameObject類。每個遊戲物件都有一個位置和速度。這使您可以輕鬆建立新型的遊戲物件。他們繼承了GameObject類的屬性和方法。正方形只是一個例子,但是您也可以通過這種方式為遊戲製作敵人或玩家之類的物件。

class GameObject
{
    constructor (context, x, y, vx, vy){
        this.context = context;
        this.x = x;
        this.y = y;
        this.vx = vx;
        this.vy = vy;

        this.isColliding = false;
    }
}

您可以使用new關鍵字建立類的新例項。使用此createWorld()函式建立一些正方形以填充您的遊戲世界。

let gameObjects;

function createWorld(){
    gameObjects = [
        new Square(context, 250, 50, 0, 50),
        new Square(context, 250, 300, 0, -50),
        new Square(context, 150, 0, 50, 50),
        new Square(context, 250, 150, 50, 50),
        new Square(context, 350, 75, -50, 50),
        new Square(context, 300, 300, 50, -50)
    ];
}

在函式中,建立了一堆正方形。他們將位置和速度作為引數傳遞給他們。目前,此函式是不會變化的,但是您可以輕鬆地對其進行修改以建立更多隨機的正方形或使用某些生成演算法。

現在一切就緒,可以繪製正方形。使用以下程式碼更新遊戲迴圈,以遍歷新建立的遊戲物件並將它們繪製在螢幕上。

function gameLoop(timeStamp)
{
    secondsPassed = (timeStamp - oldTimeStamp) / 1000;
    oldTimeStamp = timeStamp;

    // Loop over all game objects
    for (let i = 0; i < gameObjects.length; i++) {
        gameObjects[i].update(secondsPassed);
    }

    clearCanvas();

    // Do the same to draw
    for (let i = 0; i < gameObjects.length; i++) {
        gameObjects[i].draw();
    }

    window.requestAnimationFrame(gameLoop);
}

如您所見,update()和draw()不再每次迭代呼叫一次。螢幕上的每個物件,每次迭代都呼叫一次。

這樣,update()和draw()的實現是特定於物件的。對於遊戲迴圈,您嘗試繪製哪種物件都沒有關係,只要它們具有update()和draw()函式即可。

對於您正在使用的正方形,它將繪製一個簡單的正方形並將其沿直線移動。但是,請想象其他型別的物件,它們具有兩個功能的自己的實現,並且具有自己的行為和外觀。這個遊戲迴圈可以處理它。

順便說一句,您是否注意到這些新類中缺少“use strict”這一行?這是因為預設情況下,使用class關鍵字定義的類是嚴格的。因此,無需在這些類中專門新增“use strict”。

看一下結果:

[canvas]

您可以看到現在繪製了一堆矩形。他們每個人都有自己的出發位置,並朝著不同的方向前進。就像在createWorld()函式中定義的一樣。您可以調整變數以建立新的正方形型別。

為什麼需要碰撞檢測?

正方形的運動可能重疊,但是目前並沒有太大作用。如果正方形可以相互作用並表現得像實際的固體物件並彼此反彈,那將很酷。為了做到這一點,他們必須首先知道自己正在相互碰撞。這就是碰撞檢測的用武之地。

碰撞檢測是一種檢測兩個物件是否彼此碰撞的技術,或者是從現在到最後一幀之間是否發生碰撞。這是在遊戲中實現物理的第一步。

檢查物體之間的碰撞

正方形在螢幕上移動,但是沒有相交的形態。就像他們沒有注意到對方。 讓我們為此做些事情。

您將檢查運動物件之間是否存在碰撞。這就需要您遍歷所有物件,並檢查它們中的任何一個是否重疊。為此,您需要巢狀迴圈。 看一下示例:

function detectCollisions(){
    let obj1;
    let obj2;

    // Reset collision state of all objects
    for (let i = 0; i < gameObjects.length; i++) {
        gameObjects[i].isColliding = false;
    }

    // Start checking for collisions
    for (let i = 0; i < gameObjects.length; i++)
    {
        obj1 = gameObjects[i];
        for (let j = i + 1; j < gameObjects.length; j++)
        {
            obj2 = gameObjects[j];

            // Compare object1 with object2
            if (rectIntersect(obj1.x, obj1.y, obj1.width, obj1.height, obj2.x, obj2.y, obj2.width, obj2.height)){
                obj1.isColliding = true;
                obj2.isColliding = true;
            }
        }
    }
}

檢查所有物件是否彼此相交。第二個for迴圈更智慧,並且跳過所有先前已經檢查過的項。您不必檢查物件兩次。如果它們第一次重疊,那麼第二次也會重疊。當然,您不必對照自身檢查物件,因為它和自身始終會重疊。

該函式為每個物件組合呼叫rectIntersect()。當發現衝突時,它將兩個相關物件的isColliding設定為true。

還記得正方形裡的draw()函式嗎?它將對isColliding做出反應,並以其他顏色繪製正方形。您可以輕鬆看到兩個物件何時重疊。

您什麼時候檢查碰撞?

與draw()方法一樣,您要在檢查碰撞之前先更新所有遊戲物件的位置。這樣,您將始終檢查處於最新狀態的重疊物件。如果以其他方式進行操作並在更新之前檢查衝突,則將檢查前一幀的狀態是否重疊。您將始終不能執行真實情況。

另一個選擇是按照正確的順序進行碰撞檢查,但是要反覆進行。您將更新物件-a,檢查物件-a是否與所有其他物件重疊,更新物件-b,檢查物件-b與所有其他物件重疊,依此類推。這不是進行碰撞檢查的正確方法。想象一下,在更新物件a的位置後,物件a將與物件b發生碰撞。即使物件b也會先移動,系統也可能會檢測到碰撞。因此,在進行碰撞檢查之前,您必須先更新所有物件。

遊戲迴圈的正確順序是更新,碰撞檢查,清除畫布,繪製。因此,將catchCollisions()函式放在更新所有遊戲物件的迴圈之後。您的總遊戲迴圈現在看起來像這樣:

矩形之間的碰撞檢測

最後的難題是rectIntersect()方法。您可以使用它來檢查兩個矩形是否重疊。檢查兩個與軸對齊(未旋轉)的矩形之間的重疊非常簡單且直接。您可能會想出一種通過使用矩形的位置和大小來檢查兩個軸上是否重疊的方法。有很多方法可以執行此操作,但是下一個方法非常有效:

rectIntersect(x1, y1, w1, h1, x2, y2, w2, h2) {
    // Check x and y for overlap
    if (x2 > w1 + x1 || x1 > w2 + x2 || y2 > h1 + y1 || y1 > h2 + y2){
        return false;
    }
    return true;
}

該程式碼可以檢測到明顯重疊一半的矩形,在一個小矩形完全落入一個大矩形的情況下也可以使用。

有了這段程式碼,您最終可以檢查出結果。這裡又是正方形,但是這次它們相互響應。

[canvas]

檢測到碰撞後,將isColliding屬性設定為true。這使正方形以紅色繪製。 您現在可以清楚地看到兩個物件何時重疊。

檢查兩個圓是否重疊

您現在有了一種檢查未旋轉矩形之間的碰撞的方法。但是,如果您想對圓做同樣的事情怎麼辦?當然,那也不難。

假設您有兩個圓,每個圓都有自己的半徑。它們之間的距離很遠。如果距離小於兩個圓的半徑之和,則這些圓將重疊。由於圓是圓形的,因此甚至在旋轉物件時也可以使用,因此不必將其與軸對齊。

計算兩點之間的距離

您可以使用以下公式計算兩點之間的距離:

c = sqrt((x1 - x2)2 + (y1 - y2)2)

如果您將Δx和Δy視為三角形的兩個邊,則它基本上會應用勾股定理來計算點之間的直線距離, 為c。

因此,如果此距離小於或等於圓a的半徑加圓b的半徑,則這些圓會重疊或接觸。下一個函式使用此原理:

circleIntersect(x1, y1, r1, x2, y2, r2) {

    // Calculate the distance between the two circles
    let squareDistance = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);

    // When the distance is smaller or equal to the sum
    // of the two radius, the circles touch or overlap
    return squareDistance <= ((r1 + r2) * (r1 + r2))
}

如您所見,對公式進行了一些調整。數字直接相乘比使用Math.sqrt()獲得平方根要快得多,因此,在不獲取根的情況下計算距離,且半徑之和與自身相乘。結果保持不變,但是效能更好。

這是與以前相同的示例,但是這次是圓:

[canvas]

其他圖形呢?

在本文中,僅對兩種形狀進行了碰撞檢測。但是,如果您的遊戲物件由其他更復雜的形狀甚至影象組成,並且您想在它們之間進行碰撞檢查,該怎麼辦?

好吧,對於幾何形狀,您可以找到其他公式來檢測兩個物件何時重疊。 這是一個涵蓋許多不同形狀的碰撞檢測的網站。總體而言,更復雜的形狀使碰撞檢測更加困難。對於影象,您可以應用畫素完美碰撞檢測。不利的一面是,這是一個佔用大量CPU的操作。想象一下,必須將每個畫素彼此匹配,這將是一項艱鉅的工作。

這就是為什麼為了使事情變得簡單並減輕系統負擔,開發人員經常使用Hitbox來檢測形狀複雜的遊戲物件之間的碰撞。這是一種使碰撞檢測更容易並且僅使用基本幾何形狀的方法,例如本教程中介紹的矩形和圓形。因此,在開始構建對各種複雜形狀的支援之前,請嘗試考慮一種使用基本形狀和Hitbox實現相同效果的簡單方法。

什麼是Hitbox,如何使用它們?

Hitbox是遊戲物件周圍的虛構幾何形狀,用於確定碰撞檢測。想象您有一個遊戲角色。您無需檢查其手臂和腿部是否發生碰撞,而只需檢查圍繞在玩家周圍的大的假想的矩形即可。

您可以簡單地將函式用於矩形碰撞檢測(如前所述),以檢查hitboxes是否存在碰撞。它佔用的CPU少得多,並且使在遊戲中支援複雜形狀變得更加容易。在某些特殊情況下,每個遊戲物件甚至可以使用多個Hitbox。它依然勝過畫素的完美解決方案。

上圖演示了衝突檢測的不同型別。 它們各有優點和缺點:

  1. 完美的畫素法-超精確的碰撞檢測,但需要一些嚴肅的系統資源。在大多數情況下,這太過分了。
  2. Hitbox-更好的效能,但是碰撞檢測可能非常不精確。不過,在許多遊戲場景中,這並不重要。
  3. 多個Hitbox-比單個Hitbox效率低,但仍勝過畫素完美變體。並且您可以支援複雜的形狀。對於需要一些額外精度的重要遊戲物件,例如上面提到的四肢玩家,這是一個不錯的選擇。您可以為核心建立一個Hitbox,為手臂,腿部和頭部分離一個Hitbox。
    示例影象是來自遊戲Pixi Pop的實際遊戲資源。在遊戲中,當精確的碰撞檢測很重要並且沒有執行太多其他遊戲任務時,才使用資源。因此,在這種情況下,可以選擇與多個hitboxes一起使用。只需選擇最適合您的遊戲場景的選項即可。

對物理碰撞做出響應

現在,您有了可以檢測碰撞並更改顏色的遊戲物件。但是,如果物體像現實生活中的物體那樣相互反彈,會不會更酷呢?現在是時候將一些物理學應用於您的遊戲了。

要更改移動物體的速度,您需要找出碰撞發生的方向和速度。然後,您可以將速度更改應用於碰撞的物件。此原理適用於矩形和圓形。

找到碰撞的方向和速度

想象兩個遊戲物件之間的下一次碰撞。這兩個物件都有自己的速度和方向。他們不會直接撞到對方,而是在自己的路線上碰巧會引起碰撞。

您需要找出碰撞的速度和方向,以便將其應用於遊戲物件的速度。首先為發生的碰撞建立向量。該向量僅是兩個碰撞物件之間的x和y之差。您可以將其視為帶有長度和方向的箭頭。對於向量,長度也稱為幅度。像這樣計算碰撞向量:

let vCollision = {x: obj2.x - obj1.x, y: obj2.y - obj1.y};

在兩個遊戲物件的示例中,碰撞向量如下所示:

在這種情況下,大小等於兩個碰撞物件之間的距離。它與速度無關。但是您可以使用向量的方向。要獲得方向,您需要除去距離的因素。

我們首先計算碰撞向量的距離。您可以使用之前相同的公式來計算兩個碰撞圓之間的距離。因此,程式碼變為:

let distance = Math.sqrt((obj2.x-obj1.x)*(obj2.x-obj1.x) + (obj2.y-obj1.y)*(obj2.y-obj1.y));

現在使用距離來計算歸一化的碰撞向量。您基本上刪除了距離作為碰撞向量中的一個因素,因此只剩下一個方向。碰撞範數與碰撞向量的方向相同,只是norm/magnitude/length與1比較大小。也將其稱為單位向量。您可以像這樣計算歸一化向量:

let vCollisionNorm = {x: vCollision.x / distance, y: vCollision.y / distance};

基本上,這隻會為您提供碰撞方向。在兩個遊戲物件的示例中,它將如下所示:

您現在有一個方向。這是發生碰撞的方向。現在,您所需要的只是碰撞速度,您將能夠計算碰撞如何影響物體的速度。您可以像這樣計算碰撞速度:

let vRelativeVelocity = {x: obj1.vx - obj2.vx, y: obj1.vy - obj2.vy};
let speed = vRelativeVelocity.x * vCollisionNorm.x + vRelativeVelocity.y * vCollisionNorm.y;

作為示例程式碼中的第一行,將使用物件的相對速度建立另一個向量。就像您要使其中一個遊戲物件靜止時所要留下的向量。(您可以在此處詳細瞭解相對速度。)在下一個示例中更容易理解。兩個遊戲物件的向量彼此重疊顯示,因此您可以看見相對速度向量:

相對速度向量與碰撞法線一起用於計算兩個向量的點積。點積是相對速度在碰撞法線上的投影長度。換句話說,就是速度向量在碰撞方向上的長度。在此處可詳細瞭解dot products。在此處瞭解有關向量操作的更多資訊。

點積等於碰撞速度。就是這樣,您有了兩個物件之間碰撞的速度和方向。您可以將其應用於遊戲物件的速度,並使它們彼此反彈。

改變運動物體的速度

碰撞速度可以為正或負。當它為正時,物件正在朝彼此移動。當結果為負數時,他們會離開。當物體移開時,無需執行任何其他操作。他們將自行擺脫衝突。

if (speed < 0){
    break;
}

對於另一種情況,當物件彼此靠近時,請沿碰撞方向施加速度。兩個物體從碰撞中獲得相同的速度變化。將速度減去或加到兩個碰撞物件的速度上。

obj1.vx -= (speed * vCollisionNorm.x);
obj1.vy -= (speed * vCollisionNorm.y);
obj2.vx += (speed * vCollisionNorm.x);
obj2.vy += (speed * vCollisionNorm.y);

就是這樣,通過將速度應用於方向,您可以計算出碰撞速度。現在,該速度將以所涉及物件的速度進行處理。您的遊戲物件應該以自然的方式反彈。

現在看一下結果:
[canvas]

增加質量,脈衝和動力

如果願意,您可以進一步應用物理學,並根據速度計算碰撞脈衝,從而將質量納入方程式。使用衝量來計算動量。重物會將輕物推開。

let impulse = 2 * speed / (obj1.mass + obj2.mass);
obj1.vx -= (impulse * obj2.mass * vCollisionNorm.x);
obj1.vy -= (impulse * obj2.mass * vCollisionNorm.y);
obj2.vx += (impulse * obj1.mass * vCollisionNorm.x);
obj2.vy += (impulse * obj1.mass * vCollisionNorm.y);

如果您有兩個質量為1的物體,則脈衝剛好等於速度。在其他情況下,您基本上將速度分為許多小部分。重物從動量中吸收了一部分,輕物得到了很多。這使得較輕的物體受到碰撞的影響更大。

不要忘記為遊戲物件增加質量。GameObject類是儲存質量的好地方。您可以修改createWorld()函式以通過Circle和Rectangle類將質量作為引數傳遞。

這是一個經過修改的示例,可以建立許多小圓圈和兩個大圓圈。(生成演算法不是很聰明,因此物件可能在碰撞中開始)

[canvas]

在該示例中,與小圓圈相比,大圓圈的質量非常大。他們將一切推開。但是,當兩個重物相互撞擊時,它們也會彈起。

獲取物件的運動方向上的頂部

物體不斷碰撞並改變方向。對於遊戲,準確地知道哪個方向會有所幫助,因此您可以新增旋轉的紋理或基於該紋理構建遊戲邏輯。讓我們計算一下!

通過在x和y速度上使用Math.atan2(),您可以輕鬆地獲得物件的角度。結果以弧度表示,請使用Math.PI將其轉換為度。這是一個在update()函式中計算角度的示例:

update(secondsPassed){
    // Move with set velocity
    this.x += this.vx * secondsPassed;
    this.y += this.vy * secondsPassed;

    // Calculate the angle (vy before vx)
    let radians = Math.atan2(this.vy, this.vx);

    // Convert to degrees
    let degrees = 180 * radians / Math.PI;
}

您可以稍後在遊戲中使用該角度。在本系列的下一個教程中,您將學習如何使用它來繪製旋轉的影象。現在,這是一個簡單的示例實現,用一點線顯示物件的移動方向。

[canvas]

新增重力的影響

本教程中顯示的示例僅包含物理學的基本實現。您可以在遊戲中新增更多方面,以使其看起來更加自然。引力或彈力之類的事情不太容易實現。現在開始新增重力到模擬中。

對於重力,只需使用重力加速度調整物件的y速度。在地球上,重力加速度大約為9.81。您可以將其應用到遊戲物件的update()函式中。每秒將g新增到y速度,這將使物件掉落的速度越來越快。

// Set gravitational acceleration
const g = 9.81;

update(secondsPassed){
    // Apply acceleration
    this.vy += g * secondsPassed;

    // Move with set velocity
    this.x += this.vx * secondsPassed;
    this.y += this.vy * secondsPassed;
}

更新速度,然後再更新位置。如本文中有關對運動方程的積分所述,這將提供更準確的結果。這種整合稱為半隱式尤拉。

限制物體的運動空間

為了使重力效果很好地顯示,可以將物件的移動限制在畫布的邊緣。它會像一個封閉的盒子一樣在上面彈起物體。

您可以通過簡單的調整使其實現。在主要碰撞檢測功能之後立即執行下一個函式,因此將檢查物件邊緣碰撞以及物件-物件碰撞。

// Define the edges of the canvas
 const canvasWidth = 750;
 const canvasHeight = 400;

 // Set a restitution, a lower value will lose more energy when colliding
 const restitution = 0.90;

 function detectEdgeCollisions()
 {
     let obj;
     for (let i = 0; i < gameObjects.length; i++)
     {
         obj = gameObjects[i];

         // Check for left and right
         if (obj.x < obj.radius){
             obj.vx = Math.abs(obj.vx) * restitution;
             obj.x = obj.radius;
         }else if (obj.x > canvasWidth - obj.radius){
             obj.vx = -Math.abs(obj.vx) * restitution;
             obj.x = canvasWidth - obj.radius;
         }

         // Check for bottom and top
         if (obj.y < obj.radius){
             obj.vy = Math.abs(obj.vy) * restitution;
             obj.y = obj.radius;
         } else if (obj.y > canvasHeight - obj.radius){
             obj.vy = -Math.abs(obj.vy) * restitution;
             obj.y = canvasHeight - obj.radius;
         }
     }
}

基本上,它檢查是否位於邊緣之外的物件,並將其位置重置為再次落入盒子內。 然後將物件的速度翻轉以垂直於牆移動。

這是一個非常基本的實現,只能通過這種方式工作,因為畫布的邊緣是預先定義的直線。您可以對圓弧線碰撞和設定動態線執行相同的操作,但是比該簡單的示例要複雜得多。

實現彈力損耗

如果您現在執行程式碼,您將看到遊戲物件永遠不會處於靜止狀態。他們會不斷彈跳,並且永遠不會失去任何能量。為了解決這個問題,您可以實施賠償。

恢復基本上描述了每次碰撞後還剩下多少能量。它對物件的反彈具有影響。彈跳後的開始速度和結束速度之間的比率稱為恢復係數或COR。

  • COR為0的物體會在撞擊時吸收所有能量,例如一袋沙子撞擊地板。
  • COR為1的物體將具有完美的彈性,例如超級彈力的彈跳球。
  • COR > 1的物件完全是虛構的,每次碰撞後都會增加額外的能量。

在前面的編碼示例中,COR被應用於與邊緣的碰撞。這會使物體在每次反彈後僅損失一點能量。這將使模擬更加逼真,而忽略它會使物件永遠反彈。

要完成彈力的實現,您還需要將其應用於涉及物件-物件衝突的物件。只需將它們的速度乘以COR(只需恢復程式碼即可)。現在,每次碰撞都會消耗一點能量。

當兩個物體以不同的恢復設定碰撞時,例如,當彈跳球擊中一袋沙子時,最低恢復將計算在內。在這種情況下,彈跳球或沙袋都不會反彈,它們的彈力都被袋子吸收了。

let speed = vRelativeVelocity.x * vecCollisionNorm.x + vRelativeVelocity.y * vecCollisionNorm.y;
speed *= Math.min(obj1.restitution, obj2.restitution);

下一個實時畫布示例顯示了重力,彈力和撞擊的應用。

[canvas]

您可以輕鬆地調整變數以建立不同的方案。設定較高的重力以模擬在異物行星上的位置或降低其彈力,以使這些物體像吸收所有撞擊的沙袋一樣起作用。

改善表現的方法

您可能現在還沒有真正注意到它,但是如果同時顯示許多遊戲物件或顯示更復雜的形狀,則碰撞檢測和反應會給您的系統帶來很大壓力。以下是一些有助於提高效能的提示。它們似乎很明顯,但是當遊戲變得更加複雜時,很容易忽略其中一些概念。

  • 僅比較足夠近的物件以至於可能發生碰撞。您可以使用網格系統,或者僅在物件輸入特定半徑時才檢測碰撞。這稱為將碰撞檢測分為寬相和窄相。在此處瞭解有關寬相碰撞檢測的更多資訊。

  • 保持物件池清潔。在物體看不見或在遊戲中被破壞時清理它們。

  • 排除背景/固定物體。有些物件永遠不會對碰撞做出反應,因此不要在迭代中包含它們。

  • 使用Hitboxs。如前所述,命中盒是優化碰撞檢測並簡化複雜形狀的好方法。

  • 調整碰撞檢測和物理的實現來適應您的遊戲。當您要做的只是井字遊戲時,您不需要完整的物理引擎。這是一個非常棒的例子,但是您明白了。剝離邏輯以僅支援所需的內容。

處理快速移動的物體

關於碰撞檢測的最後一點。上面的示例通過檢查兩個物件是否重疊來檢測衝突。在許多情況下,這是一個很好的解決方案。但是,當您的物件高速移動時,它將無法正常工作。當速度高於最小物件的大小時,物件就有機會跳過碰撞檢查。 他們彼此穿越。

想象一下,您檢查遊戲中子彈與敵人之間是否有碰撞。第一幀子彈在敵人面前。沒有重疊,因此沒有碰到物體。下一幀子彈移動得如此之快,它現在位於敵人的後面。仍然沒有重疊,因此沒有碰撞。但是子彈確實穿過了敵人,應該受到了打擊。

這是一張影象,用來演示快速移動的物體(例如子彈)的情況,該物體從未與另一個遊戲物體真正重疊,而應該引起了碰撞:

您需要針對這種情況的另一種方法。最簡單的方法是限制遊戲物件的速度。簡而言之,請確保速度永遠不會大於最小的遊戲物件,以使其無法通過。對於許多型別的遊戲來說,這是一個很好的解決方案,並且投入的精力最少。

另一種解決方案是使用投影路徑而不是兩個物件的當前位置執行碰撞檢測。嘗試將子彈的路徑視覺化為一條線。線的長度等於子彈行進的距離。現在,您可以使用線到矩形或線到圓的碰撞檢查來發現子彈是否會擊中另一個物體。對於大的子彈,可以使用矩形而不是直線。

這是一個簡化的解決方案。在此過程中,您可能會遇到其他問題,例如查詢影響點或確定首先擊中較大集合中的哪個物件。但是這裡提到的步驟可能會幫助您指出正確的方向。目前,本教程僅涉及快速移動的物件。

下一步是什麼?

碰撞和物理學的所有內容到此為止。您的碰撞檢查已經到位,您的遊戲物件現在正在以一種半自然的方式進行互動。如果您有任何意見或問題,請隨時將其釋出在下面的評論部分。

在本教程的下一步中,您將學習如何在遊戲中使用影象並建立精靈動畫。