如何解决碰撞后改变球的方向
我编写了这段代码来演示一个基本的可视化 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 的必要概念:Elastic Ball Collision Tutorial。
这是该教程中代码的最终版本:
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),360))
});
balls.push({
pos: createVector(width * 0.25,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,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,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>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。