EDIT: I rewrote a minimal reproducible example in p5.js, so you can see what's happening here:
let keyInput = []
let player
let block
function setup() {
createCanvas(600, 600)
player = new Player(0, height/2)
block = new Block(200, height-100, 100, 1000)
}
function draw() {
background(220);
player.draw()
block.draw()
player.moveX()
block.checkCollisionsX()
player.moveY()
block.checkCollisionsY()
}
function keyPressed() {
keyInput[keyCode] = true;
}
function keyReleased() {
keyInput[keyCode] = false;
}
function rectCollide(x1, y1, w1, h1, x2, y2, w2, h2) {
return x1 + w1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 < y2 + h2;
}
//BLOCK CONSTRUCTOR
class Block {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.movingSpeed = 3;
this.start = 200
this.stop = 400
}
draw() {
fill(255)
rect(this.x, this.y, this.w, this.h)
this.x += this.movingSpeed;
if (this.x > this.stop) {
this.movingSpeed = -this.movingSpeed;
}
if (this.x < this.start) {
this.movingSpeed = abs(this.movingSpeed);
}
}
checkCollisionsX() {
if (rectCollide(player.pos.x, player.pos.y, player.width, player.height, this.x, this.y, this.w, this.h)) {
if (player.vel.x < 0) { // If the player moved left and collided with the right side of block
player.pos.x = this.x + this.w;
} else { // If the player moved right and collided with the left side of block
player.pos.x = this.x - player.width;
}
player.vel.x = 0;
}
}
checkCollisionsY() {
if (rectCollide(player.pos.x, player.pos.y, player.width, player.height, this.x, this.y, this.w, this.h)) {
if (player.vel.y < 0) {
player.pos.y = this.y + this.h;
player.vel.y *= -yRebound; // Not -1 because collisions are not perfectly elastic
} else {
//player.pos.x += movingSpeed; //Keep player on platform while platform is moving
player.jumps = player.numJumps;
player.pos.y = this.y - player.height;
player.vel.y = 0;
player.acc.y = 0;
}
}
}
}
//PLAYER CONSTRUCTOR
class Player {
constructor(x, y) {
this.width = 50
this.height = 100
// all 3 chars for pretty code
this.pos = new p5.Vector(x, y);
this.vel = new p5.Vector(0, 0);
this.acc = new p5.Vector(0, 0);
this.accSpeed = 0.05;
this.gravity = 0.5;
this.maxVel = 10;
this.jumpForce = 15;
this.friction = 0.15;
this.numJumps = 1;
this.jumps = this.numJumps;
this.isGrounded = false;
this.canMove = true;
this.dir = "DOWN"
}
draw() {
fill(255, 0, 0)
rect(this.pos.x, this.pos.y, this.width, this.height)
}
moveX() {
//MOVE X
if (keyInput[LEFT_ARROW] && this.vel.x > -this.maxVel && this.canMove) {
this.acc.x = -this.accSpeed;
} else if (keyInput[RIGHT_ARROW] && this.vel.x < this.maxVel && this.canMove) {
this.acc.x = this.accSpeed;
} else if (abs(this.vel.x) > 0.2) {
this.acc.x = (this.vel.x < 0) ? this.friction : -this.friction;
} else {
this.vel.x = 0;
this.acc.x = 0;
}
this.vel.x += this.acc.x; // vel += acc
this.pos.x += this.vel.x; // pos += vel
}
moveY() {
//MOVE Y
if (keyInput[UP_ARROW] && this.jumps > 0 && this.canMove) {
this.jumps--;
this.vel.y = -this.jumpForce;
}
this.acc.y += this.gravity;
this.vel.y += this.acc.y;
this.pos.y += this.vel.y;
this.acc.y = 0; // Reset acceleration
if (this.pos.y >= height - this.height) {
this.pos.y = height-this.height
this.jumps = this.numJumps;
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<main>
</main>
</body>
</html>
I'm making a 2D platformer game, and all block collisions work correctly except for the left side of a horizontally moving block. Here's what the block looks like (ignore the lag):
Here's the code that makes up the moving block:
if (type.equals("moving-horizontal-2x1")) {
image(img2x1, pos.x, pos.y, 200, 100);
pos.x += movingSpeed;
if (pos.x > stop) {
movingSpeed = -movingSpeed;
}
if (pos.x < start) {
movingSpeed = abs(movingSpeed);
}
}
When the player collides with the block, it works for all sides except the left side on the X axis, as shown by the image below.
This video shows what happens when the player collides with the moving block: https://www.youtube.com/watch?v=ewVSYd5h4rg
As you can see, the player gets moved to the other side of the block. I don't know why this is happening, since the collision detection/resolution code is the same as the other static blocks. Why does only one side work?
Here is my collision detection code for the X axis:
if (rectCollide(player.pos.x, player.pos.y, player.width, player.height, pos.x, pos.y, width, height)) {
if (player.vel.x < 0) { // If the player moved left and collided with the right side of block
player.pos.x = pos.x + width;
} else { // If the player moved right and collided with the left side of block (this is the broken side)
player.pos.x = pos.x - player.width;
}
player.vel.x = 0;
}
I've tried fixing this issue by incrementing the player's x pos when it collides with the left side, by doing something like player.pos.x += movingSpeed
, but this still doesn't work.
I believe the bug has something to do with how I draw the moving block, since the collision detection/resolution code works perfectly fine with all other static blocks.
Thanks for any help!
The problem occurs when you are slowly moving away from the block, but the block is faster than you. The player's direction of movement therefore says nothing about whether he is to the left or to the right of the obstacle. Don't check the player's velocity, check the player's position relative to the obstacles position:
Replace
if (player.vel.x < 0)
with
if (player.pos.x > this.x)