简体   繁体   中英

Canvas Collision detection not exact on an 2D Tilebased game

I make a small game with Javascript and Canvas. I have an collision detection but it works not so well. I cant go through walls. So far so good, but it works only on the left and top without overlap.

My map is array based with 0 and 1. 1 is walkable, 0 has to block

My player is 32x32 and my tiles too.

PosX is x Coordinate from may Player PosY is y Coordinate from may Player

Here is my detection Code:

var tileWidth = 32;                                                                                                                     // Fliesen breite festgelegt
var tileHeight = 32;                                                                                                                    // Fliesen höhe festgelegt

    var solidTiles = [0];                                                                                                               // var solidTiles beinhaltet die 0 aus dem array, sagt das die nicht durchdringbar sein sollen (Hol mir quasi die 0 ausem array raus

function isSolidTile(x, y) {                                                                                                            // Funktion zu festlegung das 0 Fließen nicht durchgehbar sind,  x Pixel und y Pixel der fliese
    var tileX = Math.floor(x / tileWidth);                                                                                              // Fließe in X   --> x koordinate / durch die halbe breite, damit wir den mittelpunkt der fliese als festen punkt feststellen
    var tileY = Math.floor(y / tileHeight);                                                                                             // Fließe in Y   --> y koordinate / durch die halbe breite, damit wir den mittelpunkt der fliese als festen punkt feststellen
    var tile = mapKollision[ tileY ][tileX] ;                                                                                           // WICHTIG!: Bei Listen ist auch die Zeile und Spalte einzuhalten. Bei der Abfrage einer Kollision zu erst Y dann X
    if ( tile == 0 )    {                                                                                                               //
        return true;                                                                                                                    //
    } else {return false}                                                                                                               //
}


    var altPosX = PosX;                                                                                                                 // neue variable für die alte helden position 
    var altPosY = PosY; 

if ( isSolidTile( PosX, PosY) ){                                                                                                // if wenn isSolidTile getroffen wird
            PosX = altPosX;                                                                                                             // soll er DIESE PosX in die alte Pos X umwandeln
            PosY = altPosY;                                                                                                             // soll er DIESE PosY in die alte Pos Y umwandeln
    }   

I know i have to say something like

 if( PosX + 32 ) PosX = altPosX -32;

But when i use this, my player bounced back from the right side of the Blocking Tile, and when i go left, my player got through all the blocking tiles left from my player.

But what i want is, that when my player touches with his right side of his 32x32 the left side of an blocking Tile, he has to stop.

And i dont know why it doesn`t work.

If you need more Code, please let me know.

Thanks :)

From what I see I assume your collision detection is just based on a single point of the player sprite - most likely it's center point. To make a more precise collision detection, we need to account for the corner points surrounding that object and the direction it's moving to. Additionally we need to check if we would collide with a solid object like a wall before we actually move it to that position. That way we can place it side-by-side to that object.

Consider this example:

collisionA

Our player sprite is the red square and it's moving at 4 pixels per frame. A you can see the sprite and the blue wall are just 2 pixels apart! If we move to the right by 4 pixels, the bottom right hits the wall. CollisionB

To get around that problem we need to check the sprite's top-right and bottom-right corners for a possible collision if we want to move to the right.

If we detect a collision - again, before we actually moved something on screen - we put the red hero next to the block instead.

collisionC

Heres a sophisticated example. Give it focus by a mouse click and use the cursor keys to move around.

 var whichKey = 0; var tileWidth = 32; var tileHeight = 32; var player = { tileX: 2, tileY: 2, xPos: 0, yPos: 0, speed: 3, width: 24, height: 24, topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0 }; player.xPos = player.tileX * tileWidth + tileWidth / 2 - player.width / 2; player.yPos = player.tileY * tileHeight + tileHeight / 2 - player.height / 2; var map = [ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] ]; var canvas = document.createElement("canvas"); canvas.width = 400; canvas.height = 300; var context = canvas.getContext("2d"); document.body.appendChild(canvas); function updateMap() { context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = "black"; for (var a = 0; a < map.length; a++) { for (var b = 0; b < map[0].length; b++) { if (map[a][b] == 1) { context.fillRect(b * tileWidth, a * tileHeight, tileWidth, tileHeight); } } } context.fillStyle = "red"; context.fillRect(player.xPos, player.yPos, player.width, player.height); player.tileX = Math.floor(player.xPos / tileWidth); player.tileY = Math.floor(player.yPos / tileHeight); } function getCorners(futureX, futureY) { var bottom = Math.floor((futureY + player.height - 1) / tileHeight); var top = Math.floor((futureY) / tileHeight); var left = Math.floor((futureX) / tileWidth); var right = Math.floor((futureX + player.width - 1) / tileWidth); player.topLeft = map[top][left]; player.topRight = map[top][right]; player.bottomLeft = map[bottom][left]; player.bottomRight = map[bottom][right]; } function move(directionX, directionY) { getCorners(player.xPos + player.speed * directionX, player.yPos); if (directionX == -1) { if (player.topLeft == 0 && player.bottomLeft == 0) { player.xPos += player.speed * directionX; } else { player.xPos = player.tileX * tileWidth; } } if (directionX == 1) { if (player.topRight == 0 && player.bottomRight == 0) { player.xPos += player.speed * directionX; } else { player.xPos = (player.tileX + 1) * tileWidth - player.width; } } getCorners(player.xPos, player.yPos + player.speed * directionY); if (directionY == -1) { if (player.topLeft == 0 && player.topRight == 0) { player.yPos += player.speed * directionY; } else { player.yPos = player.tileY * tileHeight; } } if (directionY == 1) { if (player.bottomLeft == 0 && player.bottomRight == 0) { player.yPos += player.speed * directionY; } else { player.yPos = (player.tileY + 1) * tileHeight - player.height; } } } window.addEventListener('keydown', function(e) { whichKey = e.keyCode; }); window.addEventListener('keyup', function(e) { whichKey = 0; }); function loop() { if (whichKey == 37) { move(-1, 0); } if (whichKey == 39) { move(1, 0); } if (whichKey == 38) { move(0, -1); } if (whichKey == 40) { move(0, 1); } updateMap(); } var interval = setInterval(loop, 20); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM