3
\$\begingroup\$

I'm building a basic RPG game and I'm looking at collisions/pickups etc now.

It's tile based and I'm using HTML5 and Javascript.

I use a 2D array to create my tilemap.

I'm currently using a switch statement for whatever key has been pressed to move the player.

I have if statements to stop the player going off the edge of the map and viewport. If the player is about to land on a tile with tileID 3 then the player stops.

Here is the statement:

canvas.addEventListener('keydown', function(e) {
    console.log(e);
    var key = null;
    switch (e.which) {
        case 37:
            // Left
            if (playerX > 0) {
                playerX--;
            }
            if(board[playerX][playerY] == 3){
                playerX++;
            }
            break;
        case 38:
            // Up
            if (playerY > 0) playerY--;
            if(board[playerX][playerY] == 3){
                playerY++;
            }
            break;
        case 39:
            // Right
            if (playerX < worldWidth) {
                playerX++;
            }
            if(board[playerX][playerY] == 3){
                playerX--;
            }
            break;
        case 40:
            // Down
            if (playerY < worldHeight) playerY++;
            if(board[playerX][playerY] == 3){
                playerY--;
            }
            break;
    }

    viewX = playerX - Math.floor(0.5 * viewWidth);
    if (viewX < 0) viewX = 0;
    if (viewX+viewWidth > worldWidth) viewX = worldWidth - viewWidth;


    viewY = playerY - Math.floor(0.5 * viewHeight);
    if (viewY < 0) viewY = 0;
    if (viewY+viewHeight > worldHeight) viewY = worldHeight - viewHeight;


}, false);

Is there a more efficient way of handling collisions, than loads of if statements for each key?

The reason I ask is because I plan on having many items that the player will need to be able to pickup or not walk through like walls cliffs etc.

\$\endgroup\$

3 Answers 3

5
\$\begingroup\$

Collisions need to be detected and resolved after you retrieve the user input and apply all the transformations to the object.

So, for instance, your board would have a bounding box equal to the board width and height. To check for a collision with the outer bounds you would ask if your player object is outside the board's bounding box.

If so, then you need to apply transformations to the object's x and y coordinates so that it is inside the box.

The main conceptual point, I suppose, is that you shouldn't be checking for collisions as a precondition for user input. Instead, you should be checking for collisions as a result of user input, and then applying transformations to resolve it.

\$\endgroup\$
2
  • \$\begingroup\$ Ok so seperate the movements and the CD, Good point thankyou, How about structuring the collisions? LIke in my question, are big if statements the best way or are there others? \$\endgroup\$
    – Tom Burman
    Commented Dec 10, 2012 at 6:29
  • 1
    \$\begingroup\$ If statements are the only way I can think of making that check. It might sound inefficient, but it's best to not estimate the performance of a computer until it starts performing inefficiently. So check the bounds, and if the bounds are exceeded, calculate the overlap and transform the x and y coordinates of the player. If you run into performance issues, you can split the board into sectors so that you're only checking collisions for the area the player is in. \$\endgroup\$ Commented Dec 10, 2012 at 9:55
0
\$\begingroup\$

I usually have an array of solid tiles and a function to check collisions against them.

function inArray(needle, haystack) {
    for (var key in haystack) {
        if (haystack[key] == needle) {
            return true;
        }
    }
    return false;
}

var solidTiles = [3, 6, 14];

function (isSolidTile(x, y) { //x and y are pixel coordinates, not tile coordinates
    var tileX = Math.floor(x / tileWidth);     
    var tileY = Math.floor(y / tileHeight);     
    if (inArray(board[tileX][tileY], solidTiles)) {
        return true;
    }
    return false
}

With this function you could get the coordinates of the player's new location before moving him, check if those coordinates are a solid tile, and then handle it however you want (ie. move the player to the edge of the tile). You could get the coordinates of the tile the player is moving to with something like var newX = player.x + player.xVelocity and var newY = player.y + player.yVelocity.

For colliding with items or other sprites (which I assume are not tiles, but other entities with their own pixel coordinates) you can use a function like:

function collides(sprite1, sprite2) {
    return a.x < b.x + b.width &&
    a.x + a.width > b.x &&
    a.y < b.y + b.height &&
    a.y + a.height > b.y;
}    

//example
if (collides(player, cocoaPuff)) {
    player.cuckoo = true;
}
\$\endgroup\$
0
\$\begingroup\$

Given the x/y of the player and the width/height you can get the position of all four corners, and then to translate to a point on the array do Math.floor((x or y)/tileSize) then you can do

for (let i = topCorner; i < bottomCorner; i++) { //topCorner is the array offset of the top edge, bottomCorner is the offset for the bottom edge.
  for (let j = leftCorner; j < rightCorner; j++) { //leftCorner is the offset for the left corner, rightCorner is the offset for the right corner
    if (tileMap[i] !== undefined && tileMap[i][j] == 0) { //tileMap is the array, check for undefined first to avoid errors.
      //add to x and y
    }
  }
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .