我正在构建一个小型tile引擎游戏。我目前正在实现简单的基于块的碰撞检测,但我遇到了真正的问题。我已经在谷歌上搜索了几个小时,寻找不同的实现方式,但似乎无法理解。我目前的努力(目前只在玩家向右移动时检测到碰撞)基本有效,但允许玩家通过障碍物的底部。碰撞使用法线贴图阵列来检测碰撞,贴图中的任何值为2都是实体对象。
我了解我需要做什么的概念-在移动我的玩家之前,计算玩家最终会进入哪个单元格。检查该单元格的值。如果是2,不要让玩家移动。
我的问题是弄清楚球员最终会在哪个单元格中,因为从技术上讲,在某些点上,球员可以同时在4个单元格中。我试过使用原点和4角检测来解决这个问题,但我就是无法实现。
JS Fiddle HERE-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);
}
}
}
};
您需要通过在当前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;
};
由于每个keydown要移动玩家8个位置,因此在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);
}