p5.j s中的角碰撞角

问题描述 投票:2回答:2

摘要:

为了在移动的矩形和下降的圆形之间建立一个简单的碰撞检测系统,我想使它更加真实。

主要问题:

- 我想要解决的主要问题是检测圆形物体何时撞到矩形的角落,然后根据该角度进行圆形反弹。

代码:

var balls = [];
var obstacle;

function setup() {
  createCanvas(400, 400);
  obstacle = new Obstacle();
}

function draw() {
  background(75);
  obstacle.display();
  obstacle.update();
  
  for (var i = 0; i < balls.length; i++) {
    balls[i].display();
	  if (!RectCircleColliding(balls[i], obstacle)){
        balls[i].update();
        balls[i].edges();
	  }
    
    //console.log(RectCircleColliding(balls[i], obstacle));
  }
}

function mousePressed() {
  balls.push(new Ball(mouseX, mouseY));
}

function Ball(x, y) {
  this.x = x;
  this.y = y;
  this.r = 15;
  this.gravity = 0.5;
  this.velocity = 0;
  this.display = function() {
    fill(255, 0, 100);
    stroke(255);
    ellipse(this.x, this.y, this.r * 2);
  }
  this.update = function() {
    this.velocity += this.gravity;
    this.y += this.velocity;
  }
  this.edges = function() {
    if (this.y >= height - this.r) {
      this.y = height - this.r;
      this.velocity = this.velocity * -1;
      this.gravity = this.gravity * 1.1;
    }
  }
}

function Obstacle() {
  this.x = width - width;
  this.y = height / 2;
  this.w = 200;
  this.h = 25;

  this.display = function() {
    fill(0);
    stroke(255);
    rect(this.x, this.y, this.w, this.h);
  }
  
  this.update = function() {
    this.x++;
    
    if (this.x > width + this.w /2) {
       this.x = -this.w;
    }
  }
}

function RectCircleColliding(Ball, Obstacle) {
  // define obstacle borders
  var oRight = Obstacle.x + Obstacle.w;
  var oLeft = Obstacle.x;
  var oTop = Obstacle.y;
  var oBottom = Obstacle.y + Obstacle.h;

  //compare ball's position (acounting for radius) with the obstacle's border
  if (Ball.x + Ball.r > oLeft) {
    if (Ball.x - Ball.r < oRight) {
      if (Ball.y + Ball.r > oTop) {
        if (Ball.y - Ball.r < oBottom) {
          
         let oldY = Ball.y;
         Ball.y = oTop - Ball.r;
         Ball.velocity = Ball.velocity * -1;
           if (Ball.gravity < 2.0){
              Ball.gravity = Ball.gravity * 1.1;  
           } else {
             Ball.velocity = 0;
             Ball.y = oldY;
         }   
         return (true);
        } 
      }
    }
  }
    return false;
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>

预期产量:

我希望看到落下的圆圈相对于它们撞击矩形的位置从矩形反弹。

如果圆圈击中角落,他们应该以不同的方式反弹而不是击中死角。

javascript processing collision-detection physics p5.js
2个回答
1
投票

我将尝试提供一个解决方案,尽可能保留原始代码。该解决方案旨在成为问题中呈现的代码的演变。

selv.sideV对象添加向侧移动(Ball),该对象初始化为0:

function Ball(x, y) {
    this.x = x;
    this.y = y;
    this.r = 15;
    this.gravity = 0.5;
    this.velocity = 0;
    this.sideV = 0 

    // ...
}

通过侧向移动将球移到update的侧面并减少侧向移动:

this.update = function() {
    this.velocity += this.gravity;
    this.y += this.velocity;
    this.x += this.sideV;
    this.sideV *= 0.98;
}

为2个盒子的交叉点测试创建函数:

function IsectRectRect(l1, r1, t1, b1, l2, r2, t2, b2) {
    return l1 < r2 && l2 < r1 && t1 < b2 && t2 < b1;
}

并且可以计算入射矢量qazxsw poi的反射矢量qazxsw poi到表面R的正常矢量(像台球一样的反射)的函数:

V

Nfunction reflect( V, N ) { R = V.copy().sub(N.copy().mult(2.0 * V.dot(N))); return R; } 碰撞时,你将处理3种情况。

  1. 球完全击中了Ball的顶部:Obstacle
  2. 球击中了Obstacle的左边缘:IsectRectRect(oL, oR, oT, oB, Ball.x, Ball.x, bT, bB)
  3. 球击中了Obstacle的右边缘:IsectRectRect(oL, oL, oT, oB, bL, bR, bT, bB)

在每种情况下,必须计算反射的法向量。这是从Obstacle的顶部或边缘到IsectRectRect(oR, oR, oT, oB, bL, bR, bT, bB)中心的矢量。 使用函数Obstacle弹跳Ball上的reflect

Ball

请参阅示例,我将更改应用于原始代码:

Obstacle

function RectCircleColliding(Ball, Obstacle) {
    let oL = Obstacle.x;
    let oR = Obstacle.x + Obstacle.w;
    let oT = Obstacle.y;
    let oB = Obstacle.y + Obstacle.h;
    let bL = Ball.x - Ball.r;
    let bR = Ball.x + Ball.r;
    let bT = Ball.y - Ball.r;
    let bB = Ball.y + Ball.r;

    let isect = false;
    let hitDir = createVector(0, 1);
    if ( IsectRectRect(oL, oR, oT, oB, Ball.x, Ball.x, bT, bB) ) {
        isect = true;
    } else if ( IsectRectRect(oL, oL, oT, oB, bL, bR, bT, bB) ) {
        hitDir = createVector(Ball.x, Ball.y).sub(createVector(oL, oT))
        isect = hitDir.mag() < Ball.r;
    } else if ( IsectRectRect(oR, oR, oT, oB, bL, bR, bT, bB) ) {
        hitDir = createVector(Ball.x, Ball.y).sub(createVector(oR, oT))
        isect = hitDir.mag() < Ball.r;
    }

    if ( isect ) {
        let dir = createVector(Ball.sideV, Ball.velocity);
        R = reflect(dir, hitDir.normalize());
        Ball.velocity = R.y;
        Ball.sideV = R.x;
        let oldY = Ball.y;
        Ball.y = oT - Ball.r;
        if (Ball.gravity < 2.0){
            Ball.gravity = Ball.gravity * 1.1;  
        } else {
            Ball.velocity = 0;
            Ball.y = oldY;
        }   
        return true;
    }
    return false;
}

3
投票

条件

球的速度必须是矢量(XY分量),而不仅仅是一个数字。


1.确定圆圈是否可能碰到一侧或一角

从矩形的中心到圆圈获取矢量的分量,并根据矩形的尺寸进行检查:

var balls = [];
var obstacle;

function setup() {
  createCanvas(400, 400);
  obstacle = new Obstacle();
}

function draw() {
  background(75);
  obstacle.display();
  obstacle.update();

  for (var i = 0; i < balls.length; i++) {
    balls[i].display();
      if (!RectCircleColliding(balls[i], obstacle)){
        balls[i].update();
        balls[i].edges();
      }

    //console.log(RectCircleColliding(balls[i], obstacle));
  }
}

function mousePressed() {
  balls.push(new Ball(mouseX, mouseY));
}

function Ball(x, y) {
  this.x = x;
  this.y = y;
  this.r = 15;
  this.gravity = 0.5;
  this.velocity = 0;
  this.sideV = 0 
  this.display = function() {
    fill(255, 0, 100);
    stroke(255);
    ellipse(this.x, this.y, this.r * 2);
  }
  this.update = function() {
      this.velocity += this.gravity;
      this.y += this.velocity;
      this.x += this.sideV;
      this.sideV *= 0.98;
  }
  this.edges = function() {
    if (this.y >= height - this.r) {
      this.y = height - this.r;
      this.velocity = this.velocity * -1;
      this.gravity = this.gravity * 1.1;
    }
  }
}

function Obstacle() {
  this.x = width - width;
  this.y = height / 2;
  this.w = 200;
  this.h = 25;

  this.display = function() {
    fill(0);
    stroke(255);
    rect(this.x, this.y, this.w, this.h);
  }

  this.update = function() {
    this.x++;

    if (this.x > width + this.w /2) {
       this.x = -this.w;
    }
  }
}

function IsectRectRect(l1, r1, t1, b1, l2, r2, t2, b2) {
    return l1 < r2 && l2 < r1 && t1 < b2 && t2 < b1;
}

function reflect( V, N ) {
    R = V.copy().sub(N.copy().mult(2.0 * V.dot(N)));
    return R;
}

function RectCircleColliding(Ball, Obstacle) {
    let oL = Obstacle.x;
    let oR = Obstacle.x + Obstacle.w;
    let oT = Obstacle.y;
    let oB = Obstacle.y + Obstacle.h;
    let bL = Ball.x - Ball.r;
    let bR = Ball.x + Ball.r;
    let bT = Ball.y - Ball.r;
    let bB = Ball.y + Ball.r;

    let isect = false;
    let hitDir = createVector(0, 1);
    if ( IsectRectRect(oL, oR, oT, oB, Ball.x, Ball.x, bT, bB) ) {
        isect = true;
    } else if ( IsectRectRect(oL, oL, oT, oB, bL, bR, bT, bB) ) {
        hitDir = createVector(Ball.x, Ball.y).sub(createVector(oL, oT))
        isect = hitDir.mag() < Ball.r;
    } else if ( IsectRectRect(oR, oR, oT, oB, bL, bR, bT, bB) ) {
        hitDir = createVector(Ball.x, Ball.y).sub(createVector(oR, oT))
        isect = hitDir.mag() < Ball.r;
    }

    if ( isect ) {
        let dir = createVector(Ball.sideV, Ball.velocity);
        R = reflect(dir, hitDir.normalize());
        Ball.velocity = R.y;
        Ball.sideV = R.x;
        let oldY = Ball.y;
        Ball.y = oT - Ball.r;
        if (Ball.gravity < 2.0){
            Ball.gravity = Ball.gravity * 1.1;  
        } else {
            Ball.velocity = 0;
            Ball.y = oldY;
        }   
        return true;
    }
    return false;
}

如果两个<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>都不为零,则球可能会撞到一个角落,否则它可能会撞到一侧。


2.确定圆是否发生碰撞

将每个符号乘以相应的半维:

// Useful temporary variables for later use
var hx = 0.5 * obstacle.w;
var hy = 0.5 * obstacle.h;
var rx = obstacle.x + hx;
var ry = obstacle.y + hy;

// displacement vector
var dx = ball.x - rx;
var dy = ball.y - ry;

// signs
var sx = dx < -hx ? -1 : (dx > hx ? 1 : 0);
var sy = dy < -hy ? -1 : (dy > hy ? 1 : 0);

3.确定碰撞法向量

sx, sy是法线向量的组成部分,但仅当球的中心位于矩形之外时:

// displacement vector from the nearest point on the rectangle
var tx = sx * (Math.abs(dx) - hx);
var ty = sy * (Math.abs(dy) - hy);

// distance from p to the center of the circle
var dc = Math.hypot(tx, ty);

if (dc <= ball.r) {
    /* they do collide */
}

4.解决任何“渗透”

(请不要成熟的笑话)

这可确保球永远不会穿透矩形表面,从而提高碰撞的视觉质量:

(tx, ty)

5.解决碰撞

如果圆沿正常方向行进,请不要解决碰撞,因为球可能会粘在表面上:

// epsilon to account for numerical imprecision
const EPSILON = 1e-6;

var nx = 0, ny = 0, nl = 0;
if (sx == 0 && sy == 0) {  // center is inside
  nx = dx > 0 ? 1 : -1;
  ny = dy > 0 ? 1 : -1;
  nl = Math.hypot(nx, ny);
} else {                   // outside
  nx = tx;
  ny = ty;
  nl = dc;
}
nx /= nl;
ny /= nl;

工作JS片段

ball.x += (ball.r - dc) * nx; 
ball.y += (ball.r - dc) * ny;
// dot-product of velocity with normal
var dv = ball.vx * nx + ball.vy * ny;

if (dv >= 0.0) {
    /* exit and don't do anything else */
}

// reflect the ball's velocity in direction of the normal
ball.vx -= 2.0 * dv * nx;
ball.vy -= 2.0 * dv * ny;
© www.soinside.com 2019 - 2024. All rights reserved.