2015-12-16 148 views
5

我正在建造一個小迷你瓷磚引擎遊戲。我目前正在執行簡單的基於塊的碰撞檢測,但是我遇到了實際問題。我已經搜索了幾個小時看看不同的實現,但似乎無法讓我的頭靠近它。我目前的努力(當前只有當玩家移動時檢測到碰撞正確),主要是工作,但允許玩家穿過障礙的底部。碰撞使用法線貼圖陣列來檢測碰撞,在貼圖中任何2的值都是一個固體對象。JavaScript的帆布遊戲 - 碰撞檢測

我理解我需要做的概念 - 在我移動我的玩家之前,計算玩家最終會進入的單元格。檢查分配給該單元格的值。如果它是2,不要讓玩家移動。

我的問題是確定玩家在技術上會以什麼樣的單元格結束,在點上,玩家可以同時在4個單元格中。我已經嘗試過使用起點和4角點檢測來解決這個問題,但我無法使其工作。

JS小提琴這裏 - https://jsfiddle.net/j1xqxze8/

我的代碼;

var Player = function() { 
     this.width = 16; 
     this.height = 16; 
     this.position = {}; 
     this.position.x = 32; 
     this.position.y = 32; 
     this.speed  = 8; 

     this.render = function() { 
      window.context.fillStyle = 'white'; 
      window.context.fillRect(this.position.x, this.position.y, this.width, this.height); 
     }; 

     var _self = this; 

     this.didCollide = function(dir) { 
      if(dir == 'right'){ 
       var newBlock = window.tileMap.getCell(Math.floor((_self.position.x + _self.width)/32), Math.floor((this.position.y + this.height/2)/32)); 

       if(newBlock == 2) 
        return true; 
      } 
     }; 

     window.addEventListener('keydown', function(e) { 
      if(e.keyCode == 38 || e.keyCode == 87){ 
       _self.position.y -= _self.speed; 
      } 

      if(e.keyCode == 40 || e.keyCode == 83){ 
       _self.position.y += _self.speed; 
      } 

      if(e.keyCode == 37 || e.keyCode == 65){ 
       _self.position.x -= _self.speed; 
      } 

      if(e.keyCode == 39 || e.keyCode == 68){ 
       if(!_self.didCollide('right')){ 
        _self.position.x += _self.speed; 
       } 
      } 
     }) 
    }; 

var TileMap = function() { 
    this.map = [ 
     [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 
     [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 
    ]; 

    this.tileSize = 32; 
    this.colors = ['black', 'red', 'green']; 

    this.getCell = function(x, y){ 
     return this.map[y][x]; 
    }; 

     this.render = function(){ 
      for(var x = 0; x < this.map.length; x++){ 
       for(var y = 0; y < this.map.length; y++){ 
        // SWAP Y AND X IN THE FILLSTYLE DUE TO BACKWARDS/MIRRORED JS ARRAY READING 
        window.context.fillStyle = this.colors[this.map[y][x]]; 
        window.context.fillRect((x * this.tileSize) - window.camera.position.x, (y * this.tileSize) - window.camera.position.y, this.tileSize, this.tileSize); 

        window.context.strokeStyle = 'yellow'; 
        window.context.strokeRect((x * this.tileSize) - window.camera.position.x, (y * this.tileSize) - window.camera.position.y, this.tileSize, this.tileSize); 
       } 
      } 
     } 
    }; 
+1

由於您在keydown每個keydown移動玩家8個位置,您必須測試這8個位置中的每個位置以查看是否發生碰撞。 – markE

+0

我已經嘗試過不同程度的速度,包括1(以便只更新一次位置),甚至在這個級別,問題仍然會發生在你可以深入到對象的位置 - 謝謝,雖然:) – Lewis

+0

我添加了一個(粗略的,未經測試的)答案,顯示如何測試每個臨時位置以查看是否發生碰撞。 – markE

回答

1

因爲你是移動玩家每8個的keydown位置,在KEYDOWN必須測試每個那些8箇中轉倉,看看是否發生碰撞。

警告: - (!可能)未經測試的代碼一些調整需要

window.addEventListener('keydown', function(e) { 
    // save x,y before the move 
    var beginningX=_self.position.x; 
    var beginningY=_self.position.y; 

    // test each interim positon between the beginning & 
    // current position for collisions 
    // if a collision occurs, stop at the collision position 
    if(e.keyCode == 38 || e.keyCode == 87){ 
     _self.position.y -= _self.speed; 
     _self.position.y = testInterimVerticalCollisions(
      beginningY, _self.position.y, _self.position.x); 
    } 

    if(e.keyCode == 40 || e.keyCode == 83){ 
     _self.position.y += _self.speed; 
     _self.position.y = testInterimVerticalCollisions(
      beginningY, _self.position.y, _self.position.x); 
    } 

    if(e.keyCode == 37 || e.keyCode == 65){ 
     _self.position.x -= _self.speed; 
     _self.position.x = testInterimHorizontalCollisions(
      beginningX, _self.position.x, _self.position.y); 
    } 

    if(e.keyCode == 39 || e.keyCode == 68){ 
     _self.position.x += _self.speed; 
     _self.position.x = testInterimHorizontalCollisions(
      beginningX, _self.position.x, _self.position.y); 
     } 
    } 
}) 

// test if any interim movement caused a collision 
// if yes, return the x that caused the collision 
// if no, return the ending x 
function testInterimHorizontalCollisions(beginningX,endingX,y){ 
    for(var x=beginningX;x<=endingX;x++){ 
     // TODO: adjust for camera position offset 
     var cellX = parseInt(x/cellWidth); 
     var cellY = parseInt(y/cellHeight); 
     if(getCell(cellX,cellY)==2){return(x);} 
    } 
    return(endingX); 
} 

// test if any interim movement caused a collision 
// if yes, return the y that caused the collision 
// if no, return the ending y 
function testInterimVerticalCollisions(beginningY,endingY,x){ 
    for(var y=beginningY;y<=endingY;y++){ 
     // TODO: adjust for camera position offset 
     var cellX = parseInt(x/cellWidth); 
     var cellY = parseInt(y/cellHeight); 
     if(getCell(cellX,cellY)==2){return(y);} 
    } 
    return(endingY); 
} 
+1

謝謝!我結束了基於這個答案的解決方案。這一切都會在一天之內輕鬆呈現給我,但還沒有;) – Lewis

2

您需要通過添加/從當前X/Y軸減速度/計算玩家的新位置。然後,您需要計算玩家在新位置覆蓋的像素範圍。然後,您需要計算與像素範圍對應的像元範圍。然後,您需要遍歷單元格區域以查看是否有任何碰撞。需要注意的是計算玩家所涵蓋的右邊/底部的像素時,你需要添加X/Y和寬度/高度,然後減去1

更改...

this.didCollide = function(dir) { 
    if(dir == 'right'){ 
     var newBlock = window.tileMap.getCell(Math.floor((_self.position.x + _self.width)/32), Math.floor((this.position.y + this.height/2)/32)); 
     if(newBlock == 2) 
      return true; 
    } 
}; 

到...

this.didCollide = function(dir) { 
    if(dir == 'right'){ 
     var col1 = Math.floor((_self.position.x + _self.speed)/32); 
     var col2 = Math.floor((_self.position.x + _self.speed + _self.width - 1)/32); 
     var row1 = Math.floor((_self.position.y)/32); 
     var row2 = Math.floor((_self.position.y + _self.height - 1)/32); 
     document.getElementById("player").textContent = "player: " + _self.position.x + " " + _self.position.y + " " + _self.width + " " + _self.height; 
     document.getElementById("cells").textContent = "cells: " + col1 + " " + col2 + " " + row1 + " " + row2; 
     for (var c = col1; c <= col2; c++) { 
      for (var r = row1; r <= row2; r++) { 
       var newBlock = window.tileMap.getCell(c, r); 
       if(newBlock == 2) { 
        return true; 
       } 
      } 
     } 
    } 
    return false; 
};