【问题标题】:Circle - circle collision response (balls stuck overlapping)圆 - 圆碰撞响应(球卡重叠)
【发布时间】:2018-07-14 04:12:39
【问题描述】:

我正在尝试使用 p5.js 在 JavaScript 中构建类似的东西:

我能够检测到两个圆之间的碰撞,我正在尝试使用来自维基百科的这些(二维弹性碰撞)公式计算新速度: 对于 theta 和 phi 的这些角度:

问题似乎是当我计算新速度时,球已经碰撞/重叠并且它们被卡住了。我认为我应该做的是根据圆圈重叠的距离计算一个新的圆圈位置。不幸的是,我不知道该怎么做。我也认为我计算的方式过于复杂和低效。 这是我的碰撞处理代码的样子:

  // takes ball object as input, sets speedvector to new x,y speed
  collision(other) {
    var newSpeed = createVector();
    var phi = Math.atan((this.pos.y - other.pos.y) / this.pos.x - other.pos.x);
    var theta1 = this.speed.heading();
    var theta2 = other.speed.heading();

    newSpeed.x = (this.speed.mag() * Math.cos(theta1 - phi) * (this.mass - other.mass) + 2 * other.mass * other.speed.mag() * Math.cos(theta2 - phi)) / (this.mass + other.mass) * Math.cos(phi) - this.speed.mag() * Math.sin(theta1 - phi) * Math.sin(phi);
    newSpeed.y = (this.speed.mag() * Math.cos(theta1 - phi) * (this.mass - other.mass) + 2 * other.mass * other.speed.mag() * Math.cos(theta2 - phi)) / (this.mass + other.mass) * Math.sin(phi) + this.speed.mag() * Math.sin(theta1 - phi) * Math.cos(phi);

    this.speed.x = newSpeed.x;
    this.speed.y = newSpeed.y;
  }

完整的代码示例:

var balls = [];
var numOfBalls = 5;
var maxSpeed = 2;


function setup() {
  createCanvas(500, 500);
  
  for (var i = 0; i < numOfBalls; i++) {
    var ball = new Ball(30);
    balls.push(ball);
  }
}

function draw() {
  background(0);
  for (var i = 0; i < balls.length; i++) {
    balls[i].move();
  }
  for (var i = 0; i < balls.length; i++) {
    balls[i].show();
  }
}

class Ball {
  constructor(radius) {
    this.radius = radius;
    this.pos = this.pickLocation();
    this.speed = createVector(random(-maxSpeed, maxSpeed), random(-maxSpeed, maxSpeed));
    this.mass = 1;
  }
  
  pickLocation() {
    //spawn within canvas
    var xOption = random(this.radius, width - this.radius);
    var yOption = random(this.radius, height - this.radius);
    
    // check whether spawning on this location doesn't overlap other circles
    for(var i = 0; i < balls.length; i++) {
      // don't check for current circle
      if(balls[i] != this) {
        // get distance to other circle
        var d = dist(xOption, yOption, balls[i].pos.x, balls[i].pos.y);
        // check whether overlapping
        if (d <= this.radius + balls[i].radius) {
          // generate new location and rerun check
          console.log("overlapping another circle, trying new location");
          var xOption = random(this.radius, width - this.radius);
          var yOption = random(this.radius, height - this.radius);
          i = -1;
        }
      }
    }
    return(createVector(xOption, yOption));
  }
  
  move() {
    for (var i = 0; i < balls.length; i++) {
      if(balls[i] != this) {
        var d = dist(this.pos.x, this.pos.y, balls[i].pos.x, balls[i].pos.y);
        if(d < this.radius + balls[i].radius) {
          this.collision(balls[i]);
        }
      }
    }
    
    if(this.pos.x - this.radius < 0 || this.pos.x + this.radius > width) {
      this.speed.x *= -1;
    }
    if(this.pos.y - this.radius < 0 || this.pos.y + this.radius > height) {
      this.speed.y *= -1;
    }
    
    this.pos.x += this.speed.x;
    this.pos.y += this.speed.y;
  }
  
  // takes ball object as input, sets speedvector to new x,y speed
  collision(other) {
    var newSpeed = createVector();
    var phi = Math.atan((this.pos.y - other.pos.y) / this.pos.x - other.pos.x);
    var theta1 = this.speed.heading();
    var theta2 = other.speed.heading();
    
    newSpeed.x = (this.speed.mag() * Math.cos(theta1 - phi) * (this.mass - other.mass) + 2 * other.mass * other.speed.mag() * Math.cos(theta2 - phi)) / (this.mass + other.mass) * Math.cos(phi) - this.speed.mag() * Math.sin(theta1 - phi) * Math.sin(phi);
    newSpeed.y = (this.speed.mag() * Math.cos(theta1 - phi) * (this.mass - other.mass) + 2 * other.mass * other.speed.mag() * Math.cos(theta2 - phi)) / (this.mass + other.mass) * Math.sin(phi) + this.speed.mag() * Math.sin(theta1 - phi) * Math.cos(phi);
    
    this.speed.x = newSpeed.x;
    this.speed.y = newSpeed.y;
  }
  
  show() {
    fill(200, 100);
    noStroke();
    ellipse(this.pos.x, this.pos.y, this.radius * 2);
  }
}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"&gt;&lt;/script&gt;

【问题讨论】:

  • 请提供一个minimal reproducible example 来说明问题。另外:当两个以上的圆相撞时,您如何处理?
  • 也许你可以给每个球一个 id 并存储最后一个球的 id,忽略与那个球的碰撞(在墙壁反弹时清除它?)
  • @KevinWorkman 我添加了示例。这应该在与 p5.js 结合的 HTML 文件中导入(我正在使用此链接:cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.9/p5.min.js)。我还没有想过如何处理两个以上的圆圈碰撞。如果我能从两个圈子开始,我会很高兴。
  • 你试过调试你的程序吗?哪一行代码的行为与您的预期不同?
  • @KevinWorkman 我试过了。例如,我确定我的对象速度矢量在碰撞后正在更新。但是,如果您能推荐一个非常有帮助的教程,我从未接受过任何有关编程的教育。我希望我的对象在碰撞后会改变方向,但我认为由于它们重叠(少量重叠),它们会不断碰撞,从而粘在一起并不断改变方向。我认为碰撞发生在帧之间,我应该计算发生碰撞的点并相应地更新对象位置。

标签: javascript collision geometry p5.js


【解决方案1】:

我编写了这段代码。我完全改变了碰撞功能,但效果很好! 此外,限制位置,使其不会卡在墙上。

几个月前我做了一个确切的东西,所以你可以看看 here

我还将画布划分为网格,这样比循环遍历每个球要高效得多。

我的可以在滞后前完成大约 4000 个球

碰撞函数的工作方式就像牛顿的摇篮。两个物体改变速度(正在移动的球 A 撞击静止的球 B,现在球 A 不动,球 B 以与球 A 相同的速度移动)

var balls = [];
var numOfBalls = 5;
var maxSpeed = 2;


function setup() {
  createCanvas(500, 500);
  
  for (var i = 0; i < numOfBalls; i++) {
    var ball = new Ball(30);
    balls.push(ball);
  }
}

function draw() {
  background(0);
  for (var i = 0; i < balls.length; i++) {
    balls[i].move();
  }
  for (var i = 0; i < balls.length; i++) {
    balls[i].show();
  }
}

class Ball {
  constructor(radius) {
    this.radius = radius;
    this.pos = this.pickLocation();
    this.speed = createVector(random(-maxSpeed, maxSpeed), random(-maxSpeed, maxSpeed));
    this.mass = 1;
  }
  
  pickLocation() {
    //spawn within canvas
    var xOption = random(this.radius, width - this.radius);
    var yOption = random(this.radius, height - this.radius);
    
    // check whether spawning on this location doesn't overlap other circles
    for(var i = 0; i < balls.length; i++) {
      // don't check for current circle
      if(balls[i] != this) {
        // get distance to other circle
        var d = dist(xOption, yOption, balls[i].pos.x, balls[i].pos.y);
        // check whether overlapping
        if (d <= this.radius + balls[i].radius) {
          // generate new location and rerun check
          console.log("overlapping another circle, trying new location");
          xOption = random(this.radius, width - this.radius);
          yOption = random(this.radius, height - this.radius);
          i = -1;
        }
      }
    }
    return(createVector(xOption, yOption));
  }
  
  move() {
    for (var i = 0; i < balls.length; i++) {
      if(balls[i] != this) {
        var d = dist(this.pos.x, this.pos.y, balls[i].pos.x, balls[i].pos.y);
        if(d < this.radius + balls[i].radius) {
          this.collision(balls[i]);
        }
      }
    }
    
    if(this.pos.x - this.radius < 0 || this.pos.x + this.radius > width) {
      this.speed.x *= -1;

    }
    if(this.pos.y - this.radius < 0 || this.pos.y + this.radius > height) {
      this.speed.y *= -1;

    }
    
    this.pos.x += this.speed.x;
    this.pos.y += this.speed.y;
    this.pos.x = constrain(this.pos.x,0,500)
    this.pos.y = constrain(this.pos.y,0,500)


  }

  // takes ball object as input, sets speedvector to new x,y speed
  collision(other) {
    this.v1 = 0;
    this.v2 = 0;
    this.direction = p5.Vector.sub(other.pos, this.pos)
    this.dist = this.direction.mag()
    this.direction.normalize();
    //this is 60 because that is the radius you give them times two
    this.correction = 60-this.dist;
    this.pos.sub(p5.Vector.mult(this.direction,this.correction/2))
    other.pos.add(p5.Vector.mult(this.direction,this.correction/2))
    this.v1 = this.direction.dot(this.speed)
    this.v2 = this.direction.dot(other.speed)
    this.direction.mult(this.v1-this.v2)
    this.speed.sub(p5.Vector.mult(this.direction,1));
    other.speed.add(p5.Vector.mult(this.direction,1))

    
  }
  
  show() {
    fill(200, 100);
    noStroke();
    ellipse(this.pos.x, this.pos.y, this.radius * 2);
  }
}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"&gt;&lt;/script&gt;

【讨论】:

  • 我才意识到这是两年前的事了,但我希望其他人可以使用它或其他东西
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-12
  • 1970-01-01
  • 2016-08-08
  • 1970-01-01
  • 1970-01-01
  • 2013-09-13
  • 1970-01-01
相关资源
最近更新 更多