Tenho este código JavaScript que simula bolinhas caindo:
const canvas = document.getElementById('simulationCanvas');
const ctx = canvas.getContext('2d');
const marbles = [];
const obstacles = [];
const gravity = 0.1; // Adjust this value to change the speed of the simulation
const friction = 0.99;
const restitution = 0.8;
class Marble {
constructor(x, y, radius, color) {
Object.assign(this, { x, y, radius, color });
this.dx = (Math.random() - 0.5) * 2;
this.dy = 0;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
update() {
// Apply gravity to the vertical velocity
this.dy += gravity;
// Apply friction to the horizontal velocity
this.dx *= friction;
// Update the marble's position based on its velocity
this.x += this.dx;
this.y += this.dy;
// Check for collisions with the bottom of the canvas
if (this.y + this.radius > canvas.height) {
// Keep the marble within the canvas boundaries
this.y = canvas.height - this.radius;
// Reverse the vertical velocity and apply restitution for bouncing effect
this.dy = -this.dy * restitution;
}
// Check for collisions with the sides of the canvas
if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
// Reverse the horizontal velocity to make the marble bounce off the walls
this.dx = -this.dx;
}
// Check for collisions with obstacles
obstacles.forEach(obstacle => {
const { normalX, normalY, isColliding, penetrationDepth } = obstacle.checkCollision(this.x, this.y, this.radius);
if (isColliding) {
// Calculate the dot product of the velocity and the collision normal
const dotProduct = this.dx * normalX + this.dy * normalY;
// Reflect the velocity vector off the surface normal
this.dx -= 2 * dotProduct * normalX;
this.dy -= 2 * dotProduct * normalY;
// Apply restitution to the reflected velocity
this.dx *= restitution;
this.dy *= restitution;
// Resolve the collision by moving the marble outside of the obstacle
this.x += normalX * penetrationDepth;
this.y += normalY * penetrationDepth;
}
});
this.draw();
}
}
class Obstacle {
constructor(x, y, width, height, angle, color) {
Object.assign(this, { x, y, width, height, angle, color });
}
draw() {
ctx.save();
ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
ctx.rotate(this.angle * Math.PI / 180);
ctx.fillStyle = this.color;
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.restore();
}
checkCollision(mx, my, mr) {
const cos = Math.cos(-this.angle * Math.PI / 180);
const sin = Math.sin(-this.angle * Math.PI / 180);
const dx = mx - (this.x + this.width / 2);
const dy = my - (this.y + this.height / 2);
const localX = cos * dx - sin * dy + this.width / 2;
const localY = sin * dx + cos * dy + this.height / 2;
const closestX = Math.max(0, Math.min(this.width, localX));
const closestY = Math.max(0, Math.min(this.height, localY));
const distanceX = localX - closestX;
const distanceY = localY - closestY;
const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
const isColliding = distance < mr;
const normalX = cos * distanceX / distance + sin * distanceY / distance;
const normalY = -sin * distanceX / distance + cos * distanceY / distance;
const penetrationDepth = mr - distance;
return { normalX, normalY, isColliding, penetrationDepth };
}
}
for (let i = 0; i < 10; i++) {
const radius = 10;
const x = Math.random() * (canvas.width - radius * 2) + radius;
const y = radius;
const color = 'blue';
marbles.push(new Marble(x, y, radius, color));
}
obstacles.push(new Obstacle(100, 500, 150, 20, -20, 'black'));
obstacles.push(new Obstacle(600, 400, 150, 20, -20, 'black'));
obstacles.push(new Obstacle(200, 200, 100, 20, 45, 'black'));
obstacles.push(new Obstacle(400, 150, 120, 20, -45, 'black'));
obstacles.push(new Obstacle(500, 500, 200, 20, 15, 'black'));
obstacles.push(new Obstacle(800, 300, 150, 20, -15, 'black'));
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
marbles.forEach(marble => marble.update());
obstacles.forEach(obstacle => obstacle.draw());
requestAnimationFrame(animate);
}
animate();
Resposta
Esse código é um bom ponto de partida para uma simulação de bolinhas. No entanto, há algumas áreas que podem ser melhoradas em termos de eficiência, precisão e robustez:
1. Detecção de colisão ineficiente:a detecção de colisão atual com obstáculos verifica todos os obstáculos em cada bolinha em cada frame. Para um grande número de bolinhas e obstáculos, isso se torna caro computacionalmente. Uma estrutura de dados de particionamento espacial, como uma quadtree ou uma grade, melhoraria significativamente a performance.
2. Possível estouro de pilha (recursão): embora não esteja presente diretamente neste código, a resolução de colisão recursiva (em que uma bolinha colide com vários obstáculos simultaneamente) pode levar a um estouro de pilha se não for tratada com cuidado. A abordagem iterativa atual é mais segura.
3. Resposta de colisão imprecisa:a resposta de colisão usa uma reflexão simples com base no vetor normal. Isso funciona razoavelmente bem para formas simples, mas pode levar a imprecisões e oscilações, especialmente quando as bolinhas colidem entre si ou em ângulos acentuados. Uma abordagem mais robusta envolveria a resolução de colisões baseada em impulso, que considera as massas e velocidades dos objetos em colisão.
4. Falta de colisões entre bolas de gude:no momento, a simulação só processa colisões entre bolas de gude e obstáculos. Adicionar a detecção e resolução de colisão entre as bolas de gude tornaria a simulação mais realista.
5. Elemento canvas ausente:o código pressupõe que um elemento de tela com o ID simulationCanvas existe no HTML. Você precisa incluir isso no arquivo HTML.
Confira uma versão aprimorada que resolve alguns desses problemas:
const canvas = document.getElementById('simulationCanvas');
const ctx = canvas.getContext('2d');
const gravity = 0.1;
const friction = 0.99;
const restitution = 0.8;
class Marble {
constructor(x, y, radius, color) {
Object.assign(this, { x, y, radius, color });
this.dx = (Math.random() - 0.5) * 2;
this.dy = 0;
this.mass = 1; // Add mass for more realistic collisions
}
// ... (draw method remains the same) ...
update() {
this.dy += gravity;
this.dx *= friction;
this.x += this.dx;
this.y += this.dy;
this.handleCollisions();
this.draw();
}
handleCollisions() {
// Bottom collision
if (this.y + this.radius > canvas.height) {
this.y = canvas.height - this.radius;
this.dy = -this.dy * restitution;
}
// Side collisions
if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
this.dx = -this.dx * restitution;
}
// Obstacle collisions (iterative approach)
obstacles.forEach(obstacle => {
const collisionData = obstacle.checkCollision(this.x, this.y, this.radius);
if (collisionData.isColliding) {
this.resolveCollision(collisionData);
}
});
}
resolveCollision(collisionData) {
const { normalX, normalY, penetrationDepth } = collisionData;
const dotProduct = this.dx * normalX + this.dy * normalY;
this.dx -= 2 * dotProduct * normalX * restitution;
this.dy -= 2 * dotProduct * normalY * restitution;
this.x += normalX * penetrationDepth;
this.y += normalY * penetrationDepth;
}
}
class Obstacle {
// ... (Obstacle class remains largely the same) ...
}
// ... (marble and obstacle creation remains the same) ...
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
marbles.forEach(marble => marble.update());
obstacles.forEach(obstacle => obstacle.draw());
requestAnimationFrame(animate);
}
animate();
Não se esqueça de incluir o elemento <canvas id="simulationCanvas"></canvas> no arquivo HTML. Essa versão aprimorada adiciona massa às bolinhas e simplifica um pouco a resolução de colisão. Para recursos mais avançados (colisões de bolinhas, particionamento espacial), será necessário implementar algoritmos mais complexos. Considere pesquisar técnicas como a detecção de colisão de fase ampla e estreita para melhorar o desempenho com muitos objetos.
|
Modelo: |
gemini-1.5-flash-002 |
Temperatura: |
0,2 |
Máximo de tokens de saída: |
8192 |
TopK: |
40.0 |
TopP: |
0,95 |
|