我需要编写一个非常简单的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.ceil
或Math.floor
则更糟糕。
有什么方法可以改善这种“碰撞检测”,这样角色就不会走过红墙了?
答案 0 :(得分:2)
有一些问题:
首先你应该避免使用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_charX
和new_charY
,并且只接受从charX
,charY
移动到新位置的所有四个点:
(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有效(即移动步骤的一半)。
答案 1 :(得分:1)
我的回答有点不同。我没有使用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!');
}
}
答案 2 :(得分:1)
您需要考虑两个方面:第一个是碰撞检测,第二个是响应。让我们从检测开始。您正在检查单个点,但实际上您有一个瓷砖尺寸,因此您必须考虑厚度。角色的坐标和瓷砖的坐标是左上角。仅比较左上角是不够的,还必须检查其他角落。例如,玩家广场的右侧是charX + config.tileSize
。
第二个方面是碰撞响应。这里可以使用的最简单的机制是检查角色的下一个碰撞位置,如果没有则移动角色。您最好分别检查两个轴,以使角色能够滑动&#34;沿着墙壁(否则直到墙壁对角地移动到墙壁中)。
答案 3 :(得分:1)
您的播放器可能会随时在地图中重叠四个方块。因此,您需要检查所有四个方格中的碰撞(对应于角色的左上角,右上角,左下角,右下角)。为了让它“挤压”走廊(假设角色与瓷砖大小相同),您可能还需要调整一个或两个像素(因此下面的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
答案 4 :(得分:0)
首先,如果您将角色更改为1,则更改瓷砖“值”但是在0中您可以通过键入来检查瓷砖是否可以步行 如果(瓦[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;