Home > Software engineering >  How can I have collision detection on a tilemap in javascript?
How can I have collision detection on a tilemap in javascript?

Time:09-23

I've been looking for some time now how to detect collisions on a tilemap between my player and the box specified in my table, but all I found are advanced tutorials, I'm trying to do this as simply as possible so that I can understand how it works too. In my table, I therefore seek to detect a collision only if the player walks on a box of value 1 (this would be a wall for example). Then the player will not be able to move on this place of my map.

My code:

// 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>

I won't go into too much detail, as I think it's pretty straightforward, but I'm a beginner and really have no idea.

CodePudding user response:

One way to solve this is by changing your game map from a 1 dimensional array to a 2 dimensional array.

So instead of:

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

Make it:

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

Or however you want to structure your game's map.

Then once you have this 2D array, keep track of the row and column index of where your player is currently located.

    let player_index_x = 3;
    let player_index_y = 5;

Update this index whenever the player changes locations; e.g if the player moves up 1, then you subtract 1 from the y index. If the player moves right 1, add one to the x index.

Then collision detection becomes a lot more straightforward because before moving left, right, up, or down, you can check something like:

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

My Recommendations:

  1. Be sure to check first whether or not the move is a valid one, so the player does not fall off the map
  2. If a collision occurs, what should happen? If a collision doesn't occur, what should happen? I recommend writing down a list of your assumptions while coding and checking them as you go. Especially in collision detection, it can be very easy to have unintended bugs from unchecked assumptions.

Resources for Learning to do this:

How can I create a two dimensional array in JavaScript?

CodePudding user response:

Solution:

Check if the new position is not 1 in the game map.

If it's 1 do nothing.

If it's not 1 assign position

Calculating position:

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

Actual code:

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
  }
}
  • Related