简体   繁体   中英

JavaScript Circle Collision Detection Bug

I got this wierd bug in my circle/ball collision simulation I'm doing as a school project. I may also add that I'm somewhat of a noob, I've been programming for about a year.

The collisions are working fine until i populate the canvas with lots of circles. I can't really see why. 25 circles is working fine but when I go up to 50 the circles starts to get buggy.

As you can see in the link below the circles start to attach to one another after running some time and spinning around like crazy, which is not something I's like to happen.

Example of 50 Circles: https://dl.dropboxusercontent.com/u/9069602/circles/vers1/collisions.html

My guess is that the risk of circles attaching to one another is greater as the space between them decreases, but it seems they have enough space when starting the simulation. I use a nextY/nextX position variables to calculate an impact one frame before the actual impact is occurs. That may also be a source of bugs. I can't really point my finger at were to start debugging.

Here is the code for the 50 circle case: https://dl.dropboxusercontent.com/u/9069602/circles/vers1/circle.js

// Returns true if two circles are overlapping
function overlapDetection( circle1, circle2 ) {
    var returnValue = false;
    var dx = circle1.nextX - circle2.nextX;
    var dy = circle1.nextY - circle2.nextY;
    var distance = ( dx * dx + dy * dy );

    if ( distance <= ( circle1.radius + circle2.radius ) * ( circle1.radius + circle2.radius ) ) {
        returnValue = true;
    }
    return returnValue; 
}

function collide() {
    var circle;
    var testCircle;
    var returnValue = false;

    for ( var i = 0; i < circles.length; i += 1 ) {
        circle = circles[i];
        for ( var j = i + 1; j < circles.length; j += 1 ) {
            testCircle = circles[j];
            if ( overlapDetection( circle, testCircle ) ) {
                collideCircles( circle, testCircle );
                collideCircle1 = circle.id;
                collideCircle2 = testCircle.id;
                returnValue = true;
            }
        }
    }
    return returnValue;
}

function collideCircles( circle1, circle2 ) {

    var dx = circle1.nextX - circle2.nextX;
    var dy = circle1.nextY - circle2.nextY;
    var collisionAngle = Math.atan2( dy, dx );

    var speed1 = Math.sqrt( circle1.velocityX * circle1.velocityX + circle1.velocityY * circle1.velocityY );
    var speed2 = Math.sqrt( circle2.velocityX * circle2.velocityX + circle2.velocityY * circle2.velocityY );

    var direction1 = Math.atan2( circle1.velocityY, circle1.velocityX );
    var direction2 = Math.atan2( circle2.velocityY, circle2.velocityX );

    var rotatedVelocityX1 = speed1 * Math.cos( direction1 - collisionAngle );
    var rotatedVelocityY1 = speed1 * Math.sin( direction1 - collisionAngle );
    var rotatedVelocityX2 = speed2 * Math.cos( direction2 - collisionAngle );
    var rotatedVelocityY2 = speed2 * Math.sin( direction2 - collisionAngle );

    var finalVelocityX1 = ( ( circle1.mass - circle2.mass ) * rotatedVelocityX1 + ( circle2.mass + circle2.mass ) * rotatedVelocityX2 ) / ( circle1.mass + circle2.mass );
    var finalVelocityX2 = ( (circle1.mass + circle1.mass ) * rotatedVelocityX1 + ( circle2.mass - circle1.mass ) * rotatedVelocityX2 ) / ( circle1.mass + circle2.mass );

    var finalVelocityY1 = rotatedVelocityY1;
    var finalVelocityY2 = rotatedVelocityY2;

    circle1.velocityX = Math.cos( collisionAngle ) * finalVelocityX1 + Math.cos( collisionAngle + Math.PI / 2 ) * finalVelocityY1;
    circle1.velocityY = Math.sin( collisionAngle ) * finalVelocityX1 + Math.sin( collisionAngle + Math.PI / 2 ) * finalVelocityY1;
    circle2.velocityX = Math.cos( collisionAngle ) * finalVelocityX2 + Math.cos( collisionAngle + Math.PI / 2 ) * finalVelocityY2;
    circle2.velocityY = Math.sin( collisionAngle ) * finalVelocityX2 + Math.sin( collisionAngle + Math.PI / 2 ) * finalVelocityY2;

    circle1.nextX += circle1.velocityX;
    circle1.nextY += circle1.velocityY;
    circle2.nextX += circle2.velocityX;
    circle2.nextY += circle2.velocityY;
}

I hope I'm being clear about the problem.

Thanks in advance!

Your problem is two-fold.

Your collideCircles(circle1, circle2) assumes circle1 and circle2 will be colliding at next coordinates, which is guaranteed by collide() and are not colliding in current coordinates (see how you calculate collisionAngle ), which may not be true at all. This is why your circles can get "stuck" if they get on top of each other.

Circles can end up on top of each other if, say collideCircles(circle1, circle2) and collideCircles(circle3, circle4) bounce circle2 and circle4 so that their next coordinates will be on top of each other. However, the collision detection loop has already passed over them and their collision won't be picked and the circles will happily move on top of each other.

Let's assume you've developed a hinge joint! =) Actually there could be several issues. Eg: when you check for a free space, another circle can already occupy it; I think that scenario is next: lets assume that two circles have

r=1; y =0;
circle1.x = 1, circle2.x =4;

checking next step:

circle1.canMoveToX(2) //=> true; coz border will move to x=3 it's empty at the moment;
circle2.canMoveToX(3)  // => true; coz border will move to x=2 it's empty at the moment;

and at this step they join.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM