Javascript HTMLJS画布游戏:瓷砖碰撞错误使玩家传送上来

Javascript HTMLJS画布游戏:瓷砖碰撞错误使玩家传送上来,javascript,html,canvas,collision-detection,tiles,Javascript,Html,Canvas,Collision Detection,Tiles,我是一个游戏开发的初学者,一直在努力在一组瓷砖和一个玩家矩形之间完成碰撞。这个游戏的特点是跳跃和重力。首先,碰撞可以工作,但非常笨重。有时,当玩家最终到达一块瓷砖的顶部并稍微靠近边缘时,它会立即传送到右侧或左侧(取决于边缘/角落),然后掉落。当与瓷砖底部碰撞时也会发生这种情况;玩家将立即传送到侧面,并进一步向上移动。据我所知,磁贴碰撞检测器会将碰撞与一侧或另一侧混淆,因为当玩家撞击磁贴边缘时,检测器会将其视为与两侧碰撞,并根据最高坐标速度(也称为speedX和speedY)决定将玩家放置在其他位

我是一个游戏开发的初学者,一直在努力在一组瓷砖和一个玩家矩形之间完成碰撞。这个游戏的特点是跳跃和重力。首先,碰撞可以工作,但非常笨重。有时,当玩家最终到达一块瓷砖的顶部并稍微靠近边缘时,它会立即传送到右侧或左侧(取决于边缘/角落),然后掉落。当与瓷砖底部碰撞时也会发生这种情况;玩家将立即传送到侧面,并进一步向上移动。据我所知,磁贴碰撞检测器会将碰撞与一侧或另一侧混淆,因为当玩家撞击磁贴边缘时,检测器会将其视为与两侧碰撞,并根据最高坐标速度(也称为speedX和speedY)决定将玩家放置在其他位置。我通过设置speedY=0来解决这个问题,每次它碰到一块瓷砖的顶部,这就解决了这个问题,但是又出现了另一个问题。现在,如果玩家在一块瓷砖的上面,然后摔倒,很快又向后扫射,它不会与瓷砖的侧面碰撞,但会很快再次回到瓷砖的上面

我只是需要一些关于如何解决这个问题的建议,因为我尝试的每件事都会导致另一个问题。我听说这是开发基于2D瓷砖的游戏时的一个常见错误

下面是一个JSFIDLE,代码正在运行:

下面是我全部代码的显示:

function startGame() {
    gameArea.start();
    actor = new player(32, 32, "green", 32, 32);
}

var mapArray = [
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,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,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,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,0,0,0,0,0,1,1,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,1,1,1,0,0,0,1,1,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]

];

var levelRows = 20;
var levelCols = 20;
 var gameArea = {
     canvas : document.getElementById('canvas'),

     start : function() {
         this.context = this.canvas.getContext("2d");
         document.body.insertBefore(this.canvas, document.body.childNodes[0]);
         requestAnimationFrame(updateGameArea);
         window.addEventListener('keydown', function (e) {
             gameArea.keys = (gameArea.keys || []);
             gameArea.keys[e.keyCode] = true;
         });

         window.addEventListener('keyup', function (e) {
             gameArea.keys = (gameArea.keys || []);
             gameArea.keys[e.keyCode] = false;
         })
     },

     clear : function(){
         this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
     },
     render : function() {
         context = this.canvas.getContext("2d");
         var tileSize = 32;
         for(i=0;i<levelRows;i++){
             for(j=0;j<levelCols;j++){
                 if(mapArray[i][j]==1){
                     context.fillStyle = "gray";
                     context.fillRect(j*tileSize,i*tileSize,tileSize,tileSize);
                 }
             }
         }
     }
 };

function TileCollisionManager(object) {
    let tileSize = 32;
    let baseCol = Math.floor(object.x / tileSize);
    let baseRow = Math.floor(object.y / tileSize);
    let colOverlap = object.x % tileSize;
    let rowOverlap = Math.floor(object.y % tileSize);

    if (object.speedX > 0) { 
        if ((mapArray[baseRow][baseCol + 1] && !mapArray[baseRow][baseCol]) ||
            (mapArray[baseRow + 1][baseCol + 1] && !mapArray[baseRow + 1][baseCol] && rowOverlap)) {
            object.x = baseCol * tileSize;
        }
    }

    if (object.speedX < 0) {
        if ((!mapArray[baseRow][baseCol + 1] && mapArray[baseRow][baseCol]) ||
            (!mapArray[baseRow + 1][baseCol + 1] && mapArray[baseRow + 1][baseCol] && rowOverlap)) {
            object.x = (baseCol + 1) * tileSize;
        }
    }

    if (object.speedY > 0) { 
        if ((mapArray[baseRow + 1][baseCol] && !mapArray[baseRow][baseCol]) || 
            (mapArray[baseRow + 1][baseCol + 1] && !mapArray[baseRow][baseCol + 1] && colOverlap)) {
            object.y = ((baseRow) * tileSize);
            object.jumping = false;
            object.speedY = 0;
        }
    }

    if (object.speedY < 0) { 
        if ((!mapArray[baseRow + 1][baseCol] && mapArray[baseRow][baseCol]) ||
            (!mapArray[baseRow + 1][baseCol + 1] && mapArray[baseRow][baseCol + 1] && colOverlap)) {
            object.y = (baseRow + 1) * tileSize;
            object.speedY = 5;
        }
    }
 }
  function updateGameArea() {
      gameArea.clear();
      gameArea.render();
      actor.update();
      actor.newPos();
      actor.speedX = 0;
      actor.speedY += actor.gravity;


      if (gameArea.keys && gameArea.keys[39]) {
          actor.speedX = 4;
      }
      if (gameArea.keys && gameArea.keys[37]) {
          actor.speedX = -4;
      }
      if (gameArea.keys && gameArea.keys[32]) { 
          if (!actor.jumping) {
              actor.jumping = true;
              actor.speedY = -actor.speed * 3;
          }
      }


      TileCollisionManager(actor);
      requestAnimationFrame(updateGameArea);
  }


  function player (width, height, color, x, y) { 
      this.width = width;
      this.height = height;
      this.x = x;
      this.y = y;
      this.speedX=0;
      this.speedY=0;
      this.gravity=0.3;
      this.speed=3;
      this.jumping=false;
      this.color = color;
      this.update = function () {
          ctx = gameArea.context;
          ctx.fillStyle = this.color;
          ctx.fillRect(
              this.x,
              this.y,
              this.width, this.height);
      };
      this.newPos = function () {
          this.x += this.speedX;
          this.y += this.speedY;
      };
函数startName(){
gameArea.start();
演员=新玩家(32,32,“绿色”,32,32);
}
变量映射数组=[
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,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,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,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,0,0,0,0,0,1,1,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,1,1,1,0,0,0,1,1,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]
];
var levelRows=20;
var levelCols=20;
var游戏区={
canvas:document.getElementById('canvas'),
开始:函数(){
this.context=this.canvas.getContext(“2d”);
document.body.insertBefore(this.canvas,document.body.childNodes[0]);
requestAnimationFrame(updateGameArea);
window.addEventListener('keydown',函数(e){
gameArea.keys=(gameArea.keys | | |[]);
gameArea.keys[e.keyCode]=true;
});
window.addEventListener('keyup',函数(e){
gameArea.keys=(gameArea.keys | | |[]);
gameArea.keys[e.keyCode]=false;
})
},
清除:函数(){
this.context.clearRect(0,0,this.canvas.width,this.canvas.height);
},
render:function(){
context=this.canvas.getContext(“2d”);
var tileSize=32;
对于(i=0;i0){
if((mapArray[baseRow+1][baseCol]&&!mapArray[baseRow][baseCol])||
(mapArray[baseRow+1][baseCol+1]&&!mapArray[baseRow][baseCol+1]&&colOverlap)){
object.y=((基线)*tileSize);
object.jumping=false;
object.com=0;
}
}
如果(object.asp<0){
if((!mapArray[baseRow+1][baseCol]&&mapArray[baseRow][baseCol])||
(!mapArray[baseRow+1][baseCol+1]&&mapArray[baseRow][baseCol+1]&&colOverlap)){
object.y=(baseRow+1)*tileSize;
对象1=5;
}
}
}
函数updateGameArea(){
gameArea.clear();
gameArea.render();
actor.update();
actor.newPos();
actor.speedX=0;
actor.speedY+=actor.gravity;
if(gameArea.keys&&gameArea.keys[39]){
actor.speedX=4;
}
if(gameArea.keys&&gameArea.keys[37]){
actor.speedX=-4;
}
if(gameArea.keys&&gameArea.keys[32]){
如果(!演员跳跃){
actor.jumping=true;
actor.speedY=-actor.speed*3;
}
}
TileCollisionManager(演员);
requestAnimationFrame(updateGameArea);
}
功能播放器(宽度、高度、颜色、x、y){
这个。宽度=宽度;
高度=高度;
这个.x=x;
这个。y=y;
这个。speedX=0;
这个.speedY=0;
这个。重力=0.3;
这个。速度=3;
这个.跳跃=错误;
这个颜色=颜色;
this.update=函数(){
ctx=gameArea.context;
ctx.fillStyle=this.color;
ctx.fillRect(
这个,x,,
这个,嗯,,
这个。宽度,这个。高度);
};
this.newPos=函数(){
this.x+=this.speedX;
this.y+=this.speedY;
};
为您提供快速解决方案。 我已经看到你第三次发布这个问题了。你没有得到答案,因为最好的解决方案是相当多的代码,非常复杂,并且需要对代码进行大量更改

所以我所做的是创建一个非常快速和简单的解决方案

以正确的顺序解决碰撞。 我没有在移动结束时检查位置,而是将代码更改为在移动的每个像素处检查。这是必要的,因为当玩家从一个位置移动到下一个位置时,你必须以正确的顺序找到碰撞。如果你在顶部或底部之前撞到一侧的墙,或者在顶部或底部之前撞到另一侧的墙,则会产生差异,而我