我正在尝试使用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);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
答案 0 :(得分:0)
我编写了这段代码。我完全更改了碰撞功能,但是效果很好! 另外,限制位置,以免卡在墙上。
几个月前我做了确切的事情,所以你可以看看 here
我还将画布划分为网格,这样比循环遍历每个球要有效得多。
在落后之前,我的球可以打约4000个球
碰撞函数的工作方式就像牛顿的摇篮。这两个对象会改变速度(正在移动的球A击中静止的球B,现在球A不动,球B以相同的速度运动)。
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);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>