HTML画布游戏:2D碰撞检测



我需要用一个角色和几个墙来编程一个非常简单的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)

在CCD_ 11=1/16(即移动步长的一半)的情况下是有效的。

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

您的玩家可能随时在地图中重叠四个方块。因此,您需要检查所有四个正方形(对应于角色的左上角、右上角、左下角和右下角)是否发生碰撞。为了允许它在走廊中"挤压"(假设角色与瓷砖大小相同),您可能还需要将其调整一个或两个像素(因此下面的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

我的答案有点不同。我会使用一个简单的数组来计算行(如果你需要的话),而不是使用2D数组。现在,您只需要检查作为参与者位置的一个索引处的映射数组:

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中行走,我会更改tile的"值",但在0中,您可以通过键入如果(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;

最新更新