Hello there,
I'm developing a HTML5 Canvas Game and am having issues with the method I've taken for collision detection. I can find and retrieve a Tile Object in my Game based on an X and Y position, which led me to believe generating "points" on Entity Sprites and checking each of them for a collision was the best way to go about it. I've had a lot of issues with this and don't really know what else to do.
The points generated on my character look like this when visualised:
Each side of the sprite has points generated for it specifically so I can tell which direction the solid tile is. In the update loop the tiles under each point are fetched and checked for if they are solid, and if they are, it prevents movement in that direction. The problem I'm facing is that when walking left + up in to a wall to the left of the player, the top and bottom collision points are also being triggered due to them being relative to the players position and having the X velocity applied to them before checking.
I can't figure out a way to solve this; below is the code I'm using to generate the points, check them and also correct the X & Y positions of the player.
Generating Points function:
generatePoints: function(config) {
var points = [],
scale = Engine.config('GLOBAL_GRAPHICS_SCALE'),
halfTile = (config.tileWidth / 2) * scale,
tilesWidth = Math.ceil(config.spritesheet.getWidth() / (config.tileWidth / 2)),
tilesHeight = Math.ceil(config.spritesheet.getHeight() / (config.tileWidth / 2)),
x = config.x, y = config.y, yOffset = 0, xOffset = 0,
directions = config.directions,
xVelocity = config.xVelocity, yVelocity = config.yVelocity;
for(var h = 0; h <= tilesHeight; h++) {
yOffset = ((h == 0) ? 1 : ((h >= tilesHeight) ? -1 : 0));
/* Left Side */
if(directions.left) points.push([ (x + xVelocity), (y + yVelocity) + (h * halfTile) + yOffset, 0 ]);
/* Right Side */
if(directions.right) points.push([ (x + xVelocity) + (tilesWidth * halfTile), (y + yVelocity) + (h * halfTile) + yOffset, 1 ]);
}
for(var w = 0; w <= tilesWidth; w++) {
xOffset = ((w == 0) ? 1 : ((w >= tilesWidth) ? -1 : 0));
/* Top */
if(directions.up) points.push([ (x + xVelocity) + (w * halfTile) + xOffset, (y + yVelocity), 2 ]);
/* Bottom */
if(directions.down) points.push([ (x + xVelocity) + (w * halfTile) + xOffset, (y + yVelocity) + (tilesHeight * halfTile), 3 ]);
}
this._points = this._points.concat(points);
return points;
},
Looping through and checking the points function:
checkPointCollision: function(points) {
var tile = null, dir = null, solid = false,
tempX = 0, tempY = 0,
tileWidth = Engine.manager('map').getActive().getTileWidth(),
collision = {
left: { move: true, x: 0, y: 0 },
right: { move: true, x: 0, y: 0 },
up: { move: true, x: 0, y: 0 },
down: { move: true, x: 0, y: 0 }
};
for(var p = 0; p < points.length; p++) { // For all the generated checking points...
solid = false;
dir = points[p][2];
tile = Engine.manager('map').getActive().findTile(points[p][0], points[p][1]);
if(Array.isArray(tile)) {
for(var t = 0; t < tile.length; t++) {
if(tile[t] != null && tile[t].isSolid()) {
solid = true;
break;
}
}
}else if(tile != null) {
solid = tile.isSolid();
}
if(solid) { // If this Tile is Solid...
tempX = points[p][0] - (points[p][0] % tileWidth);
tempY = points[p][1] - (points[p][1] % tileWidth);
switch(dir) { // Switch based on the Tile's direction
case 0: // Left
collision.left.move = false;
collision.left.x = tempX;
collision.left.y = tempY;
break;
case 1: // Right
collision.right.move = false;
collision.right.x = tempX;
collision.right.y = tempY;
break;
case 2: // Up
collision.up.move = false;
collision.up.x = tempX;
collision.up.y = tempY;
break;
case 3: // Down
collision.down.move = false;
collision.down.x = tempX;
collision.down.y = tempY;
break;
}
}
}
return collision;
},
And altogether now (Called in the update loop):
/* Begin checking Collisions */
while(checkCollision) {
var points = this.generatePoints({ x: x, y: y, xVelocity: xVelocity, yVelocity: yVelocity, tileWidth: tileWidth, spritesheet: spritesheet, directions: directions }),
collision = this.checkPointCollision(points),
hasCollision = false;
if(!collision.up.move) {
yVelocity = -((y - collision.up.y) - tileWidth);
hasCollision = true;
}
if(!collision.down.move) {
yVelocity = (collision.down.y - (spritesheet.getHeight() * scale)) - y;
hasCollision = true;
}
if(!collision.left.move) {
xVelocity = 0;
hasCollision = true;
}
if(!collision.right.move) {
xVelocity = 0;
hasCollision = true;
}
cycle++;
if(!hasCollision || cycle >= maxCycle) checkCollision = false;
}
/* End checking Collisions */
Variables should (hopefully) be self explanatory, feel free to ask for any information, any help is appreciated, thank you in advance!