2015-12-16 17 views
5

私は小さなミニタイルエンジンゲームを構築しています。私は現在、単純なブロックベースの衝突検出の実装に取り​​組んでいますが、私は本当の問題を抱えています。私はこれを複数回試してみましたが、さまざまな実装を見ていますが、私の周りに頭を浮かべているようには見えません。私の現在の努力(現在のところ、プレーヤーがを右に移動したときの衝突を検出するだけで、)はほとんど機能しますが、プレーヤーは障害物の底部を通過することができます。コリジョンでは、通常のマップ配列を使用して衝突を検出し、マップ内の2の値はすべてソリッドオブジェクトです。Javascript Canvas Game - 衝突検出

私はプレイヤーを移動する前に、プレイヤーがどのセルに入るかを計算します。そのセルに割り当てられている値を確認します。 2の場合は、プレーヤーの移動を許可しないでください。

私の問題は、プレイヤーが技術的にどんなセルになるのかをポイント上で把握することです。プレイヤーは同時に4つのセルに入ることができます。私は起源と4コーナー検出を使用してこれを回避しようとしましたが、私はそれを働かせることはできません。 HERE

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ごとに8つのポジションを移動しているので、キーダウンでは、8つのポジションのそれぞれをテストして衝突が発生するかどうかを確認する必要があります。 – markE

+0

私はこれを試してみましたが、速度を変えてみましたが(1度しか位置を更新しないように)、このレベルでさえも、深くオブジェクトにクリップインできる場所に問題があります - ありがとう、:) – Lewis

+0

私は、衝突が発生したかどうかを確認するために各暫定的な位置をテストする方法を示す(粗い、テストされていない)答えを追加しました。 – markE

答えて

1

あなたがプレイヤーにKeyDownイベントごとに8つの位置を移動しているので、KeyDownイベントであなたは、衝突が発生したかどうかを確認するために、これらの8つの中間位置のそれぞれをテストする必要があります。

警告:未テストコード - いくつかの調整(!たぶん)あなたは現在のx/yの位置から/までの速度を差し引い/追加することによって、プレイヤーの新しい位置を計算する必要が

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

を必要としていました。次に、新しい位置でプレーヤーがカバーしているピクセルの範囲を計算する必要があります。次に、ピクセルの範囲に対応するセルの範囲を計算する必要があります。次に、セルの範囲をループして、衝突がないかどうかを調べる必要があります。

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

に...

...プレイヤーでカバー右/下のピクセルを計算するとき、あなたはX/Yと幅/高さを追加し、1

変更を減算する必要があることに注意してください

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