繁体   English   中英

HTML Canvas游戏:2D碰撞检测

[英]HTML Canvas game: 2D collision detection

我需要编写一个非常简单的2D HTML画布游戏,其中包含一个角色和一些墙壁。 地图(俯视图)是一个多维数组( 1=walls

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,0,0,1,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,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,1,1,0,0,0],
  [1,0,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,1,0,0,0,0,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,1],
  [1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1],
  [1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1],
  [1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1],
  [0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,1],
  [1,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,1],
  [1,1,1,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,1],
  [1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,1,0,0,1,1],
  [1,1,1,1,1,1,1,1,1,1,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]
];

角色不应该走过墙壁......所以他应该只走“0”。 我已经得到了地图渲染和角色的行走部分工作得很好,但我还不知道如何检查碰撞。

JSBin上可以找到一个非常简单的版本。 您可以使用箭头键或WASD移动(黑色方块)。

我已经尝试过使用这样的东西进行非常简单的碰撞检测:

function checkCollision( x, y ) {
  if ( map[ Math.round( x ) ][ Math.round( y ) ] !== 0 ) {
    return true; // Collision
  }
  return false;
}

但这并不是很有效(参见JSBin )。 使用Math.round ,角色和墙壁重叠......如果我使用Math.ceilMath.floor那就更糟了。

有什么方法可以改善这种“碰撞检测”,这样角色就不能走过红墙了?

有一些问题:

首先,你应该避免使用0.1作为坐标步骤,因为它是浮点数中的“坏数字”(当以二进制表示时它是周期性的)。 更好的是0.125(1/8)。 添加/减去1/8将保证您的数字始终保持1/8精确倍数,而不会累积任何错误。

你应该定义一个“ok(x,y)”函数来检查(可能是小数点)点(x,y)是有效还是在墙内......一个简单的实现可能是:

return map[y|0][x|0] == 0; //  <expr>|0 is used to convert to integer

最后你应该计算new_charXnew_charY ,只接受从charXcharY移动到新位置,如果所有四个点:

(new_charX+s, new_charY+s)
(new_charX+1-s, new_charY+s)
(new_charX+s, new_charY+1-s)
(new_charX+1-s, new_charY+1-s)

s = 1/16时有效(即移动步骤的一半)。

示例: http//jsbin.com/wipimidije/edit?js,output

我的回答有点不同。 我没有使用2D数组,而是使用一个简单的数组并计算行数(如果你甚至需要它们)。 现在,您只需要在作为actor位置的一个索引处检查地图数组:

var map = [0,0,0,1,1,0,0,1,1,0,0,...];

//heres how to calculate the x/y if you need it for something else
var pos_x = position % NUM_OF_ROWS;
var pos_y = Math.floor(position / NUM_OF_ROWS);

//for collisions now you just check the value of the array at that index
leftKey.addEventListener('keypress', function() { 
    test(position - 1);
});
rightKey.addEventListener('keypress', function() { 
    test(position + 1);
});
upKey.addEventListener('keypress', function() { 
    test(position + NUM_OF_ROWS);
});
downKey.addEventListener('keypress', function() { 
    test(position - NUM_OF_ROWS);
});

function test(n) {
    if (map[n] === 0) {
        //if there's no collision, update the position.
        position = n;
    } else {
        console.log('Collided!');
    }
}

您需要考虑两个方面:第一个是碰撞检测,第二个是响应。 让我们从检测开始。 您正在检查单个点,但实际上您有一个瓷砖尺寸,因此您必须考虑厚度。 角色的坐标和瓷砖的坐标是左上角。 仅比较左上角是不够的,还必须检查其他角落。 例如,玩家广场的右侧是charX + config.tileSize

第二个方面是碰撞响应。 这里可以使用的最简单的机制是检查角色的下一个碰撞位置,如果没有则移动角色。 您最好分别检查两个轴,以使角色沿着墙壁“滑动”(否则直到在对角线移动到墙壁中时卡在墙壁中)。

您的播放器可能随时在地图中重叠四个方块。 因此,您需要检查所有四个方格中的碰撞(对应于角色的左上角,右上角,左下角,右下角)。 为了让它“挤压”通过走廊(假设角色与瓷砖的大小相同),您可能还需要将其调整一个或两个像素(因此下面的1 / config.tileSize )。

function checkCollision( x, y ) {
  var x1 = Math.floor(x + 1 / config.tileSize), 
      y1 = Math.floor(y + 1 / config.tileSize),
      x2 = Math.floor(x + 1 - 1 / config.tileSize), 
      y2 = Math.floor(y + 1 - 1 / config.tileSize);
  if (map[y1][x1] !== 0 || map[y2][x1] !== 0 || map[y1][x2] !== 0 || 
      map[y2][x2] !== 0) {
    return true; // Collision
  }
  return false;
}

看到这个版本的JSBin

首先,如果你将角色更改为1,那么我会更改瓷砖的“值”但是在0中你可以通过输入If(tile [x] [y])来检查瓷砖是否可以走路...然后,我会,首先计算下一个位置,然后如果玩家能够......

Var nextpos = new position;

If(KEYLEFT){
    Nextpos.x = currpos - 1;
}

If(nextpos > 0 && nextpos < mapsize && tile[nextpos.x][nextpos.y])
    Player.pos = nextpos;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM