I'm attempting to create a 2D Platformer Game and I'm having difficulty with the physics I have implemented. Each loop my code does the following:
- Apply the physics to a temporary X and Y Co-ordinates to find the new position of the Player
- Generate points around the sprite to check for collisions and assign them directions (Which allows for collision correction based on the direction of the collision)
- Loop through each of the points, find the Tile each point is on and check whether it is solid. If it is, mark that direction (The direction linked to the point, that I created earlier) as solid.
- After checking all of the points, only apply the physics to X and/or Y if no collision is detected for that direction.
My code, simplified, is below:
var tileWidth = 8, // Tile Width/Height
gravity = 0.3, // Map Gravity
friction = 0.8, // Map Friction
speed = 10, // Get the Users Speed
xVelocity = 0, // Get the Players X Velocity
yVelocity = 0, // Get the Players Y Velocity
x = this.x, // Players X Value
y = this.y, // Players Y Value
origX = x, origY = y, // Store the original X & Y Values for later use
jumping = this.jumping, // Get whether the Player is currently jumping
grounded = this.grounded, // Get whether the Player is currently grounded
scale = 4, // Store the Global Graphics scale for rendering
spritesheet = this.spritesheet; // Fetch the Players Sprite
if(isPressed(UP_ARROW)) { // If the Up Arrow Key is pressed
if(!jumping && grounded) { // If the Player is grounded and NOT already Jumping
jumping = true; // They're now Jumping...
grounded = false; // And no longer grounded
yVelocity = -(speed / 2) * 2;
}
}
if(isPressed(RIGHT_ARROW)) { // If the Right Arrow Key is pressed
if(xVelocity < speed) {
xVelocity++;
}
}
if(isPressed(LEFT_ARROW)) { // If the Left Arrow Key is pressed
if(xVelocity > -speed) {
xVelocity--;
}
}
// Apply Gravity and Friction to the X & Y Velocities
xVelocity *= friction;
yVelocity += gravity;
grounded = false; // Reset the grounded value to allow jumping
var points = [],
newX = (x + xVelocity),
newY = (y + yVelocity),
tile = null,
dir = null,
checkedDown = false,
tilesWidth = Math.ceil(spritesheet.getWidth() / (tileWidth / 2)),
tilesHeight = Math.ceil(spritesheet.getHeight() / (tileWidth / 2));
for(var h = 0; h <= tilesHeight; h++) {
points.push([ newX, newY + (h * ((tileWidth / 2) * scale)) + ((h == 0) ? 1 : ((h >= tilesHeight) ? -1 : 0)), 0 ]);
points.push([ newX + (tilesWidth * ((tileWidth / 2) * scale)), newY + (h * ((tileWidth / 2) * scale)) + ((h == 0) ? 1 : ((h >= tilesHeight) ? -1 : 0)), 1 ]);
}
for(var w = 0; w <= tilesWidth; w++) {
points.push([ newX + (w * ((tileWidth / 2) * scale)) + ((w == 0) ? 1 : ((w >= tilesWidth) ? -1 : 0)), newY, 2 ]);
points.push([ newX + (w * ((tileWidth / 2) * scale)) + ((w == 0) ? 1 : ((w >= tilesWidth) ? -1 : 0)), newY + (tilesHeight * ((tileWidth / 2) * scale)), 3 ]);
}
var moveLeft = true,
moveRight = true,
moveUp = true,
moveDown = true,
solid = false;
for(var p = 0; p < points.length; p++) { // For all the generated checking points...
dir = points[p][2];
tile = Engine.manager('map').getActive().findTile(points[p][0], points[p][1]);
solid = tile.isSolid();
if(solid) { // If this Tile is Solid...
switch(dir) { // Switch based on the Tile's direction
case 0: // Left
moveLeft = false;
break;
case 1: // Right
moveRight = false;
break;
case 2: // Up
moveUp = false;
break;
case 3: // Down
moveDown = false;
break;
}
}
}
if(!moveDown) {
yVelocity = 0;
jumping = false;
grounded = true;
}
if(!moveLeft || !moveRight) {
xVelocity = 0;
jumping = false;
}
if(grounded) yVelocity = 0;
// Add the X & Y Velocity to the X & Y values
x += xVelocity;
y += yVelocity;
// Round off our X & Y Value to stop Graphical glitches
x = Math.ceil(x);
y = Math.ceil(y);
(Copied from the Entity Update Loop, the scale
value is for scaling up graphics)
The Collision points generation would generate points like this:
The issue I'm having is that when a player falls and hits the floor, the collision detection for Left and Right is triggered. This is because it checks for collision on Left, Right, Up and Down with the new co-ordinates, and even if they collide with the ground it triggers the Left/Right because they were also in a solid tile at the time. What I want to know is; how should I go about this? I've been trying to think of a different way to approach this but I've had no luck, I'm not great when it comes to physics (Hence the super-simple Friction and Gravity)