如何在javascript中对tilemap进行冲突检测



我一直在寻找如何检测我的播放器和表中指定的框之间的tilemap上的冲突,但我找到的都是高级教程,我试图尽可能简单地做到这一点,以便我也能理解它是如何工作的。因此,在我的表中,只有当玩家走在值为1的盒子上(例如,这将是一堵墙(时,我才会检测到碰撞。然后玩家将无法在我的地图上移动。

我的代码:

// Initi
ctx = null;
var ctx = document.getElementById("canvas").getContext("2d");
// Map
var gameMap = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
var tileW = 40,
tileH = 40;
var mapW = 10,
mapH = 10;
window.onload = function() {
requestAnimationFrame(drawGame);
ctx.font = "bold 10pt sans-serif";
};
// Player
var x = 100;
var y = 100;
var radius = 10;
var upPressed = false;
var downPressed = false;
var leftPressed = false;
var rightPressed = false;
var speed = 1;
function drawPlayer() {
ctx.fillStyle = "green";
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2)
ctx.fill();
}
// Inputs
function inputs() {
if (upPressed) {
y = y - speed;
}
if (downPressed) {
y = y + speed;
}
if (leftPressed) {
x = x - speed;
}
if (rightPressed) {
x = x + speed;
}
}
document.body.addEventListener("keydown", keyDown)
document.body.addEventListener("keyup", keyUp)
function keyDown(event) {
if (event.keyCode == 38) {
upPressed = true;
}
if (event.keyCode == 40) {
downPressed = true;
}
if (event.keyCode == 37) {
leftPressed = true;
}
if (event.keyCode == 39) {
rightPressed = true;
}
if (event.keyCode == 65) {
speedCodePressed = true;
speed = 20;
}
if (event.keyCode == 32) {
shootPressed = true;
}
}
function keyUp(event) {
if (event.keyCode == 38) {
upPressed = false;
}
if (event.keyCode == 40) {
downPressed = false;
}
if (event.keyCode == 37) {
leftPressed = false;
}
if (event.keyCode == 39) {
rightPressed = false;
}
if (event.keyCode == 32) {
shootPressed = false;
}
}
// game map draw function
function drawMap() {
if (ctx == null) {
return;
}
for (var y = 0; y < mapH; ++y) {
for (var x = 0; x < mapW; ++x) {
switch (gameMap[((y * mapW) + x)]) {
case 0:
ctx.fillStyle = "#685b48";
break;
default:
ctx.fillStyle = "#5aa457";
}
ctx.fillRect(x * tileW, y * tileH, tileW, tileH);
}
}
}
// clear screen
function clearScreen() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// game loop
function drawGame() {
requestAnimationFrame(drawGame);
clearScreen();
drawMap();
drawPlayer();
inputs();
}
<canvas id="canvas"></canvas>

我不会说太多细节,因为我认为这很简单,但我是一个初学者,真的不知道。

请参阅下面的更改。。。

  • 我添加了宽度相同的canvas.height = tileH * mapH,以匹配游戏的实际大小
  • 创建了一个新对象var player = { x: 100, y: 100 , radius: 10, speed: 1 },您应该将与玩家相关的所有内容都保留在该对象中
  • 我使用Path2D来创建我们绘制的结构(墙(和用于碰撞的路径
  • 碰撞是用isPointInPath检测到的。点击此处了解更多信息:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath
  • 我还将gameMap更改为二维数组,现在我们正在使用Path2D,这让一切都变得更容易,虽然不是真正需要的,但我更喜欢这种方式

var canvas = document.getElementById("canvas")
var tileW = 40
var tileH = 40
var mapW = 10
var mapH = 10
var ctx = canvas.getContext("2d");
var upPressed = false;
var downPressed = false;
var leftPressed = false;
var rightPressed = false
var player = { x: 100, y: 100, radius: 10, speed: 1 }
var gameMap = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 1, 0, 0, 0, 1, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
var path = new Path2D()
var walls = new Path2D()
window.onload = function() {
canvas.height = tileH * mapH
canvas.width = tileW * mapW
for (var y = 0; y < mapH; ++y) {
for (var x = 0; x < mapW; ++x) {
if (gameMap[y][x]) {
path.rect(x * tileW- player.radius, y * tileH- player.radius, tileW + player.radius*2, tileH + player.radius*2)
walls.rect(x * tileW, y * tileH, tileW, tileH)
}
}
}
requestAnimationFrame(drawGame);
};
function drawPlayer() {
ctx.fillStyle = "green";
ctx.beginPath();
ctx.arc(player.x, player.y, player.radius, 0, Math.PI * 2)
ctx.fill();
}
function inputs() {
var newx = player.x
var newy = player.y
if (upPressed) newy = player.y - player.speed;
if (downPressed) newy = player.y + player.speed;
if (leftPressed) newx = player.x - player.speed;
if (rightPressed) newx = player.x + player.speed;
if (!ctx.isPointInPath(path, newx, newy)) {
player.x = newx;
player.y = newy;
}
}
document.body.addEventListener("keydown", keyDown)
document.body.addEventListener("keyup", keyUp)
function keyDown(event) {
if (event.keyCode == 38) upPressed = true;
if (event.keyCode == 40) downPressed = true;
if (event.keyCode == 37) leftPressed = true;
if (event.keyCode == 39) rightPressed = true;
}
function keyUp(event) {
if (event.keyCode == 38) upPressed = false;
if (event.keyCode == 40) downPressed = false;
if (event.keyCode == 37) leftPressed = false;
if (event.keyCode == 39) rightPressed = false;
}
function drawGame() {
ctx.fillStyle = "#685b48"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#5aa457"
ctx.fill(walls)
//ctx.stroke(path);
drawPlayer();
inputs();
requestAnimationFrame(drawGame);
}
<canvas id="canvas"></canvas>

解决方案:

检查新位置是否不是游戏地图中的1

如果是1,什么也不做。

如果不是1分配位置

计算位置:

Math.floor(y / tileH) // y
Math.floor(x / tileW) // x

实际代码:

function inputs() {
let newX = x
let newY = y
if(upPressed) {
newY -= speed
}
if(downPressed) {
newY += speed
}
if(leftPressed) {
newX -= speed
}
if(rightPressed) {
newX += speed
}
if (gameMap[Math.floor(newY / tileH)][Math.floor(newX / tileW)] !== 1) {
x = newX
y = newY
}
}

解决此问题的一种方法是将游戏地图从一维数组更改为二维数组。

所以不是:

var gameMap = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];

成功:

let gameMap = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 0, 1, 0, 0, 0, 1, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];

或者无论你想如何构建你的游戏地图。

然后,一旦你有了这个2D数组,就要跟踪你的玩家当前所在的行和列索引。

let player_index_x = 3;
let player_index_y = 5;

每当玩家改变位置时,更新此索引;例如,如果玩家向上移动1,那么你就从y指数中减去1。如果玩家向右移动1,则在x索引上加1。

然后碰撞检测变得更加简单,因为在向左、向右、向上或向下移动之前,您可以检查以下内容:

if(left_pressed)
{
// make sure that it is indeed possible to move left
if(player_index_x > 1)
{
if(gameMap[player_index_x - 1][player_index_y] == 1)
{
// collision detected, do not move, return if in function
}
else
{
// move player
player_index_x -= 1;
}
}
}

我的建议:

  1. 一定要先检查一下这个动作是否有效,这样玩家就不会从地图上掉下来
  2. 如果发生碰撞,该怎么办?如果没有发生碰撞,该怎么办?我建议在编码时写下你的假设列表,并在进行检查时进行检查。特别是在冲突检测中,由于未经检查的假设,很容易出现意外的错误

学习资源:

如何在JavaScript中创建二维数组?

最新更新