我有这个奇怪的问题。我正在尝试编写一些 2d 碰撞并偶然发现碰撞检测程序。我决定尝试将其翻译成 javascript,并可能在此过程中学到一些东西。
问题是,当我在浏览器中运行它时,一旦两个圆圈发生碰撞,所有圆圈就会冻结在原地,但程序不会崩溃,我在控制台中也没有错误。
我试图调试它,我认为问题出在 checkForCollision 函数中的第一个 if 语句中。就像它总是假的一样。
这是原始版本的链接(向下滚动到“清单 3”以获取完整代码): http ://compsci.ca/v3/viewtopic.php?t=14897&postdays=0&postorder=asc&start=0
这是我的翻译:
var canvas = document.getElementById('canvas01');
var drawFps = document.getElementById('fps');
var context = canvas.getContext('2d');
// The amount of delay between frames
var delay = 50;
// The maximum distance two circles can be apart and still be considered colliding
var epsilon = 10^-9;
// The number of circles
var numCircles = 5;
// We anticipate many circles so we create an array
var circles = new Array();
// Stores the amount of time untill a collision occurs
var t;
// Initialize the circles
createCircle();
function createCircle() {
for(var i = 0; i < numCircles; i++) {
var velX = Math.floor(Math.random() * 5) + 1; // this will get a number between 1 and 5;
velX *= Math.floor(Math.random() * 2) == 1 ? 1 : -1; // this will add minus sign in 50% of cases
var velY = Math.floor(Math.random() * 5) + 1;
velY *= Math.floor(Math.random() * 2) == 1 ? 1 : -1;
var radius = Math.floor(Math.random() * 30) + 15;
var mass = Math.PI * Math.pow(radius, 2);
circleData = {
x : Math.floor(Math.random() * canvas.width),
y : Math.floor(Math.random() * canvas.height),
vx : velX,
vy : velY,
vxp : velX,
vyp : velY,
r : radius,
m : mass
}
circles.push(circleData);
}
}
setInterval(loop, 17);
// Returns the amount of frames untill a collision will occur
function timeToCollision() {
var t = Number.MAX_VALUE;
var A;
var B;
var C;
var D;
var DISC;
// Loop through every pair of circles and calculate when they will collide
for(var i = 0; i < circles.length; i++) {
for(var j = 0; j < circles.length; j++) {
if(movingToCircle (circles[i], circles[j])) {
// Breaking down the formula for t
A = Math.pow(circles[i].vx, 2) + Math.pow(circles[i].vy, 2) - 2 * circles[i].vx * circles[j].vx + Math.pow(circles[j].vx, 2) - 2 * circles[i].vy * circles[j].vy + Math.pow(circles[j].vy, 2);
B = -circles[i].x * circles[i].vx - circles[i].y * circles[i].vy + circles[i].vx * circles[j].x + circles[i].vy * circles[j].y + circles[i].x * circles[j].vx - circles[j].x * circles[j].vx + circles[i].y * circles[j].vy - circles[j].y * circles[j].vy;
C = Math.pow(circles[i].vx, 2) + Math.pow(circles[i].vy, 2) - 2 * circles[i].vx * circles[j].vx + Math.pow(circles[j].vx, 2) - 2 * circles[i].vy * circles[j].vy + Math.pow(circles[j].vy, 2);
D = Math.pow(circles[i].x, 2) + Math.pow(circles[i].y, 2) - Math.pow(circles[i].r, 2) - 2 * circles[i].x * circles[j].x + Math.pow(circles[j].x, 2) - 2 * circles[i].y * circles[j].y + Math.pow(circles[j].y, 2) - 2 * circles[i].r * circles[j].r - Math.pow(circles[j].r, 2);
DISC = Math.pow((-2 * B), 2) - 4 * C * D;
// If the discriminent if non negative, a collision will occur and
// we must compare the time to our current time of collision. We
// udate the time if we find a collision that has occurd earlier
// than the previous one.
if(DISC >= 0) {
// We want the smallest time
t = Math.min(Math.min(t, 0.5 * (2 * B - Math.sqrt(DISC)) / A), 0.5 * (2 * B + Math.sqrt(DISC)) / A)
}
}
}
}
return t;
}
// Draws all the circles to the screen
function drawCircles() {
for(var i = 0; i < circles.length; i++) {
context.fillStyle = '#000000';
context.beginPath();
context.arc(circles[i].x, circles[i].y, circles[i].r, 0, 2 * Math.PI, true);
context.closePath();
context.fill();
}
}
// Updates all the circles attributes. If a collision Occures in between frames,
// the circles will be updated to the point of the collision. We return when the
// collision occurs so that we can adjust the delay in the main loop.
function updateCircles() {
// We want to increment by at most one frame
var t = Math.min(1, timeToCollision());
for(var i = 0; i < circles.length; i++) {
circles[i].x += circles[i].vx * t;
circles[i].y += circles[i].vy * t;
}
return t;
}
// Collision reaction function
function collide(c1, c2) {
var nx = (c1.x - c2.x) / (c1.r + c2.r);
var ny = (c1.y - c2.y) / (c1.r + c2.r);
var a1 = c1.vx * nx + c1.vy * ny;
var a2 = c2.vx * nx + c2.vy * ny;
var p = 2 * (a1 - a2) / (c1.m + c2.m);
c1.vxp = c1.vx - p * nx * c2.m;
c1.vyp = c1.vy - p * ny * c2.m;
c2.vxp = c2.vx + p * nx * c1.m;
c2.vyp = c2.vy + p * ny * c1.m;
}
// Checks if a collision has occured between any of the circles
function checkForCollision() {
for(var i = 0; i < circles.length; i++) {
for(var j = 0; j < circles.length; j++) {
if(movingToCircle(circles[i], circles[j]) && Math.pow((circles[j].x - circles[i].x), 2) + Math.pow((circles[j].y - circles[i].y), 2) <= Math.pow((circles[i].r + circles[j].r + epsilon), 2)) {
collide(circles[i], circles[j]);
}
}
if(circles[i].x < 1 || circles[i].x > canvas.width) {
circles[i].vxp *= -1;
}
if(circles[i].y < 1 || circles[i].y > canvas.height) {
circles[i].vyp *= -1;
}
}
for(var i = 0; i < circles.length; i++) {
circles[i].vx = circles[i].vxp;
circles[i].vy = circles[i].vyp;
}
}
// Tells us if two circles are moving towards each other
function movingToCircle(c1, c2) {
// Position Vector dotted with the Relative Velocity Vector
return (c2.x - c1.x) * (c1.vx - c2.vx) + (c2.y - c1.y) * (c1.vy - c2.vy) > 0;
}
// Main animation loop
function loop() {
// Clear Canvas
context.fillStyle = '#ffffff';
context.fillRect( 0, 0, canvas.width, canvas.height );
drawCircles();
checkForCollision();
t = updateCircles();
}
请注意,我将球改为圆形只是因为我发现它更适合 2d。
先感谢您。