碰撞后改变球的方向



我编写这段代码是为了演示一个基本的可视化p5js项目。在这里有10个不同大小和颜色的球,它们在随机位置产生,在画布上移动,并可能相互碰撞。我不是在寻找弹性碰撞或";现实主义;这种物质的碰撞物理学。我只想让球换到不同的方向(只要有效,就可以是随机的(,并相应地工作。

这是我的代码:

class Ball {
//create new ball using given arguments
constructor(pos, vel, radius, color) {
this.pos = pos;
this.vel = vel;
this.radius = radius;
this.color = color;
}
//collision detection
collide(check) {
if (check == this) {
return;
}
let relative = p5.Vector.sub(check.pos, this.pos);
let dist = relative.mag() - (this.radius + check.radius);
if (dist < 0) { //HELP HERE! <--
this.vel.mult(-1);
check.vel.mult(-1);
}
}
//give life to the ball
move() {
this.pos.add(this.vel);
if (this.pos.x < this.radius) {
this.pos.x = this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.x > width - this.radius) {
this.pos.x = width - this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.y < this.radius) {
this.pos.y = this.radius;
this.vel.y = -this.vel.y;
}
if (this.pos.y > height - this.radius) {
this.pos.y = height - this.radius;
this.vel.y = -this.vel.y;
}
}
//show the ball on the canvas
render() {
fill(this.color);
noStroke();
ellipse(this.pos.x, this.pos.y, this.radius * 2);
}
}
let balls = []; //stores all the balls
function setup() {
createCanvas(window.windowWidth, window.windowHeight);
let n = 10;
//loop to create n balls
for (i = 0; i < n; i++) {
balls.push(
new Ball(
createVector(random(width), random(height)),
p5.Vector.random2D().mult(random(5)),
random(20, 50),
color(random(255), random(255), random(255))
)
);
}
}
function draw() {
background(0);
//loop to detect collision at all instances
for (let i = 0; i < balls.length; i++) {
for (let j = 0; j < i; j++) {
balls[i].collide(balls[j]);
}
}
//loop to render and move all balls
for (let i = 0; i < balls.length; i++) {
balls[i].move();
balls[i].render();
}
}

以下是该项目的链接:https://editor.p5js.org/AdilBub/sketches/TNn2OREsN

我所需要的只是碰撞,将球的方向改变为随机方向,而不是卡住。如有任何帮助,我们将不胜感激。我教孩子们这个程序,所以我只想基本的碰撞,不必是";现实主义";。

感谢您的帮助。非常感谢。

您目前遇到的球被卡住的问题与随机生成重叠的球有关,这样在一次移动迭代后,它们仍然重叠。当这种情况发生时,两个球将在适当的位置反复振荡,相互碰撞。你可以通过在添加新球之前检查碰撞来防止这种情况:

class Ball {
//create new ball using given arguments
constructor(pos, vel, radius, color) {
this.pos = pos;
this.vel = vel;
this.radius = radius;
this.color = color;
}

isColliding(check) {
if (check == this) {
return;
}
let relative = p5.Vector.sub(check.pos, this.pos);
let dist = relative.mag() - (this.radius + check.radius);
return dist < 0;
}

//collision detection
collide(check) {
if (this.isColliding(check)) {
this.vel.x *= -1;
this.vel.y *= -1;
check.vel.x *= -1;
check.vel.y *= -1;
}
}
//give life to the ball
move() {
this.pos.add(this.vel);
if (this.pos.x < this.radius) {
this.pos.x = this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.x > width - this.radius) {
this.pos.x = width - this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.y < this.radius) {
this.pos.y = this.radius;
this.vel.y = -this.vel.y;
}
if (this.pos.y > height - this.radius) {
this.pos.y = height - this.radius;
this.vel.y = -this.vel.y;
}
}
//show the ball on the canvas
render() {
fill(this.color);
noStroke();
ellipse(this.pos.x, this.pos.y, this.radius * 2);
}
}
let balls = []; //stores all the balls
function setup() {
createCanvas(500, 500);
let n = 10;
//loop to create n balls
for (i = 0; i < n; i++) {
let newBall =
new Ball(
createVector(random(width), random(height)),
p5.Vector.random2D().mult(random(5)),
random(20, 40),
color(random(255), random(255), random(255))
);
let isOk = true;
// check for collisions with existing balls
for (let j = 0; j < balls.length; j++) {
if (newBall.isColliding(balls[j])) {
isOk = false;
break;
}
}
if (isOk) {
balls.push(newBall);
} else {
// try again
i--;
}
}
}
function draw() {
background(0);
//loop to detect collision at all instances
for (let i = 0; i < balls.length; i++) {
for (let j = 0; j < i; j++) {
balls[i].collide(balls[j]);
}
}
//loop to render and move all balls
for (let i = 0; i < balls.length; i++) {
balls[i].move();
balls[i].render();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>


也就是说,全弹性碰撞(这意味着碰撞是瞬时的,不涉及由于变形和由此产生的热发射而造成的能量损失(实际上很容易模拟。以下是我在OpenProcessing上制作的教程,演示了使用p5.js的必要概念:弹性球碰撞教程。

以下是该教程中代码的最终版本:

const radius = 30;
const speed = 100;
let time;
let balls = []
let boundary = [];
let obstacles = [];
let paused = false;
function setup() {
createCanvas(400, 400);
angleMode(DEGREES);
ellipseMode(RADIUS);
boundary.push(createVector(60, 4));
boundary.push(createVector(width - 4, 60));
boundary.push(createVector(width - 60, height - 4));
boundary.push(createVector(4, height - 60));
obstacles.push(createVector(width / 2, height / 2));
balls.push({
pos: createVector(width * 0.25, height * 0.25),
vel: createVector(speed, 0).rotate(random(0, 360))
});
balls.push({
pos: createVector(width * 0.75, height * 0.75),
vel: createVector(speed, 0).rotate(random(0, 360))
});
balls.push({
pos: createVector(width * 0.25, height * 0.75),
vel: createVector(speed, 0).rotate(random(0, 360))
});
time = millis();
}
function keyPressed() {
if (key === "p") {
paused = !paused;
time = millis();
}
}
function draw() {
if (paused) {
return;
}

deltaT = millis() - time;
time = millis();
background('dimgray');
push();
fill('lightgray');
stroke('black');
strokeWeight(2);
beginShape();
for (let v of boundary) {
vertex(v.x, v.y);
}
endShape(CLOSE);
pop();
push();
fill('dimgray');
for (let obstacle of obstacles) {
circle(obstacle.x, obstacle.y, radius);
}
pop();
for (let i = 0; i < balls.length; i++) {
let ball = balls[i];
// update position
ball.pos = createVector(
min(max(0, ball.pos.x + ball.vel.x * (deltaT / 1000)), width),
min(max(0, ball.pos.y + ball.vel.y * (deltaT / 1000)), height)
);
// check for collisions
for (let i = 0; i < boundary.length; i++) {
checkCollision(ball, boundary[i], boundary[(i + 1) % boundary.length]);
}
for (let obstacle of obstacles) {
// Find the tangent plane that is perpendicular to a line from the obstacle to
// the moving circle
// A vector pointing in the direction of the moving object
let dirVector = p5.Vector.sub(ball.pos, obstacle).normalize().mult(radius);
// The point on the perimiter of the obstacle that is in the direction of the
// moving object
let p1 = p5.Vector.add(obstacle, dirVector);
checkCollision(ball, p1, p5.Vector.add(p1, p5.Vector.rotate(dirVector, -90)));
}

// Check for collisions with other balls
for (let j = 0; j < i; j++) {
let other = balls[j];

let distance = dist(ball.pos.x, ball.pos.y, other.pos.x, other.pos.y);
if (distance / 2 < radius) {
push();
let midPoint = p5.Vector.add(ball.pos, other.pos).div(2);
let boundaryVector = p5.Vector.sub(other.pos, ball.pos).rotate(-90);

let v1Parallel = project(ball.vel, boundaryVector);
let v2Parallel = project(other.vel, boundaryVector);

let v1Perpendicular = p5.Vector.sub(ball.vel, v1Parallel);
let v2Perpendicular = p5.Vector.sub(other.vel, v2Parallel);

ball.vel = p5.Vector.add(v1Parallel, v2Perpendicular);
other.vel = p5.Vector.add(v2Parallel, v1Perpendicular);

let bounce = min(radius, 2 * radius - distance);
ball.pos.add(p5.Vector.rotate(boundaryVector, -90).normalize().mult(bounce));
other.pos.add(p5.Vector.rotate(boundaryVector, 90).normalize().mult(bounce));

pop();
}
}
}
// Only draw balls after all position updates are complete
for (let ball of balls) {
circle(ball.pos.x, ball.pos.y, radius);
}
}
function drawLine(origin, offset) {
line(origin.x, origin.y, origin.x + offset.x, origin.y + offset.y);
}
// Handles collision with a plane given two points on the plane.
// It is assumed that given a vector from p1 to p2, roating that vector
// clockwise 90 degrees will give a vector pointing to the in-bounds side of the
// plane (i.e. a "normal").
function checkCollision(ball, p1, p2) {
let boundaryVector = p5.Vector.sub(p2, p1);
let objVector = p5.Vector.sub(ball.pos, p1);
let angle = boundaryVector.angleBetween(objVector);
let distance = objVector.mag() * sin(angle);
if (distance <= radius) {
// Collision
let vParallel = project(ball.vel, boundaryVector);
let vPerpendicular = p5.Vector.sub(ball.vel, vParallel);
ball.vel = p5.Vector.add(vParallel, p5.Vector.mult(vPerpendicular, -1));
let bounce = min(radius, (radius - distance) * 2);
// If the ball has crossed over beyond the plane we want to offset it to be on
// the in-bounds side of the plane.
let bounceOffset = p5.Vector.rotate(boundaryVector, 90).normalize().mult(bounce);
ball.pos.add(bounceOffset);
}
}
// p5.Vector helpers
function project(vect1, vect2) {
vect2 = p5.Vector.normalize(vect2);
return p5.Vector.mult(vect2, p5.Vector.dot(vect1, vect2));
}
function reject(vect1, vect2) {
return p5.Vector.sub(vect1, project(vect1, vect2));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>