【问题标题】:How can I have collision detection on a tilemap in javascript?如何在 javascript 中的 tilemap 上进行碰撞检测?
【发布时间】:2022-09-23 23:23:37
【问题描述】:

我一直在寻找一段时间如何检测我的玩家和我的表中指定的盒子之间的瓷砖地图上的碰撞,但我发现的都是高级教程,我试图尽可能简单地做到这一点我也能理解它是如何工作的。 因此,在我的表中,我只在玩家走在价值为 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();
}
&lt;canvas id=\"canvas\"&gt;&lt;/canvas&gt;

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

  • 您的代码没有尝试检测碰撞...

标签: javascript arrays canvas jstilemap


【解决方案1】:

请参阅下面的更改...

  • 我为宽度添加了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);
}
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

【讨论】:

    【解决方案2】:

    解决方案:

    检查新位置是否不是游戏地图中的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
      }
    }
    

    【讨论】:

    • 我现在正在尝试它不起作用
    • gameMap 不是二维数组,类似 gameMap[0][0] 将不起作用
    • 是的,但是突然之间,我不得不重新考虑绘制地图的方法,我宁愿在 1d 中。
    【解决方案3】:

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

    所以而不是:

        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]
        ];
    

    或者您想构建游戏地图。

    然后,一旦有了这个二维数组,就可以跟踪播放器当前所在位置的行和列索引。

        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. 如果发生碰撞,应该怎么办?如果没有发生碰撞,应该发生什么?我建议在编码时写下你的假设列表,并在你进行时检查它们。特别是在碰撞检测中,很容易因为未经检查的假设而产生意外错误。

      学习资源:

      How can I create a two dimensional array in JavaScript?

    【讨论】:

    • 对我不起作用
    • 什么没用?
    • 当我尝试实现此代码以使用 player_index_x 和 y 保持一维数组时,它不起作用,我无法向左移动。
    • 如果您使用二维数组,它会起作用吗?
    • 您不能只是将数组更改为 2D 数组并期望地图的绘制工作,绘制的逻辑也必须改变
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 2012-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多