I am re-asking this question since I did not make myself clear in what I wanted in my last question.
Does anyone know how to do elastic collision or handle collision
The answer is actually quite simple: swap the velocities of each block when they collide. That's it! Also for your collision test change RectA.x
to just x
, since they are normal variables given:
function collides(rectA, rectB) {
return !(x + rectA.width < x2 ||
x2 + rectB.width < x ||
y + rectA.height < y2 ||
y2 + rectB.height < y);
};
And swapping velocities:
if(collides(drawing, drawing2)){
var t = vx; var t2 = vy;
vx = vx2; vy = vy2;
vx2 = t; vy2 = t2;
};
And after those small changes we have working elastic collisions: http://jsfiddle.net/Y7MFq/11/
To do a rectangular collision detection can be more complicated than it perhaps looks.
It's not just about figuring out if the two rectangles intersects or overlaps, but we also need to know at what angle they collide and what direction they move in order to deflect them properly, ideally transfer "velocity" to each other (mass/energy) and so forth.
This method that I present here will do the following steps:
The code for detecting the intersection and angle is as follows, where r1
and r2
are here objects with properties x
, y
, w
and h
.
function collides(r1, r2) {
/// classic intersection test
var hit = !(r1.x + r1.w < r2.x ||
r2.x + r2.w < r1.x ||
r1.y + r1.h < r2.y ||
r2.y + r2.h < r1.y);
/// if intersects, get angle between the two rects to determine hit zone
if (hit) {
/// calc angle
var dx = r2.x - r1.x;
var dy = r2.y - r1.y;
/// for simplicity convert radians to degree
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
if (angle < 0) angle += 360;
return angle;
} else
return null;
}
This function will return an angle or null
which we then use to determine deflection in our loop (that is: the angle is used to determine the hit zone in our case). This is needed so that they bounce off in the correct direction.
With just a simple intersection test and deflection you can risk the boxes deflecting like the image on the right, which is not correct for a 2D scenario. You want the boxes to continue in the same direction of where there is no impact as in the left.
Here is how we can determine which velocity vector to reverse (tip: if you want a more physical correct deflection you can let the rectangles "absorb" some of the other's velocity but I won't cover that here):
var angle = collides({x: x, y: y, w: 100, h: 100}, /// rect 1
{x: x2, y: y2, w: 100, h: 100}); /// rect 2
/// did we have an intersection?
if (angle !== null) {
/// if we're not already in a hit situation, create one
if (!hit) {
hit = true;
/// zone 1 - right
if ((angle >= 0 && angle < 45) || (angle > 315 && angle < 360)) {
/// if moving in + direction deflect rect 1 in x direction etc.
if (vx > 0) vx = -vx;
if (vx2 < 0) vx2 = -vx2;
} else if (angle >= 45 && angle < 135) { /// zone 2 - bottom
if (vy > 0) vy = -vy;
if (vy2 < 0) vy2 = -vy2;
} else if (angle >= 135 && angle < 225) { /// zone 3 - left
if (vx < 0) vx = -vx;
if (vx2 > 0) vx2 = -vx2;
} else { /// zone 4 - top
if (vy < 0) vy = -vy;
if (vy2 > 0) vy2 = -vy2;
}
}
} else
hit = false; /// reset hit when this hit is done (angle = null)
And that's pretty much it.
The hit
flag is used so that when we get a hit we are marking the "situation" as a hit situation so we don't get internal deflections (which can happen at high speeds for example). As long as we get an angle after hit is set to true we are still in the same hit situation (in theory anyways). When we receive null we reset and are ready for a new hit situation.
Also worth to mention is that the primary rectangle here (whose side we check against) is the first one (the black in this case).
If you want to throw in more that two rectangle then I would suggest a different approach than used here when it comes to the rectangles themselves. I would recommend creating a rectangle object which is self-contained in regards to its position, size, color and also embeds methods to update velocity, direction and paint. The rectangle objects could be maintained by a host objects which performs the clearing and calls the objects' update method for example.
To detect collisions you could then iterate the array with these objects to find out which rectangle collided with the current being tested. It's important here that you "mark" (using a flag) a rectangle that has been tested as there will always be at least two in a collision and if you test A and then B you will end up reversing the effect of velocity change without using a flag to skip testing of the collision "partner" object per frame.
Note: there are special cases not covered here such as collision on exact corners, or where a rectangle is trapped between an edge and the other rectangle (you can use the hit flag mentioned above for the edge tests as well).
I have not optimized any of the code but tried to keep it as simple as I can to make it more understandable.
Hope this helps!