Chrome自帶恐龍小游戲的源碼研究(七)


  在上一篇《Chrome自帶恐龍小游戲的源碼研究(六)》中研究了恐龍的跳躍過程,這一篇研究恐龍與障礙物之間的碰撞檢測。

碰撞盒子

  游戲中采用的是矩形(非旋轉矩形)碰撞。這類碰撞優點是計算比較簡單,缺點是對不規則物體的檢測不夠精確。如果不做更為精細的處理,結果會像下圖:

 

如圖所示,兩個盒子雖然有重疊部分,但實際情況是恐龍和仙人掌之間並未發生碰撞。為了解決這個問題,需要建立多個碰撞盒子:

不過這樣還是有問題,觀察圖片,恐龍和仙人掌都有四個碰撞盒子,如果每次Game Loop里都對這些盒子進行碰撞檢測,那么結果是每次需要進行4X4=16次計算,如果物體或者盒子很多,就會導致運算量大大增加,造成嚴重的性能問題。為改進這一點,只需要先檢測兩個大盒子之間是否碰撞,如果沒有,則略去里面小盒子的碰撞檢測。反之則對里面的小盒子做碰撞檢測。游戲中使用CollisionBox構造函數創建碰撞盒子:

/**
* 碰撞盒子
* @param x    {number} 盒子x坐標
* @param y    {number} 盒子y坐標
* @param w    {number} 盒子寬度
* @param h    {number} 盒子高度
*/
function CollisionBox(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.width = w;
    this.height = h;
}

使用boxCompare方法檢測兩個盒子是否發生碰撞:

 1 /**
 2 * 碰撞檢測
 3 * @param tRexBox {Object} 霸王龍的碰撞盒子
 4 * @param obstacleBox {Object} 障礙物的碰撞盒子
 5 */
 6 function boxCompare(tRexBox, obstacleBox) {
 7     var tRexBoxX = tRexBox.x,
 8         tRexBoxY = tRexBox.y,
 9         obstacleBoxX = obstacleBox.x,
10         obstacleBoxY = obstacleBox.y;
11 
12     return tRexBoxX < obstacleBoxX + obstacleBox.width && tRexBoxX + tRexBox.width > obstacleBoxX && tRexBoxY < obstacleBoxY + obstacleBox.height && tRexBox.height + tRexBoxY > obstacleBoxY;
13 }

 

建立碰撞盒子

  接下來要為恐龍和障礙物建立碰撞盒子。游戲中為恐龍建立了6個碰撞盒子,分布在頭、軀干和腳,同時它還有閃避狀態:

        Trex.collisionBoxes = {
            DUCKING:[
                new CollisionBox(1,18,55,25)
            ],
            RUNNING: [
                new CollisionBox(22, 0, 17, 16),
                new CollisionBox(1, 18, 30, 9),
                new CollisionBox(10, 35, 14, 8),
                new CollisionBox(1, 24, 29, 5),
                new CollisionBox(5, 30, 21, 4),
                new CollisionBox(9, 34, 15, 4)
            ]
        };

障礙物的碰撞盒子定義在Obstacle.types中:

 1 Obstacle.types = [{
 2     type: 'CACTUS_SMALL',
 3     width: 17,
 4     height: 35,
 5     yPos: 105,
 6     multipleSpeed: 4,
 7     minGap: 120,
 8     minSpeed: 0,
 9     collisionBoxes: [new CollisionBox(0, 7, 5, 27), new CollisionBox(4, 0, 6, 34), new CollisionBox(10, 4, 7, 14)]
10 },
11 {
12     type: 'CACTUS_LARGE',
13     width: 25,
14     height: 50,
15     yPos: 90,
16     multipleSpeed: 7,
17     minGap: 120,
18     minSpeed: 0,
19     collisionBoxes: [new CollisionBox(0, 12, 7, 38), new CollisionBox(8, 0, 7, 49), new CollisionBox(13, 10, 10, 38)]
20 },
21 {
22     type: 'PTERODACTYL',
23     width: 46,
24     height: 40,
25     yPos: [100, 75, 50],
26     // Variable height mobile.
27     multipleSpeed: 999,
28     minSpeed: 8.5,
29     minGap: 150,
30     collisionBoxes: [new CollisionBox(15, 15, 16, 5), new CollisionBox(18, 21, 24, 6), new CollisionBox(2, 14, 4, 3), new CollisionBox(6, 10, 4, 7), new CollisionBox(10, 8, 6, 9)],
31     numFrames: 2,
32     frameRate: 1000 / 6,
33     speedOffset: .8
34 }];
View Code

不過這只是定義了障礙物數量為1的情況,復數的障礙物需要在創建時修正碰撞盒子:

1 if (this.size > 1) {//只針對仙人掌
2     this.collisionBoxes[1].width = this.width - this.collisionBoxes[0].width - this.collisionBoxes[2].width;
3     this.collisionBoxes[2].x = this.width - this.collisionBoxes[2].width;
4 }

下圖分別為單數和復數的盒子。

最后執行碰撞檢測:

 1 function checkForCollision(obstacle, tRex) {
 2         //創建最外層的大盒子
 3     var tRexBox = new CollisionBox(tRex.xPos + 1, tRex.yPos + 1, tRex.config.WIDTH - 2, tRex.config.HEIGHT - 2);
 4     var obstacleBox = new CollisionBox(obstacle.xPos + 1, obstacle.yPos + 1, obstacle.typeConfig.width * obstacle.size - 2, obstacle.typeConfig.height - 2);
 5 
 6     }
 7     if (boxCompare(tRexBox, obstacleBox)) {
 8         var collisionBoxes = obstacle.collisionBoxes;
 9         var tRexCollisionBoxes = tRex.ducking ? Trex.collisionBoxes.DUCKING: Trex.collisionBoxes.RUNNING;
10 
11         for (var t = 0; t < tRexCollisionBoxes.length; t++) {
12             for (var i = 0; i < collisionBoxes.length; i++) {
13                 //修正盒子
14                 var adjTrexBox = createAdjustedCollisionBox(tRexCollisionBoxes[t], tRexBox);
15                 var adjObstacleBox = createAdjustedCollisionBox(collisionBoxes[i], obstacleBox);
16                 var crashed = boxCompare(adjTrexBox, adjObstacleBox);
17 
18                 if (crashed) {
19                     return [adjTrexBox, adjObstacleBox];
20                 }
21             }
22         }
23     }
24     return false;
25 }
View Code
//修正盒子,將相對坐標轉為畫布坐標
function createAdjustedCollisionBox(box, adjustment) {
    return new CollisionBox(box.x + adjustment.x, box.y + adjustment.y, box.width, box.height);
}
View Code

以下是最終運行效果,打開控制台就能看到碰撞輸出:

 

后記

  通過建立碰撞盒子進行碰撞檢測在應用上非常廣泛,著名的街機游戲《街霸》和《拳皇》就是采用了這種方式:

 

可以看到游戲中對人物建立了多個碰撞盒子,紅色代表攻擊區域,藍色代表可以被攻擊的區域,綠色區域之間不能重疊,用來推擠對手。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM