简体   繁体   English

在基于2D Tile的游戏中画布碰撞检测不完全

[英]Canvas Collision detection not exact on an 2D Tilebased game

I make a small game with Javascript and Canvas. 我用Javascript和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 我的地图是基于0和1的数组。1是可步行的,0必须阻塞

My player is 32x32 and my tiles too. 我的播放器是32x32,我的磁贴也是。

PosX is x Coordinate from may Player PosY is y Coordinate from may Player PosX是xy可能来自玩家的坐标PosY是y可能来自玩家的坐标

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. 但是我想要的是,当我的玩家用他的32x32的右侧触摸阻塞的Tile的左侧时,他必须停下来。

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. 我们的播放器精灵是红色方块,它以每帧4像素的速度移动。 A you can see the sprite and the blue wall are just 2 pixels apart! 可以看到精灵和蓝色的墙壁相隔仅2个像素! If we move to the right by 4 pixels, the bottom right hits the wall. 如果我们向右移动4个像素,则右下角会碰到墙。 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); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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