Firstly, I wouldn't recommend writing a separate piece of code for each direction. Instead, try to formulate a general solution. Also, if you need more complex collision checking and handling, you might want to use a physics engine like Box2D. But if you want to write the code yourself, here is my explanation:
I assume you are using a rectangle as your player's collision box and that you don't need continuous collision detection (needed for very fast objects). I also assume that going in positive y-direction is going 'up' and going in positive x-direction is going 'right'. I don't know electron, but will try to use obvious variable and function names. Further down I defined some utility functions. The minimal approach I can think of works as follows.
Separate your code into two parts:
- Calculating your player's speed and setting new position
- Detecting and handling collisions
Calculating player's speed and setting new position
Calculate the player's speed as a 2D vector with x- and y- components (this may involve walking, jumping, falling)
// Set horizontal speed depending on user's input
if (key_is_pressed(LEFT) && !keyIsPressed(RIGHT)) {
speed.x = -walk_speed;
} else if (key_is_pressed(RIGHT) && !keyIsPressed(LEFT)) {
speed.x = walk_speed;
} else {
speed.x = 0;
}
// Calculate vertical speed (jumping, gravity)
if (isGrounded) {
key_is_pressed(UP) { // JUMP
isGrounded = false;
speed.y = jump_speed;
}
} else {
speed.y -= gravity; // FALL - accelerate downwards
}
// Move player to new position
x = x + speed.x;
y = y + speed.y;
Detecting and handling collisions
- Check whether the player is colliding with a solid object
- If there is a collision, move the player out of the solid object either horizontally or vertically depending on his/her position relative to the solid object and set the player's respective speed to 0
This pseudocode is a more detailed description of the last step:
// FOR LOOP START
if (check_collision_rectangle(x, y, w, h, solid.x, solid.y, solid.w, solid.h)) {
deltaX = get_x_overlap(x, y, w, h, solid.x, solid.y, solid.w, solid.h);
deltaY = get_y_overlap(x, y, w, h, solid.x, solid.y, solid.w, solid.h);
// Move player out of solid object horizontally or vertically,
// whichever is shorter, and set respective speed to 0
if (Math.abs(deltaX) <= Math.abs(deltaY)) {
x = x + deltaX;
} else {
if (deltaY < 0) { // if player just landed
isGrounded = true;
}
y = y + deltaY;
}
}
// FOR LOOP END
Moving in steps of limited size
Should you face the problem, that the player is penetrating solid objects or behaving weirdly, it might be due to a too big jump from one frame to the next. In this case you could moving the player at steps of a limited size at a time. Do this by moving the player by the step size into the desired direction. This can be done by first normalizing the speed-vector. Then multiply it by step_size and then executing the code inside a loop until the player was moved by the full amount.
let magnitude = Math.pow(speed.x, 2) + Math.pow(speed.y, 2);
let speedNormalizedX = speed.x / magnitude;
let speedNormalizedY = speed.y / magnitude;
let stepSize = 5;
while(magnitude > 0) {
if magnitude >= stepSize {
x = x + speedNormalizedX * stepSize;
y = y + speedNormalizedY * stepSize;
} else {
x = x + speedNormalizedX * magnitude;
y = y + speedNormalizedY * magnitude;
}
magnitude = magnitude - stepSize;
// DO COLLISION CHECKING AND HANDLING HERE
}
Note that this is not very performant, if the step size is small. But it shouldn't cause problems for a basic platformer.
Utility functions
function get_x_overlap(x1, y1, w1, h1, x2, y2, w2, h2) {
leftOverlap = x2 - (x1 + w1);
rightOverlap = x1 - (x2 + w2);
// if not overlapping return 0
if (leftOverlap <= 0 || rightOverlap <= 0) {
return 0;
}
if (leftOverlap < rightOverlap) {
return -leftOverlap;
} else {
return rightOverlap;
}
}
function get_y_overlap(x1, y1, w1, h1, x2, y2, w2, h2) {
bottomOverlap = y2 - (y1 + h1);
topOverlap = y1 - (y2 + h2);
// if not overlapping return 0
if (bottomOverlap <= 0 || topOverlap <= 0) {
return 0;
}
if (bottomOverlap < topOverlap ) {
return -bottomOverlap;
} else {
return topOverlap;
}
}
function check_collision_rectangle(x1, y1, w1, h1, x2, y2, w2, h2) {
// if rect1 is above, below, left or right of rect2, return false
if (y1 > y2 + h2) { // above
return false;
}
if (y1 + h1 < y2) { // below
return false;
}
if (x1 + w1 < x2) { // left
return false;
}
if (x1 > x2 + w2) { // right
return false;
}
// else return true
return true;
}