2015-12-16 8 views
5

Ich baue ein kleines Mini-Tile-Engine-Spiel. Momentan arbeite ich an der Implementierung einer einfachen blockbasierten Kollisionserkennung, allerdings habe ich echte Probleme. Ich habe dies stundenlang gegoogelt, indem ich verschiedene Implementierungen betrachtet habe, aber ich kann mich nicht darum kümmern. Meine derzeitigen Bemühungen (nur Kollisionen werden derzeit erkannt, wenn der Spieler sich bewegt rechts), funktioniert meistens, aber erlaubt dem Spieler, durch den unteren Teil des Hindernisses zu gehen. Die Kollision verwendet das normale Karten-Array zum Erkennen von Kollisionen. Jeder Wert von 2 in der Karte ist ein Solid-Objekt.Javascript Canvas Game - Kollisionserkennung

Ich verstehe die Konzepte von dem, was ich tun muss - bevor ich meinen Player bewege, errechne, in welcher Zelle der Spieler landen wird. Prüfe, welcher Wert dieser Zelle zugewiesen wurde. Wenn es 2 ist, darf der Spieler sich nicht bewegen.

Mein Problem ist herauszufinden, in welcher Zelle der Spieler technisch enden wird, an Punkten kann der Spieler in 4 Zellen gleichzeitig sein. Ich habe versucht, mit Herkunft und 4 Eckenerkennung zu umgehen, aber ich kann es einfach nicht funktionieren.

JS Fiddle HIER - https://jsfiddle.net/j1xqxze8/

My-Code;

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

Da Sie den Player 8 Positionen pro Keydown bewegen, müssen Sie in Keydown jede dieser 8 Positionen testen, um zu sehen, ob eine Kollision auftritt. – markE

+0

Ich habe das mit verschiedenen Graden der Geschwindigkeit versucht, einschließlich 1 (um nur die Position einmal zu aktualisieren) und sogar auf dieser Ebene tritt das Problem immer noch auf, wo Sie tief in das Objekt einclipsen können - Danke, aber :) – Lewis

+0

Ich habe eine (grobe, ungetestete) Antwort hinzugefügt, die zeigt, wie man jede Zwischenposition testet, um zu sehen, ob eine Kollision auftritt. – markE

Antwort

1

Da Sie den Player 8 Positionen pro keydown bewegen, in keydown Sie jeden dieser acht Zwischenpositionen testen muss eine Kollision stattfindet, um zu sehen, ob.

Warnung: - (! Wahrscheinlich) nicht getesteten Code einiger Optimierungen erforderlich

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

Danke! Am Ende ging ich mit einer Lösung auf der Grundlage dieser Antwort. Das alles wird mir eines Tages leicht kommen, aber noch nicht;) – Lewis

2

Sie müssen die neue Position des Spielers zu berechnen, indem das Hinzufügen/Subtrahieren Geschwindigkeit zu/von Strom x/y-Position. Dann müssen Sie den Pixelbereich berechnen, der vom Player an der neuen Position abgedeckt wird. Dann müssen Sie den Bereich der Zellen berechnen, der dem Pixelbereich entspricht. Dann müssen Sie den Zellbereich durchlaufen, um zu sehen, ob es Kollisionen gibt. Beachten Sie, dass, wenn die rechts/unten Pixel Berechnung durch den Spieler bedeckt, Sie x/y und Breite/Höhe hinzufügen müssen und dann 1.

ändern subtrahieren ...

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

zu ...

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