2012-10-27 63 views
1

我正在做一個平臺遊戲,但我碰到一個碰撞檢測問題。我製作了一個在屏幕/地圖上繪製平鋪的功能。在那個函數中是我的碰撞檢測,當只繪製一個圖塊時它工作正常,但是當我用三塊圖塊創建「樓梯」時,第一塊圖塊不起作用。玩家只是被「推」在瓦片上。側面檢測不起作用。其他瓷磚工作得很好。HTML5畫布中的碰撞檢測。優化太

這裏是碰撞檢測和畫瓷磚代碼:

//Function that allows you to draw a tile 
function drawTile(type, x, y, collision){ 
    var tileImg = new Image(); 
    tileImg.onload = function(){ 
     ctx.drawImage(tileImg, x, y) 
    }; 
    tileImg.src = "images/" + type + ".png"; 

    if (collision){ 
     //Worst collision detection ever. 
     if((player_x + player_width == x) && (player_y + player_height > y)){ 
      canMoveRight = false; 
     }else if((player_x == x + 32) && (player_y + player_height > y)){ 
      canMoveLeft = false; 
     }else if((player_y + player_height > y) && (player_x + player_width >= x) && (player_x + player_width <= x + 64)){ 
      player_y = y - player_height; 
     }else{ 
      canMoveRight = true; 
      canMoveLeft = true; 
     } 
    } 
} 

//Draw the map 
function drawMap(){ 
    drawTile("block", 96, 208, true); 
    drawTile("block", 128, 208, true); 
    drawTile("block", 128, 176, true); 
}; 

正如你可以看到,碰撞檢測代碼還挺吮吸。因此,如果您向我展示了更好的製作方法,那也是一件好事。

只是說如果你需要知道的東西。 :)

+1

儘管我同意碰撞檢測代碼的確看起來有些尷尬,但是在不瞭解更多遊戲機制的情況下,不可能真正說出它是否可以以更好的方式完成。在哪方面更好。您可能希望將碰撞檢測放入函數中 - 以這種方式更改檢測代碼更容易。另外,爲了提高速度 - 不要畫出不改變幀的部分,你可能需要在第2(或第3或第10等)畫布上緩存這些部分。這個鏈接提到了多個畫布:http://nicolahibbert.com/optimising-html5-canvas-games/ – enhzflep

回答

1

隨着箱子碰撞檢測,重要的是分別比較x和y。這裏是我如何做類似過去的僞代碼:

var o1 = {x:100, y:229, w:30, h:30}; 
var o2 = {x:100, y:200, w:30, h:30}; 

//Amount of overlap 
leftDist = (o2.x - o2.w/2) - (o1.x + o1.w/2); 
rightDist = (o1.x - o1.w/2) - (o2.x + o2.w/2); 
topDist  = (o2.y - o2.h/2) - (o1.y + o1.h/2); 
bottomDist = (o1.y - o1.h/2) - (o2.y + o2.h/2); 


if(leftDist < 0 && 
    rightDist < 0 && 
    topDist  < 0 && 
    bottomDist < 0){ 

    //Get the closest collision 
    var closest; 
    var direction; //0 = left, 1 = right, 2 = top, 3 = bottom 


    var xDist = o1.x - o2.x; 
    var yDist = o1.y - o2.y; 

    if(xDist < 0) { 
     closest = leftDist; 
     direction = 0; 
    } else { 
     closest = rightDist; 
     direction = 1; 
    } 


    if(yDist < 0) { 
     if(closest < yDist) { 
      closest = topDist; 
      direction = 2; 
     } 
    } else { 
     if(closest < yDist) { 
      closest = bottomDist; 
      direction = 3; 
     } 
    } 



    //Last, jump to the contact position 
    switch(direction) { 

     case 0: 
      o1.x += closest; 
      break; 
     case 1: 
      o1.x -= closest; 
      break; 
     case 2: 
      o1.y += closest; 
      break; 
     case 3: 
      o1.y -= closest; 
      break; 

    } 


} 

讓我知道你是否有任何問題。

11

我知道這聽起來像可怕的開銷,但你應該考慮使用場景圖來處理所有的碰撞檢測,繪圖甚至點擊屏幕上的事件。

場景圖是basicly較1 - 父母正兒關係的樹數據結構(注:每一個HTML頁面的DOM也是一個場景圖)

所以要的形式給出了這一點,你將有一個基本接口或稱爲「節點」的抽象類或其他,代表sceneGraph必須實現的每個節點的接口。再次,它就像dom中的Elements一樣,它們都具有CSS屬性,用於事件處理和位置修飾符的方法。

節點:

{ 
    children: [], 

    update: function() { 
     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].update(); 
     } 
    }, 

    draw: function() { 
     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].draw(); 
     } 
    } 
} 

現在,正如你可能知道,如果你在每馬林,位置或什麼DOM移動一個元素使以往任何時候,所有的子元素全自動去新位置他們的父母,這種行爲是通過轉換繼承來實現的,爲了簡單起見,我不會展示3D轉換矩陣,而只是一個翻譯(一個x,y偏移量)。

節點:

{ 
    children: [], 
    translation: new Translation(), 


    update: function(worldTranslation) { 
     worldTranslation.addTranslation(this.translation); 

     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].update(worldTranslation); 
     } 

     worldTranslation.removeTranslation(this.translation); 
    }, 

    draw: function() { 
     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].draw(); 
     } 
    } 
} 

轉型:

{ 
    x: 0, 
    y: 0, 

    addTranslation: function(translation) { 
     this.x += translation.x; 
     this.y += translation.y; 
    }, 

    removeTranslation: function(translation) { 
     this.x -= translation.x; 
     this.y -= translation.y; 
    } 
} 

(注意,我們不實例化新的翻譯對象,在全球翻譯,因爲它更便宜,只需添加/刪除值)

Now your worldTranslation(or globalTranslatio n)具有節點可以從父節點繼承的所有偏移量。

在我進入碰撞檢測之前,我將演示如何使用此技術繪製精靈。 Basicly你會在你的拉環填充數組與位置圖像對

節點:

{ 
    children: [], 
    translation: new Translation(), 

    image: null, //assume this value is a valid htmlImage or htmlCanvas element, ready to be drawn onto a canvas 
    screenPosition: null, //assume this is an object with x and y values like {x: 0, y: 0} 

    update: function(worldTranslation) { 
     worldTranslation.addTranslation(this.translation); 

     this.screenPosition = {x: worldTranslation.x, y: worldTranslation.y}; 

     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].update(worldTranslation); 
     } 

     worldTranslation.removeTranslation(this.translation); 
    }, 

    draw: function(spriteBatch) { 

     spriteBatch.push({ 
      x: this.screenPosition.x, 
      y: this.screenPosition.y, 
     }); 

     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].draw(spriteBatch); 
     } 
    } 
} 

渲染功能:

function drawScene(rootNode, context) { 
    //clear context here 

    var spriteBatch = []; 
    rootNode.draw(spriteBatch); 

    //if you have to, do sorting according to position.x, position.y or some z-value you can set in the draw function 

    for(var i = 0; i < spriteBatch.length; i++) { 
     var sprite = spriteBatch[i]; 

     context.drawImage(sprite.image, sprite.position.x, sprite.position.y); 
    } 
} 

現在,我們有基本的從sceneGraph理解繪製圖像,我們去碰撞檢測:

首先,我們需要我們的節點有BoundryBoxes:

盒:

{ 
    x: 0, 
    y: 0, 

    width: 0, 
    height: 0, 

    collides: function(box) { 
     return !(
       ((this.y + this.height) < (box.y)) || 
       (this.y > (box.y + box.height)) || 
       ((this.x + this.width) < box.x) || 
       (this.x > (box.x + box.width)) 
      ); 
    } 
} 
在2D遊戲我喜歡有沒有在其節點的座標系boundry盒

,而擁有它知道其絕對值值。 通過CSS解釋: 在CSS中,您可以設置邊距和填充,這些值不依賴於頁面,而只存在於具有這些值的元素的「座標系」中。所以它像有position:absolute和left:10px vs margin-left:10px;在2D中的好處是我們不需要將sceneGraph一直冒泡以找到檢測結果,並且我們不必計算出當前座標系中的框 - >進入世界座標系 - >回到每個節點進行碰撞檢測。

節點:

{ 
    children: [], 
    translation: new Translation(), 

    image: null, 
    screenPosition: null, 

    box: null, //the boundry box 

    update: function(worldTranslation) { 
     worldTranslation.addTranslation(this.translation); 

     this.screenPosition = {x: worldTranslation.x, y: worldTranslation.y}; 

     this.box.x = worldTranslation.x; 
     this.box.y = worldTranslation.y; 
     this.box.width = this.image.width; 
     this.box.height = this.image.height; 

     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].update(worldTranslation); 
     } 

     worldTranslation.removeTranslation(this.translation); 
    }, 

    collide: function(box, collisions) { 
     if(this.box.collides(box)) { 
      collisions.push(this); 
     } 

     //we will optimize this later, in normal sceneGraphs a boundry box asures that it contains ALL children 
     //so we only will go further down the tree if this node collides with the box 
     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].collide(box, collisions); 
     } 
    }, 

    draw: function(spriteBatch) { 

     spriteBatch.push({ 
      x: this.screenPosition.x, 
      y: this.screenPosition.y, 
      image: this.image, 
     }); 

     for(var i = 0; i < this.children.length; i++) { 
      this.children[i].draw(spriteBatch); 
     } 
    } 
} 

衝突功能:

function collideScene(rootNode, box) { 
    var hits = []; 

    rootNode.collide(box, hits); 

    for(var i = 0; i < hits.length; i++) { 
     var hit = hits[i]; 

     //your code for every hit 
    } 
} 

注: 並不是每一個節點來表示圖像,您可以創建節點,只是添加翻譯給它的孩子,就像製作一個沒有視覺效果的DIV容器來安排一堆物體,而只需要e一個人的位置。一個字可以包括:

CharacterRoot (Translation Only) 
    CharacterSprite (Image) 
    Weapon (Image) 
    Shield (Image) 

現在,這些都只是一些基本知識角落找尋場景管理在遊戲中,你可以在google上許多撒姆我這裏使用的,做一些進一步的優化和隨意問任何問題。

+0

很好的答案,希望你得到一些值得讚賞的提議。 – Loktar