2d Square to rectangle collision detection and action(physics)
Asked Answered
D

3

7

so I made this way to detect and react to squares moving and touching other squares. It uses Pythagorean theorem to make a third parameter so you don't get two true if statements when squares touch. I recently have been attempting to use this method on a square to a rectangle, and cannot seem to get it working. I have drawn lines to help visualize what the code is doing. Anybody have any suggestions on how to get this collision working properly?

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

let gravity = 1.5;
let friction = 0.9;

//CHARACTER:
class Player {
    constructor(x, y, w, h, vx, vy, c, j) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.vx = vx;
        this.vy = vy;
        this.color = c;
        this.jumping = j;
    }
    draw() {
      context.fillStyle = this.color;
      context.fillRect(this.x, this.y, this.w, this.h);
    }
    canvasCollision() {
        if (this.x <= 0) this.x = 0;
        if (this.y <= 0) this.y = 0;
        if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
        if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
    }
    update() {
        this.draw(); 
        this.vy += gravity;
        this.x += this.vx;
        this.y += this.vy;
        this.vx *= friction;
        this.vy *= friction;
        this.canvasCollision() //must be after other updates
    }
}

let player1 = new Player(75, canvas.height/2 + 75, 75, 75, 0, 0, '#8DAA9D', false); 

function controlPlayer1(obj) {
    //this order matters. If update is before jump then obj won't jump when on top of other block.
    if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
    if (controller1.left1) { obj.vx -= 0.5 };
    if (controller1.right1) { obj.vx += 0.5 };
    obj.update();
}

//MOVEMENT:
class Controller {
    constructor() {
        this.left1  = false;
        this.up1    = false;
        this.right1 = false;

        this.down1  = false;

        let controller1 = (e) => {
              if (e.code === 'KeyD')   { this.right1 = e.type === 'keydown' }
            if (e.code === 'KeyA')   { this.left1 = e.type === 'keydown' }
            if (e.code === 'KeyW')   { this.up1 = e.type === 'keydown' }    
            if (e.code === 'KeyS')  { this.down1 = e.type === 'keydown' }       
        }
        
    window.addEventListener('keydown', controller1);
    window.addEventListener('keyup', controller1);

    }
}

let controller1 = new Controller();

//PLATFORM
class Platform {
  constructor(x, y, w, h, yv, c) {
      this.x = x;
      this.y = y;
      this.w = w;
      this.h = h;
      this.yv = yv;
      this.color = c;
  }
  draw(){
    context.fillStyle = this.color;
    context.fillRect(this.x, this.y, this.w, this.h);
  }
  update(){
    this.draw();
    this.y += this.yv;
    this.yv *= 0.9;// friction
  }
}

let platform1 = new Platform(canvas.width/2, canvas.height - 150, 100, 30, 0, '#202020');
let platform2 = new Platform (canvas.width/4, canvas.height/2, 75, 75, 0, '#202020');

//COLLISION DETECTION
function platformDetection(obj, obj2){
 //center point of each side of obj1
 let objLeft = {x: obj.x,  y: obj.y + obj.h/2};
 let objTop = {x: obj.x + obj.w/2, y: obj.y};
 let objRight = {x: obj.x + obj.w, y: obj.y + obj.h/2};
 let objBottom = {x: obj.x + obj.w/2, y: obj.y + obj.h};
 //center point of each side a obj2
 let obj2Left = {x: obj2.x, y: obj2.y + obj2.h/2};
 let obj2Top = {x: obj2.x + obj2.w/2, y: obj2.y};
 let obj2Right = {x: obj2.x + obj2.w, y: obj2.y + obj2.h/2};
 let obj2Bottom = {x: obj2.x + obj2.w/2, y: obj2.y + obj2.h};
 //distance between obj1 and obj2 opposing sides
 let rightDistX = objRight.x - obj2Left.x;
 let rightDistY = objRight.y - obj2Left.y;
 let leftDistX = objLeft.x - obj2Right.x;
 let leftDistY = objLeft.y - obj2Right.y;
 let topDistX =  objTop.x - obj2Bottom.x;
 let topDistY = objTop.y - obj2Bottom.y;
 let bottomDistX = objBottom.x - obj2Top.x;
 let bottomDistY = objBottom.y - obj2Top.y;
 //pythagorean theorem for distance. dRight is from the right side of obj1 to the left of obj2. the rest follow suit.
 let dRight = Math.sqrt(rightDistX*rightDistX + rightDistY*rightDistY);
 let dLeft = Math.sqrt(leftDistX*leftDistX + leftDistY*leftDistY);
 let dTop = Math.sqrt(topDistX*topDistX + topDistY*topDistY);
 let dBottom = Math.sqrt(bottomDistX*bottomDistX + bottomDistY*bottomDistY);
 //Math.min return the smallest value thus variable minimum will be which ever sides are closest together
 let minimum = Math.min(dRight, dLeft, dBottom, dTop);
 let val = 0;
 //compare minimum to pythagorean theorem and set val based on which ever side is closest
 if (dTop == minimum) {
  val = 1;
  //the context stuff can be deleted. It's just here for visual. The if statements can be one line each.
  context.lineWidth = 2;
  context.strokeStyle = 'blue';
  context.beginPath();
  context.moveTo(objTop.x, objTop.y); 
  context.lineTo(obj2Bottom.x, obj2Bottom.y);
  context.stroke();
}
else if (dRight == minimum) {
  val = 2;
  context.strokeStyle = 'orange';
  context.beginPath();
  context.moveTo(objRight.x, objRight.y); 
  context.lineTo(obj2Left.x, obj2Left.y);
  context.stroke();
}
else if (dBottom == minimum) {
  val = 3;
  context.strokeStyle = 'green';
  context.beginPath();
  context.moveTo(objBottom.x, objBottom.y); 
  context.lineTo(obj2Top.x, obj2Top.y);
  context.stroke();
}
else if (dLeft == minimum) {
  val = 4;
  context.strokeStyle = 'pink';
  context.beginPath();
  context.moveTo(objLeft.x, objLeft.y); 
  context.lineTo(obj2Right.x, obj2Right.y);
  context.stroke();
}
 //pass the objects and val 
 platformAction(obj, obj2, val);
}

//ACTION
function platformAction(obj, obj2, val){
//player1 top to player2 bottom
if (obj.y <= obj2.y + obj2.h && obj2.y + obj2.h >= obj.y && val == 1) {
  obj2.y = obj.y - obj2.h; 
  obj.y = obj2.y + obj2.h;
  obj2.vy = 0;
  obj2.jumping = false;
  obj.jumping = true;
}
//player1 right to player2 left
if (obj.x + obj.w >= obj2.x && obj2.x <= obj.x + obj.w && val == 2) {
  obj2.x = obj.x + obj.w;
  obj.x = obj2.x - obj.w - 1;
  obj2.vx = 0;
}
//player1 bottom to player2 top
if (obj.y + obj.h >= obj2.y && obj2.y <= obj.y + obj.h && val == 3) {
  obj.y = obj2.y - obj.h;
  obj2.y = obj.y + obj.h;
  obj.vy = 0;
  obj.jumping = false;
  obj2.jumping = true;
}
//player1 left to player2 right
if (obj.x <= obj2.x + obj2.w && obj2.x + obj2.w >= obj.x && val == 4) {
  obj2.x = obj.x - obj2.w;
  obj.x = obj2.x + obj2.w + 1;
  obj.vx = 0;
  obj2.vx = 0;
}
}

function initObj(obj){
obj.update();
}

function loop() {
  context.clearRect(0, 0, canvas.width, canvas.height); 
  context.fillStyle = 'grey';
  context.fillRect(0, 0, canvas.width, canvas.height);

  //PLATFORM
  initObj(platform1);
  initObj(platform2);

  //PLATFORM DETECTION
  platformDetection(player1, platform1);
  platformDetection(player1, platform2);

  //PLAYER
  controlPlayer1(player1); 

  requestAnimationFrame(loop)
}
loop();
<!DOCTYPE html>
<html>
  <head>

    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
    <title>Tone.io</title>
    <style>
    body {
      height:100vh;
      width:100vh;
      margin: 0;
    }
    </style>
  </head>
 
  <body>

    <canvas id="canvas"></canvas>

    <script src = "Js/Tone.js" type = "text/javascript"></script>

  </body>

</html>
Damoiselle answered 7/5, 2021 at 19:26 Comment(13)
it's way easier than how you are doing it: #306816Maxiemaxilla
@Berto99 No, this will not work in this case. Whenever certain sides collide, I am reassigning their position to prevent the overlap. The sides must include the entire length of the side, because that causes an issue when you jump next to the square(or rectangle), and will cause it to not complete the full jump. I have messed with this for over 2 months now, and a third parameter is the only way to make it possible. But thank you for the suggestion. If I have made a misconception, pls tell me.Damoiselle
Please upvote this to gather further attention to the issue, because this is laid out as simply as possible with extremely detailed comments.Damoiselle
I've upvotes... but i suggest you to create a minimal reproducible example, with test input and expected output if you want to have more help (because there is way to much code in your question that probably does not serve any function to the problem itself)Maxiemaxilla
@Berto99 This code is purely minimalistic and everything's purpose is commented on. It's less than 200 lines of code. It's like reading a paragraph or two of a book. And there is a snippet you can play at the bottom to visualize it. You can also copy that snippet and use it on your IDE if you need, which I suggest. If u need clarity on the objective: It's simply to simulate collision between a moving rectangle and a square without any bugs making unfluid movement or visual overlap between the shapes. Thanks for your input :).Damoiselle
maybe a better fit for codereview.stackexchange.com ?Phenacite
@Phenacite it would only be on-topic for CR when the code is working as expected.Zirconium
Can you specify what, exactly, is not working? What is the expected output? What is happening instead?Finsteraarhorn
@JohnPaulR I have a square and a rectangle. I want to be able to make collision detection and action that allows the collision with the rectangle like the square given in the sample code. My method of collision detection works with squares, but not rectangles, that's the issue, and I have been trying to brainstorm alternative ways to do this for a square to a rectangle. Basically, Expected output: square to rectangle to act like square to square in the given example. Current output: Square to rectangle is not acting like square to square in the given example.Damoiselle
@Phenacite Did u read the question? There is some theory that I do not understand and am having trouble implementing. I'm posting the question to see if anyone else can brainstorm a solution or alternative theories about how to make it function. It is not completed code, so it's pointless to post on that forum.Damoiselle
@Damoiselle What is the difference in how they behave? Currently the only difference that I see is that controllable square gets "stuck" against the rectangle when approaching from the right-hand side mid-jump. And this is simply because of the lines saying obj.vx = 0; obj2.vx = 0; in the val==4 conditional in your platformAction function.Finsteraarhorn
I believe, conceptually, using "distance to midpoint" only reliably works for collision detection for shapes where all side lengths are equal, though.Finsteraarhorn
@JohnPaulR Thx for the insight, that does seem to be the conceptual issue. Currently I'm unaware of any other collision detection that will work with the physics and reactions of the squares when colliding.Damoiselle
O
4

@MPdoor2 "I made...". Anywho, when I gave you that code I did specifically say I had hacked it from a method I created to be used on tilemaps. The method works flawless for that purpose although there is more to the code and yes it is built for squares since that's what tiles map mainly are.

I have been playing with alternate methods of doing CD. Here's a shorter method that (so far) seems to be working well. This method still determines the distance between each side but in a different way. Once the broadphase determines a collision has occurred it calls the narrow phase and whichever side has the shortest distance is the side being penetrated. i.e. when you collide with another block from the player right to the object left we know that even the Y axis penetrates (top and bottom corners of player). This calculates the distance between all three and since the distance between X would be 0 it is the shortest and the CD for moving the player in the Y direction does not get called.

Try the snippet below and see if this works for you.

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

canvas.width = 700;
canvas.height = 500;

let gravity = 1.5;
let friction = 0.9;

//CHARACTER:
class Player {
    constructor(x, y, vx, vy, c, j) {
        //each player must have separate values for control purposes i.e. velocity/jump/attack etc.
        this.x = x;
        this.y = y;
        this.w = 50;
        this.h = 50;
        this.vx = vx;
        this.vy = vy;
        this.color = c;
        this.jumping = j;
        this.center = {x: this.x + this.w/2,  y: this.y + this.h/2};
        this.topLeft = {x: this.x, y: this.y};
        this.topRight = {x: this.x + this.w, y: this.y};
        this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
        this.bottomLeft = {x: this.x, y: this.y + this.h};
    }
    draw() {
       context.fillStyle = this.color;
       context.fillRect(this.x, this.y, this.w, this.h);
    }
    canvasCollision() {
        if (this.x <= 0) this.x = 0;
        if (this.y <= 0) this.y = 0;
        if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
        if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
    }
    update() {
        this.draw(); 
        this.vy += gravity;
        this.x += this.vx;
        this.y += this.vy;
        this.vx *= friction;
        this.vy *= friction;
        this.center = {x: this.x + this.w/2,  y: this.y + this.h/2};
        this.topLeft = {x: this.x, y: this.y};
        this.topRight = {x: this.x + this.w, y: this.y};
        this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
        this.bottomLeft = {x: this.x, y: this.y + this.h};
        this.canvasCollision() //must be after other updates
    }
}
let player = new Player(0, 0, 0, 0, 'red', false); 

function controlPlayer1(obj) {
    if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
    if (controller1.left1) { obj.vx -= 0.5 };
    if (controller1.right1) { obj.vx += 0.5 };
    obj.update();
}

class Platform {
    constructor(x,y,w,h) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.center = {x: this.x + this.w/2,  y: this.y + this.h/2};
        this.topLeft = {x: this.x, y: this.y};
        this.topRight = {x: this.x + this.w, y: this.y};
        this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
        this.bottomLeft = {x: this.x, y: this.y + this.h};
    }
    draw() {
        context.fillStyle = 'blue';
        context.fillRect(this.x, this.y, this.w, this.h);
    }
}

let platforms = [];
function createPlatforms() {
    for (let i=0; i<4; i++) {
       platforms.push(new Platform(200 * i, 450 - (i * 75), 100, 25));
    }
}
createPlatforms();

let platform1 = platforms.push(new Platform(300, 400, 50, 100));
let platform2 = platforms.push(new Platform(400, 400, 50, 50));

//MOVEMENT:
class Controller {
    constructor() {
        this.left1  = false;
        this.up1    = false;
        this.right1 = false;
        this.left2  = false;
        this.up2    = false;
        this.right2 = false;

        let controller1 = (e) => {
            if (e.code === 'ArrowRight') { this.right1 = e.type === 'keydown' }
            if (e.code === 'ArrowLeft')  { this.left1 = e.type === 'keydown' }
            if (e.code === 'ArrowUp')    { this.up1 = e.type === 'keydown' }           
        }
    
    window.addEventListener('keydown', controller1);
    window.addEventListener('keyup', controller1);
    }
}
let controller1 = new Controller();

function collisionDetection(obj) {
    if (player.x + player.w < obj.x ||
        player.x > obj.x + obj.w ||
        player.y + player.h < obj.y ||
        player.y > obj.y + obj.h) {
            return
        }
        narrowPhase(obj);
}

function narrowPhase(obj) {
    let playerTop_ObjBottom = Math.abs(player.y - (obj.y + obj.h));
    let playerRight_ObjLeft = Math.abs((player.x + player.w) - obj.x);
    let playerLeft_ObjRight = Math.abs(player.x - (obj.x + obj.w));
    let playerBottom_ObjTop = Math.abs((player.y + player.h) - obj.y);

    if ((player.y <= obj.y + obj.h && player.y + player.h > obj.y + obj.h) && (playerTop_ObjBottom < playerRight_ObjLeft && playerTop_ObjBottom < playerLeft_ObjRight)) {
        player.y = obj.y + obj.h;
        player.vy = 0;
    }
    if ((player.y + player.h >= obj.y && player.y < obj.y) && (playerBottom_ObjTop < playerRight_ObjLeft && playerBottom_ObjTop < playerLeft_ObjRight)) {
        player.y = obj.y - player.h; 
        player.jumping = false;
        player.vy = 0;
    }
    if ((player.x + player.w >= obj.x && player.x < obj.x) && (playerRight_ObjLeft < playerTop_ObjBottom && playerRight_ObjLeft < playerBottom_ObjTop)) {
        player.x = obj.x - player.w;
        player.vx = 0; 
    }
    if ((player.x <= obj.x + obj.w && player.x + player.w > obj.x + obj.w) && (playerLeft_ObjRight < playerTop_ObjBottom && playerLeft_ObjRight < playerBottom_ObjTop)) {
        player.x = obj.x + obj.w;
        player.vx = 0; 
    }
}

function animate() {
    context.clearRect(0, 0, canvas.width, canvas.height); 
    context.fillStyle = 'grey';
    context.fillRect(0, 0, canvas.width, canvas.height);
    controlPlayer1(player); 
    for (let i=0;i<platforms.length;i++) {
        platforms[i].draw();
        collisionDetection(platforms[i])
    };
    requestAnimationFrame(animate)
}
animate();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height">
    <title>CD</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="squareRectCollision2.js"></script>
</body>
</html>
Over answered 14/5, 2021 at 13:45 Comment(5)
I apologize for writing "I made", after reconstructing what you showed me for a while, lead me to the same code you had, and I said "I made" subconsciously. You have full credit, and thank you again for the help and insightDamoiselle
Hey, I made a youtube video on it too: youtube.com/watch?v=bVqgLa3z9eg&t=23s . Feel free to credit yourself or whoever you got it from :) thx againDamoiselle
Hey that’s a good video from what I saw so far. You learn quick. Now you’ll have to make another one with the new stuff. So glad it worked out for you. It’s hard to find a good vanilla js tutorial for CD that doesn’t just change the color of the block but actually prevents penetration.Over
BTW I tried that “partial sides” technique also. Didn’t work well for me either. Lol. If I end up refactoring the formula that I posted above I will come back and update.Over
I will for sure make another video! AND ALSO, When you made the array, that was somthing I was trying to figure out aswell!!! in the looping function I can add platforms[i].y += 1; and make all the platforms move at once, im going to use that to make the platforms move when the square jumpsDamoiselle
R
1

To detect collisions between rectangles, use the position of their sides. For instance, they "collide vertically" if:

  • object1's bottom side is below object2's bottom, and object1's top is above object2's bottom
  • or object1's top side is above object2's top, and object1's bottom is below object2's top

Consider they do collide if they collide both "vertically" and "horizontally".

Now once you've determined that, you need to detect from which edge. I'm using the closest one here, evaluating the distance for all that "collide vertically" or "horizontally".

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

canvas.width = Math.min(window.innerWidth - 3, 800);
canvas.height = Math.min(window.innerHeight - 3, 200);

let gravity = 1.5;
let friction = 0.9;

//CHARACTER:
class Player {
  constructor(x, y, w, h, vx, vy, c, j) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.vx = vx;
    this.vy = vy;
    this.color = c;
    this.jumping = j;
  }
  draw() {
    context.fillStyle = this.color;
    context.fillRect(this.x, this.y, this.w, this.h);
  }
  canvasCollision() {
    if (this.x <= 0) this.x = 0;
    if (this.y <= 0) this.y = 0;
    if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
    if (this.y + this.h >= canvas.height) {
      this.y = canvas.height - this.h;
      this.vy = 0;
      this.jumping = false
    };
  }
  update() {
    this.draw();
    this.vy += gravity;
    this.x += this.vx;
    this.y += this.vy;
    this.vx *= friction;
    this.vy *= friction;
    this.canvasCollision() //must be after other updates
  }
}

let player1 = new Player(75, canvas.height / 2 + 75, 75, 75, 0, 0, '#8DAA9D', false);

function controlPlayer1(obj) {
  //this order matters. If update is before jump then obj won't jump when on top of other block.
  if (controller1.up1 && !obj.jumping) {
    obj.vy -= 25;
    obj.jumping = true
  };
  if (controller1.left1) {
    obj.vx -= 0.5
  };
  if (controller1.right1) {
    obj.vx += 0.5
  };
  obj.update();
}

//MOVEMENT:
class Controller {
  constructor() {
    this.left1 = false;
    this.up1 = false;
    this.right1 = false;

    this.down1 = false;

    let controller1 = (e) => {
      if (e.code === 'KeyD') {
        this.right1 = e.type === 'keydown'
      }
      if (e.code === 'KeyA') {
        this.left1 = e.type === 'keydown'
      }
      if (e.code === 'KeyW') {
        this.up1 = e.type === 'keydown'
      }
      if (e.code === 'KeyS') {
        this.down1 = e.type === 'keydown'
      }
    }

    window.addEventListener('keydown', controller1);
    window.addEventListener('keyup', controller1);

  }
}

let controller1 = new Controller();

//PLATFORM
class Platform {
  constructor(x, y, w, h, yv, c) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.yv = yv;
    this.color = c;
  }
  draw() {
    context.fillStyle = this.color;
    context.fillRect(this.x, this.y, this.w, this.h);
  }
  update() {
    this.draw();
    this.y += this.yv;
    this.yv *= 0.9; // friction
  }
}

let platform1 = new Platform(canvas.width / 2, canvas.height - 150, 100, 30, 0, '#202020');
let platform2 = new Platform(canvas.width / 4, canvas.height / 2, 75, 75, 0, '#202020');

//COLLISION DETECTION
function platformDetection(obj, obj2) {

  let ol1 = obj.x;
  let ot1 = obj.y;
  let or1 = ol1 + obj.w;
  let ob1 = ot1 + obj.h;
  let ol2 = obj2.x;
  let ot2 = obj2.y;
  let or2 = ol2 + obj2.w;
  let ob2 = ot2 + obj2.h;

  let [collideTop, collideBottom, collideRight, collideLeft] = [-1, -1, -1, -1];

  if (ob1 >= ob2) {
    if (ot1 <= ob2) {
      collideTop = ob2 - ot1;
    }
  }
  if (ol1 <= ol2) {
    if (or1 >= ol2) {
      collideRight = or1 - ol2;
    }
  }
  if (ot1 <= ot2) {
    if (ob1 >= ot2) {
      collideBottom = ob1 - ot2;
    }
  }
  if (or1 >= or2) {
    if (ol1 <= or2) {
      collideLeft = or2 - ol1;
    }
  }
  let min = minOfVals(collideTop, collideRight, collideBottom, collideLeft);
  let val = 0;
  if (min >= 0) {
    if (min == collideTop && (collideLeft >= 0 || collideRight >= 0)) {
      val = 1;
    } else if (min == collideRight && (collideTop >= 0 || collideBottom >= 0)) {
      val = 2;
    } else if (min == collideBottom && (collideLeft >= 0 || collideRight >= 0)) {
      val = 3;
    } else if (min == collideLeft && (collideTop >= 0 || collideBottom >= 0)) {
      val = 4;
    }
  }
  if (val) console.log(val, min, collideTop, collideRight, collideBottom, collideLeft);
  //pass the objects and val 
  platformAction(obj, obj2, val);
}

function minOfVals(...vals) {
  let min = -1;

  function isBelowMin(v) {
    return v >= 0 && (min < 0 || v < min);
  }
  for (v of vals) {
    if (isBelowMin(v)) min = v;
  }
  return min;
}

//ACTION
function platformAction(obj, obj2, val) {
  //player1 top to player2 bottom
  if (val == 1) {
    console.log("colliding from top");
    obj2.y = obj.y - obj2.h;
    //obj.y = obj2.y + obj2.h; //useless
    obj2.vy = 0;
    obj2.jumping = false;
    obj.jumping = true;
  }
  //player1 right to player2 left
  if (val == 2) {
    console.log("colliding from right");
    obj2.x = obj.x + obj.w;
    obj.x = obj2.x - obj.w - 1;
    obj2.vx = 0;
  }
  //player1 bottom to player2 top
  if (val == 3) {
    console.log("colliding from bottom");
    obj.y = obj2.y - obj.h;
    //obj2.y = obj.y + obj.h; //useless
    obj.vy = 0;
    obj.jumping = false;
    obj2.jumping = true;
  }
  //player1 left to player2 right
  if (val == 4) {
    console.log("colliding from left");
    obj2.x = obj.x - obj2.w;
    obj.x = obj2.x + obj2.w + 1;
    obj.vx = 0;
    obj2.vx = 0;
  }
}

function initObj(obj) {
  obj.update();
}

function loop() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = 'grey';
  context.fillRect(0, 0, canvas.width, canvas.height);

  //PLATFORM
  initObj(platform1);
  initObj(platform2);

  //PLATFORM DETECTION
  platformDetection(player1, platform1);
  platformDetection(player1, platform2);

  //PLAYER
  controlPlayer1(player1);

  requestAnimationFrame(loop)
}
loop();
<!DOCTYPE html>
<html>

<head>

  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
  <title>Tone.io</title>
  <style>
    html {
      margin: 0;
      padding: 0;
    }
    body {
      height: 100vh;
      width: 100vh;
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>

  <canvas id="canvas"></canvas>

  <script src="Js/Tone.js" type="text/javascript"></script>

</body>

</html>

There's still a different behavior when your controlled player comes from the left or from the right: push speed is not the same. But that's not due to collisions, so I'm letting you figure out this one (maybe it's on purpose too).

Rabin answered 14/5, 2021 at 14:41 Comment(2)
Thank you so much for your help! I wish I could give more points out so you could also have some reward, but the man that wrote the other answer helped me out on a previous question, so I must give him the reward. Thank you againDamoiselle
Welcome. Just read it and use whatever helps youRabin
D
0

Yo, thx Justin for a solution. I took that solution and also added it to a previous question I asked, which was how to do the collision detection and action for two squares that have movement (with arrows and wasd). This is how I added it:

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

let gravity = 1.5;
let friction = 0.9;

//CHARACTER:
class Player {
   constructor(x, y, w, h, vx, vy, c, j) {
       this.x = x;
       this.y = y;
       this.w = w;
       this.h = h;
       this.vx = vx;
       this.vy = vy;
       this.color = c;
       this.jumping = j;

       this.center = {x: this.x + this.w/2,  y: this.y + this.h/2};
       this.topLeft = {x: this.x, y: this.y};
       this.topRight = {x: this.x + this.w, y: this.y};
       this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
       this.bottomLeft = {x: this.x, y: this.y + this.h};
   }
   draw() {
     context.fillStyle = this.color;
     context.fillRect(this.x, this.y, this.w, this.h);
   }
   canvasCollision() {
       if (this.x <= 0) this.x = 0;
       if (this.y <= 0) this.y = 0;
       if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
       if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
   }
   update() {
       this.draw(); 
       this.vy += gravity;
       this.x += this.vx;
       this.y += this.vy;
       this.vx *= friction;
       this.vy *= friction;
       this.canvasCollision() //must be after other updates
   }
}

let player1 = new Player(canvas.width/2, canvas.height/2 + 75, 75, 75, 0, 0, 'darkgrey', false); 
let player2 = new Player(75, canvas.height/2 + 75, 75, 75, 0, 0, '#8DAA9D', false); 


function controlPlayer1(obj) {
   //this order matters. If update is before jump then obj won't jump when on top of other block.
   if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
   if (controller1.left1) { obj.vx -= 0.5 };
   if (controller1.right1) { obj.vx += 0.5 };
   obj.update();
}

function controlPlayer2(obj) {
   if (controller2.up2 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
   if (controller2.right2) { obj.vx += 0.5 };
   if (controller2.left2) { obj.vx -= 0.5 };
   obj.update();
}

//MOVEMENT:
class Controller {
   constructor() {
       this.left1  = false;
       this.up1    = false;
       this.right1 = false;

       this.left2  = false;
       this.up2    = false;
       this.right2 = false;

       this.down  = false;

       let controller1 = (e) => {
           if (e.code === 'ArrowRight') { this.right1 = e.type === 'keydown' }
           if (e.code === 'ArrowLeft')  { this.left1 = e.type === 'keydown' }
           if (e.code === 'ArrowUp')    { this.up1 = e.type === 'keydown' }   
           if (e.code === 'ArrowDown')      { this.down = e.type === 'keydown' }          
       }
       
       let controller2 = (e) => {
           if (e.code === 'KeyD')   { this.right2 = e.type === 'keydown' }
           if (e.code === 'KeyA')   { this.left2 = e.type === 'keydown' }
           if (e.code === 'KeyW')   { this.up2 = e.type === 'keydown' }    
           if (e.code === 'KeyS')  { this.down = e.type === 'keydown' }         
       }  

   window.addEventListener('keydown', controller1);
   window.addEventListener('keyup', controller1);
   window.addEventListener('keydown', controller2);
   window.addEventListener('keyup', controller2);
   }
}

let controller1 = new Controller();
let controller2 = new Controller();

//COLLISION DETECTION
function collisionDetection(obj, obj2){
 if (obj.x + obj.w <= obj2.x ||
   obj.x >= obj2.x + obj2.w ||
   obj.y + obj.h < obj2.y ||
   obj.y > obj2.y + obj2.h)
   return
collisionAction(obj, obj2);
}

//ACTION 
function collisionAction(obj, obj2){
   let playerTop_ObjBottom = Math.abs(obj.y - (obj2.y + obj2.h));
   let playerRight_ObjLeft = Math.abs((obj.x + obj.w) - obj2.x);
   let playerLeft_ObjRight = Math.abs(obj.x - (obj2.x + obj2.w));
   let playerBottom_ObjTop = Math.abs((obj.y + obj.h) - obj2.y);
   
   //virtical obj 1 top 
   if ((obj.y <= obj2.y + obj2.h && obj.y + obj.h > obj2.y + obj2.h) && (playerTop_ObjBottom < playerRight_ObjLeft && playerTop_ObjBottom < playerLeft_ObjRight)) {
       obj2.y = obj.y - obj2.h;
       obj2.jumping = false;
       obj2.vy = 0;}
       
   //virtical obj 1 bottom 
   if ((obj.y + obj.h >= obj2.y && obj.y < obj2.y) && (playerBottom_ObjTop < playerRight_ObjLeft && playerBottom_ObjTop < playerLeft_ObjRight)) {
       obj.y = obj2.y - obj.h; 
       obj.jumping = false;
       obj.vy = 0;}

   //horizontal obj 1 right 
   if ((obj.x + obj.w >= obj2.x && obj.x < obj2.x) && (playerRight_ObjLeft < playerTop_ObjBottom && playerRight_ObjLeft < playerBottom_ObjTop)) {
     obj2.x = obj.x + obj.w;
     obj.x = obj2.x - obj.w;
     obj.vx = 0;
     obj2.vx = 0;}

   //horizontal obj1 left 
   if ((obj.x <= obj2.x + obj2.w && obj.x + obj.w > obj2.x + obj2.w) && (playerLeft_ObjRight < playerTop_ObjBottom && playerLeft_ObjRight < playerBottom_ObjTop)) {
     obj2.x = obj.x - obj2.w;
     obj.x = obj2.x + obj2.w;
     obj.vx = 0;
     obj2.vx = 0;}
}

function loop() {
 context.clearRect(0, 0, canvas.width, canvas.height); 
 context.fillStyle = 'grey';
 context.fillRect(0, 0, canvas.width, canvas.height);

 //PLAYER
 controlPlayer1(player1); 
 controlPlayer2(player2);
 
 collisionDetection(player1, player2)

 requestAnimationFrame(loop)
}
loop();
Damoiselle answered 21/5, 2021 at 15:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.