2D正方形到矩形的碰撞检测和操作(物理)

2022-05-26 00:00:00 collision javascript

所以我做了这种方法来检测和反应移动和接触其他方块的方块。它使用毕达哥拉斯定理来制作第三个参数,所以当正方形接触时,你不会得到两个真的IF语句。我最近一直试图在正方形到矩形上使用这种方法,但似乎无法使其工作。我已经绘制了线条,以帮助可视化代码所做的工作。有没有人对如何让碰撞正常工作有什么建议?

数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
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>


解决方案

@MPdoor 2;我制作了...无论如何,当我给你代码时,我确实特别说过,我是从我创建的一种用于地图的方法中窃取的。为了达到这个目的,该方法工作得无懈可击,尽管代码还有更多内容,是的,它是为正方形而构建的,因为这就是瓷砖地图的主要功能。

我一直在尝试其他制作CD的方法。这里有一个更短的方法,(到目前为止)似乎运行得很好。此方法仍然确定每一侧之间的距离,但方式不同。一旦宽相位确定发生了碰撞,它就称为窄相位,距离最短的那一边就是被穿透的那一边。也就是说,当你从玩家右侧到物体左侧与另一块积木相撞时,我们知道即使是Y轴也会穿透(玩家的上角和下角)。这会计算所有三个之间的距离,因为X之间的距离为0,所以它是最短的,并且不会调用用于在Y方向上移动播放机的CD。

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

数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
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>

相关文章