Search code examples
javascriptprocessingcollision

Player glitches through left side of horizontal moving block


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):

enter image description here

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. enter image description here

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!


Solution

  • 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)