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

原文地址:Collision detection and physics

使用JavaScript執(zhí)行碰撞檢測并產(chǎn)生物理反應。檢查圖形之間是否重疊,應用Hitbox并計算新速度。通過物體的質(zhì)量,重力和恢復力使其更自然。 在本教程結(jié)束時,您將在游戲中運行基本的物理模擬。

創(chuàng)建一些移動的對象

如果您已經(jīng)知道如何創(chuàng)建運動對象,并且只對檢測碰撞或物理感興趣,請向下滾動至下一部分。

在檢測運動物體之間的碰撞之前,首先需要一些物體。 在上一教程中,您學習了如何移動單個矩形。讓我們擴展這種邏輯,并創(chuàng)建一堆移動對象來填充游戲。首先,定義一種新型的游戲?qū)ο?。這將是一個簡單的正方形。

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()函數(shù)。這次,它被提取到一個單獨的正方形class中。這樣,您可以創(chuàng)建一個正方形的許多實例,并且它們都使用相同的邏輯進行繪制和更新。您將很容易管理正方形的行為和外觀。

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

所有方塊都繼承自GameObject類。每個游戲?qū)ο蠖加幸粋€位置和速度。這使您可以輕松創(chuàng)建新型的游戲?qū)ο?。他?a target="_blank">繼承了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關(guān)鍵字創(chuàng)建類的新實例。使用此createWorld()函數(shù)創(chuàng)建一些正方形以填充您的游戲世界。

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)
    ];
}

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

現(xiàn)在一切就緒,可以繪制正方形。使用以下代碼更新游戲循環(huán),以遍歷新創(chuàng)建的游戲?qū)ο蟛⑺鼈兝L制在屏幕上。

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()不再每次迭代調(diào)用一次。屏幕上的每個對象,每次迭代都調(diào)用一次。

這樣,update()和draw()的實現(xiàn)是特定于對象的。對于游戲循環(huán),您嘗試繪制哪種對象都沒有關(guān)系,只要它們具有update()和draw()函數(shù)即可。

對于您正在使用的正方形,它將繪制一個簡單的正方形并將其沿直線移動。但是,請想象其他類型的對象,它們具有兩個功能的自己的實現(xiàn),并且具有自己的行為和外觀。這個游戲循環(huán)可以處理它。

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

看一下結(jié)果:

image

您可以看到現(xiàn)在繪制了一堆矩形。他們每個人都有自己的出發(fā)位置,并朝著不同的方向前進。就像在createWorld()函數(shù)中定義的一樣。您可以調(diào)整變量以創(chuàng)建新的正方形類型。

為什么需要碰撞檢測?

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

碰撞檢測是一種檢測兩個對象是否彼此碰撞的技術(shù),或者是從現(xiàn)在到最后一幀之間是否發(fā)生碰撞。這是在游戲中實現(xiàn)物理的第一步。

檢查物體之間的碰撞

正方形在屏幕上移動,但是沒有相交的形態(tài)。就像他們沒有注意到對方。 讓我們?yōu)榇俗鲂┦虑椤?/p>

您將檢查運動對象之間是否存在碰撞。這就需要您遍歷所有對象,并檢查它們中的任何一個是否重疊。為此,您需要嵌套循環(huán)。 看一下示例:

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循環(huán)更智能,并且跳過所有先前已經(jīng)檢查過的項。您不必檢查對象兩次。如果它們第一次重疊,那么第二次也會重疊。當然,您不必對照自身檢查對象,因為它和自身始終會重疊。

該函數(shù)為每個對象組合調(diào)用rectIntersect()。當發(fā)現(xiàn)沖突時,它將兩個相關(guān)對象的isColliding設置為true。

還記得正方形里的draw()函數(shù)嗎?它將對isColliding做出反應,并以其他顏色繪制正方形。您可以輕松看到兩個對象何時重疊。

您什么時候檢查碰撞?

與draw()方法一樣,您要在檢查碰撞之前先更新所有游戲?qū)ο蟮奈恢?。這樣,您將始終檢查處于最新狀態(tài)的重疊對象。如果以其他方式進行操作并在更新之前檢查沖突,則將檢查前一幀的狀態(tài)是否重疊。您將始終不能執(zhí)行真實情況。

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

游戲循環(huán)的正確順序是更新,碰撞檢查,清除畫布,繪制。因此,將catchCollisions()函數(shù)放在更新所有游戲?qū)ο蟮难h(huán)之后。您的總游戲循環(huán)現(xiàn)在看起來像這樣:

The place of collision detection inside a game loop

矩形之間的碰撞檢測

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

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;
}

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

有了這段代碼,您最終可以檢查出結(jié)果。這里又是正方形,但是這次它們相互響應。

image

檢測到碰撞后,將isColliding屬性設置為true。這使正方形以紅色繪制。 您現(xiàn)在可以清楚地看到兩個對象何時重疊。

檢查兩個圓是否重疊

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

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

Circle on circle collision detection

計算兩點之間的距離

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

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

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

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

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))
}

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

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

image

其他圖形呢?

在本文中,僅對兩種形狀進行了碰撞檢測。但是,如果您的游戲?qū)ο笥善渌鼜碗s的形狀甚至圖像組成,并且您想在它們之間進行碰撞檢查,該怎么辦?

好吧,對于幾何形狀,您可以找到其他公式來檢測兩個對象何時重疊。 這是一個涵蓋許多不同形狀的碰撞檢測的網(wǎng)站??傮w而言,更復雜的形狀使碰撞檢測更加困難。對于圖像,您可以應用像素完美碰撞檢測。不利的一面是,這是一個占用大量CPU的操作。想象一下,必須將每個像素彼此匹配,這將是一項艱巨的工作。

這就是為什么為了使事情變得簡單并減輕系統(tǒng)負擔,開發(fā)人員經(jīng)常使用Hitbox來檢測形狀復雜的游戲?qū)ο笾g的碰撞。這是一種使碰撞檢測更容易并且僅使用基本幾何形狀的方法,例如本教程中介紹的矩形和圓形。因此,在開始構(gòu)建對各種復雜形狀的支持之前,請嘗試考慮一種使用基本形狀和Hitbox實現(xiàn)相同效果的簡單方法。

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

Hitbox是游戲?qū)ο笾車奶摌?gòu)幾何形狀,用于確定碰撞檢測。想象您有一個游戲角色。您無需檢查其手臂和腿部是否發(fā)生碰撞,而只需檢查圍繞在玩家周圍的大的假想的矩形即可。

您可以簡單地將函數(shù)用于矩形碰撞檢測(如前所述),以檢查hitboxes是否存在碰撞。它占用的CPU少得多,并且使在游戲中支持復雜形狀變得更加容易。在某些特殊情況下,每個游戲?qū)ο笊踔量梢允褂枚鄠€Hitbox。它依然勝過像素的完美解決方案。

The use of hitboxes for collision detection

上圖演示了沖突檢測的不同類型。 它們各有優(yōu)點和缺點:

  1. 完美的像素法-超精確的碰撞檢測,但需要一些嚴肅的系統(tǒng)資源。在大多數(shù)情況下,這太過分了。
  2. Hitbox-更好的性能,但是碰撞檢測可能非常不精確。不過,在許多游戲場景中,這并不重要。
  3. 多個Hitbox-比單個Hitbox效率低,但仍勝過像素完美變體。并且您可以支持復雜的形狀。對于需要一些額外精度的重要游戲?qū)ο?,例如上面提到的四肢玩家,這是一個不錯的選擇。您可以為核心創(chuàng)建一個Hitbox,為手臂,腿部和頭部分離一個Hitbox。
    示例圖像是來自游戲Pixi Pop的實際游戲資源。在游戲中,當精確的碰撞檢測很重要并且沒有運行太多其他游戲任務時,才使用資源。因此,在這種情況下,可以選擇與多個hitboxes一起使用。只需選擇最適合您的游戲場景的選項即可。

對物理碰撞做出響應

現(xiàn)在,您有了可以檢測碰撞并更改顏色的游戲?qū)ο?。但是,如果物體像現(xiàn)實生活中的物體那樣相互反彈,會不會更酷呢?現(xiàn)在是時候?qū)⒁恍┪锢韺W應用于您的游戲了。

要更改移動物體的速度,您需要找出碰撞發(fā)生的方向和速度。然后,您可以將速度更改應用于碰撞的對象。此原理適用于矩形和圓形。

找到碰撞的方向和速度

想象兩個游戲?qū)ο笾g的下一次碰撞。這兩個對象都有自己的速度和方向。他們不會直接撞到對方,而是在自己的路線上碰巧會引起碰撞。

Vectors of two game objects in collision

您需要找出碰撞的速度和方向,以便將其應用于游戲?qū)ο蟮乃俣?。首先為發(fā)生的碰撞創(chuàng)建矢量。該矢量僅是兩個碰撞對象之間的x和y之差。您可以將其視為帶有長度和方向的箭頭。對于矢量,長度也稱為幅度。像這樣計算碰撞矢量:

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

在兩個游戲?qū)ο蟮氖纠?,碰撞矢量如下所示?/p>

Collision vector of two colliding objects

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

我們首先計算碰撞矢量的距離。您可以使用之前相同的公式來計算兩個碰撞圓之間的距離。因此,代碼變?yōu)椋?/p>

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

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

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

基本上,這只會為您提供碰撞方向。在兩個游戲?qū)ο蟮氖纠?,它將如下所示?/p>

Unit vector of collision

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

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

作為示例代碼中的第一行,將使用對象的相對速度創(chuàng)建另一個矢量。就像您要使其中一個游戲?qū)ο箪o止時所要留下的向量。(您可以在此處詳細了解相對速度。)在下一個示例中更容易理解。兩個游戲?qū)ο蟮南蛄勘舜酥丿B顯示,因此您可以看見相對速度向量:

Relative velocity between two objects

相對速度向量與碰撞法線一起用于計算兩個向量的點積。點積是相對速度在碰撞法線上的投影長度。換句話說,就是速度向量在碰撞方向上的長度。在此處可詳細了解dot products。在此處了解有關(guān)矢量操作的更多信息。

Dot product of the velocity and direction vectors, makes speed

點積等于碰撞速度。就是這樣,您有了兩個對象之間碰撞的速度和方向。您可以將其應用于游戲?qū)ο蟮乃俣?,并使它們彼此反彈?/p>

改變運動物體的速度

碰撞速度可以為正或負。當它為正時,對象正在朝彼此移動。當結(jié)果為負數(shù)時,他們會離開。當物體移開時,無需執(zhí)行任何其他操作。他們將自行擺脫沖突。

if (speed < 0){
    break;
}

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

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

就是這樣,通過將速度應用于方向,您可以計算出碰撞速度?,F(xiàn)在,該速度將以所涉及對象的速度進行處理。您的游戲?qū)ο髴撘宰匀坏姆绞椒磸棥?/p>

現(xiàn)在看一下結(jié)果:


image

增加質(zhì)量,脈沖和動力

如果愿意,您可以進一步應用物理學,并根據(jù)速度計算碰撞脈沖,從而將質(zhì)量納入方程式。使用沖量來計算動量。重物會將輕物推開。

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);

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

不要忘記為游戲?qū)ο笤黾淤|(zhì)量。GameObject類是存儲質(zhì)量的好地方。您可以修改createWorld()函數(shù)以通過Circle和Rectangle類將質(zhì)量作為參數(shù)傳遞。

這是一個經(jīng)過修改的示例,可以創(chuàng)建許多小圓圈和兩個大圓圈。(生成算法不是很聰明,因此對象可能在碰撞中開始)

image

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

獲取對象的運動方向上的頂部

物體不斷碰撞并改變方向。對于游戲,準確地知道哪個方向會有所幫助,因此您可以添加旋轉(zhuǎn)的紋理或基于該紋理構(gòu)建游戲邏輯。讓我們計算一下!

通過在x和y速度上使用Math.atan2(),您可以輕松地獲得對象的角度。結(jié)果以弧度表示,請使用Math.PI將其轉(zhuǎn)換為度。這是一個在update()函數(shù)中計算角度的示例:

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;
}

您可以稍后在游戲中使用該角度。在本系列的下一個教程中,您將學習如何使用它來繪制旋轉(zhuǎn)的圖像。現(xiàn)在,這是一個簡單的示例實現(xiàn),用一點線顯示對象的移動方向。

image

添加重力的影響

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

對于重力,只需使用重力加速度調(diào)整對象的y速度。在地球上,重力加速度大約為9.81。您可以將其應用到游戲?qū)ο蟮膗pdate()函數(shù)中。每秒將g添加到y(tǒng)速度,這將使對象掉落的速度越來越快。

// 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;
}

更新速度,然后再更新位置。如本文中有關(guān)對運動方程的積分所述,這將提供更準確的結(jié)果。這種集成稱為半隱式歐拉。

限制物體的運動空間

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

您可以通過簡單的調(diào)整使其實現(xiàn)。在主要碰撞檢測功能之后立即執(zhí)行下一個函數(shù),因此將檢查對象邊緣碰撞以及對象-對象碰撞。

// 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;
         }
     }
}

基本上,它檢查是否位于邊緣之外的對象,并將其位置重置為再次落入盒子內(nèi)。 然后將對象的速度翻轉(zhuǎn)以垂直于墻移動。

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

實現(xiàn)彈力損耗

如果您現(xiàn)在運行代碼,您將看到游戲?qū)ο笥肋h不會處于靜止狀態(tài)。他們會不斷彈跳,并且永遠不會失去任何能量。為了解決這個問題,您可以實施賠償。

恢復基本上描述了每次碰撞后還剩下多少能量。它對對象的反彈具有影響。彈跳后的開始速度和結(jié)束速度之間的比率稱為恢復系數(shù)或COR。

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

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

要完成彈力的實現(xiàn),您還需要將其應用于涉及對象-對象沖突的對象。只需將它們的速度乘以COR(只需恢復代碼即可)?,F(xiàn)在,每次碰撞都會消耗一點能量。

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

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

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

image

您可以輕松地調(diào)整變量以創(chuàng)建不同的方案。設置較高的重力以模擬在異物行星上的位置或降低其彈力,以使這些物體像吸收所有撞擊的沙袋一樣起作用。

改善表現(xiàn)的方法

您可能現(xiàn)在還沒有真正注意到它,但是如果同時顯示許多游戲?qū)ο蠡蝻@示更復雜的形狀,則碰撞檢測和反應會給您的系統(tǒng)帶來很大壓力。以下是一些有助于提高性能的提示。它們似乎很明顯,但是當游戲變得更加復雜時,很容易忽略其中一些概念。

  • 僅比較足夠近的對象以至于可能發(fā)生碰撞。您可以使用網(wǎng)格系統(tǒng),或者僅在對象輸入特定半徑時才檢測碰撞。這稱為將碰撞檢測分為寬相和窄相。在此處了解有關(guān)寬相碰撞檢測的更多信息。

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

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

  • 使用Hitboxs。如前所述,命中盒是優(yōu)化碰撞檢測并簡化復雜形狀的好方法。

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

處理快速移動的物體

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

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

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

Collision detection for fast moving objects, like bullets

您需要針對這種情況的另一種方法。最簡單的方法是限制游戲?qū)ο蟮乃俣取:喍灾?,請確保速度永遠不會大于最小的游戲?qū)ο螅允蛊錈o法通過。對于許多類型的游戲來說,這是一個很好的解決方案,并且投入的精力最少。

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

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

下一步是什么?

碰撞和物理學的所有內(nèi)容到此為止。您的碰撞檢查已經(jīng)到位,您的游戲?qū)ο蟋F(xiàn)在正在以一種半自然的方式進行交互。如果您有任何意見或問題,請隨時將其發(fā)布在下面的評論部分。

在本教程的下一步中,您將學習如何在游戲中使用圖像并創(chuàng)建精靈動畫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容