2D正方形与矩形的碰撞检测和响应(物理学)

7

所以我创建了一种检测和响应正方形移动并触碰到其他正方形的方法。它使用勾股定理创建第三个参数,以便在正方形接触时不会得到两个真实的语句。最近我一直试图将此方法用于正方形和矩形之间,但似乎无法使其正常工作。我已经绘制了线条来帮助可视化代码执行情况。有人对如何使此碰撞正常工作有任何建议吗?

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>


1
这很容易,比你现在做的方式要简单得多:https://dev59.com/C3VC5IYBdhLWcg3wZwTj - Alberto Sinigaglia
2
我已经点赞了...但是我建议你创建一个最小可重现的示例,包括测试输入和期望输出,如果你想得到更多帮助(因为你的问题中有太多代码,可能对问题本身没有任何作用)。 - Alberto Sinigaglia
1
也许更适合在https://codereview.stackexchange.com/上进行。 - mb21
3
请问具体哪些部分出现了问题?期望得到的输出是什么?实际上发生了什么? - John Paul R
2
@MPdoor2 它们的行为有什么区别?目前我看到的唯一区别是当从右侧中间跳跃时,可控正方形会“卡住”矩形。这只是因为在您的platformAction函数中的val == 4条件中,有一些代码行说obj.vx = 0; obj2.vx = 0; - John Paul R
显示剩余8条评论
3个回答

4

@MPdoor2 "我创建了一个...". 总之,当我给你那段代码时,我确切地说我是从我创建的用于瓷砖地图的方法中盗用的。该方法在那个目的上运行得很完美,尽管这段代码还有更多内容,是针对正方形构建的,因为瓷砖地图主要都是正方形。

我一直在尝试不同的碰撞检测方法。下面是一种更简短的方法(到目前为止)似乎运行良好。此方法仍然确定每个面之间的距离,但方法不同。一旦广义检测发现发生了碰撞,它就调用精细检测,并且具有最短距离的侧面是正在被穿透的侧面。即当您从玩家右侧与另一个块碰撞到对象左侧时,我们知道甚至Y轴也有穿透(玩家的顶部和底部角)。这计算出所有三个角之间的距离,由于X之间的距离为0,因此它是最短的,因此不会调用在Y方向上移动玩家的CD。

尝试下面的代码片段,看看是否适用于您。

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>


我为写下“我做了”而道歉,经过一段时间重构你向我展示的内容后,我得到了与你相同的代码,并下意识地说了“我做了”。你完全功劳归你,再次感谢你的帮助和见解。 - MPdoor2
嘿,我也在YouTube上制作了一个视频:https://www.youtube.com/watch?v=bVqgLa3z9eg&t=23s。随意给自己或你从哪里得到的人留下荣誉:) 再次感谢 - MPdoor2
1
嘿,从我目前看到的来看,那是一个不错的视频。你学得很快。现在你需要用新的东西制作另一个视频。很高兴它对你有用。很难找到一个好的纯JS教程,能够防止穿透而不仅仅是改变块的颜色。 - Justin
1
顺便说一句,我也尝试过“部分边缘”的技巧。对我来说效果不佳。哈哈。如果我最终重构了上面发布的公式,我会回来更新的。 - Justin
我一定会再制作另一个视频!而且,当你创建数组时,那也是我试图弄清楚的事情!!在循环函数中,我可以添加 platforms[i].y += 1; 并使所有平台同时移动,我将使用它来使方块跳跃时平台移动。 - MPdoor2

1
要检测矩形之间的碰撞,请使用它们边缘的位置。例如,如果:对象1的底部低于对象2的底部,并且对象1的顶部在对象2的底部上方,或者对象1的顶部高于对象2的顶部,并且对象1的底部在对象2的顶部下方,则它们“垂直碰撞”。考虑如果它们在“垂直”和“水平”两个方向都碰撞了,则它们会发生碰撞。现在,一旦确定了这一点,您需要检测从哪个边缘发生碰撞。我在此处使用最接近的边缘,评估所有“垂直”或“水平”碰撞的距离。

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>

当您控制的玩家从左侧或右侧进入时仍会有不同的行为:推动速度不同。但这不是由于碰撞引起的,所以我让您自己解决这个问题(也许这也是有意为之的)。


非常感谢您的帮助!我希望我能给更多的积分,这样您也可以得到一些奖励,但是写另一个答案的人在之前的问题上帮了我,所以我必须给他奖励。再次感谢您。 - MPdoor2
1
欢迎。请阅读并使用对您有帮助的内容。 - julien.giband

0

嗨,感谢Justin提供的解决方案。我采用了这个解决方案,并将其添加到之前我提出的一个问题中,即如何检测两个具有运动(使用箭头和WASD)的正方形的碰撞并执行相应操作。这是我的添加方式:

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();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接