Saturday, 27 September 2014

Calculating collision detection and rebound angles using vectors

Having given some consideration to how we could use vectors for collision detection as well as calculating rebounds/ricochets, it's time to get coding.

After a number of false starts, and drawing far too many triangles and intersecting vectors, we reckon we've got a workable approach. It's using the dot product of two vectors.
Let's say we've calculated that we have two intersecting lines/vectors - our playing piece is firing a weapon in one direction, but the destination point is intersected by a wall on the map. We want to calculate the angle at which the bullet will bounce off the wall.


Just by looking at the images above, we can see that the angle that the object hits the wall should also be the angle that it bounces off. So when the bullet is fired almost straight at the wall, the angle of deflection for the rebounding bullet is quite steep. However, if the bullet is fired at the wall from a shallow angle to begin with, it almost glances off the wall, with the amount of deflection minimised.

Our task is to work out the angles in the diagrams above. To do this, we're going to create two vectors, but both with a common "origin" - the point of collision. So we create one vector between the starting point and the point of collision, and another vector, from the point of collision to the endpoint of the wall/obstacle being struck


It doesn't really matter which endpoint of the wall we choose. If, after our calculations we have an angle greater than 90 degrees, we can easily find the value of the "correct" angle, by subtracting this value from 180.

Now using online resources for dot products of vectors (it's been a long time since we did any of this kind of trigonometry!) we can calculate the dot product of the vectors to work out the angle between them. This website - http://www.mathsisfun.com/algebra/vectors-dot-product.html - gives a great explanation of how to do this



Once we have an acute angle between the two vectors, we know that the angle of ricochet/rebound is going to be 180 - (2*approach angle). And once we have the angle of rebound, we can add this to the angle of the wall, and use the distance beyond the point of collision to the original destination, to work out where the ricocheted bullet actually ends up.

Now the dot product of two vectors a+b is:

a.b = (ax * bx) + (ay * by)
and this gives us a scalar value (i.e. a "plain" value, not a vector)

There's also another equation to calculate the dot product of two vectors:
a.b = |a| * |b| * cos(angle)
where |a| is the length of the vector a and |b| is the length of the vector b.

Since we're looking to calculate the angle, we can re-arrange this to
cos(angle) = ((ax * bx) + (ay * by)) / |a| * |b|

We're going to need to use pythagoras to calculate the length of the vectors, then add these into the equation, where |a| is the square root of (ax * ax) + (ay * ay) and |b| is the square root of (bx * bx) + y * by). So....

cos(angle) = ((ax * bx) + (ay * by)) / ( sqrt((ax*ax)+(ay*ay)) * sqrt((bx*bx)+(by*by)) )

Phew! That's a mouthful. So with the angle between the vectors calculated, we subtract two lots of these from 180 to get the angle of ricochet.


Now to calculate the final destination point, we can work out the angle between the starting point and add it to the angle of reflection, to get the total angle between the final destination point and the x axis.


And if we can work out the total angle from the x axis to the destination point (the sum of the blue and green angles) we can simply subtract from 180 to get the angle of the destination vector (shown in red in the diagram below):


We already know the length of the vector (it's the bit left over from the collision point to the original destination point) which is marked as "d" in the image above. We also know the angle of the vector (marked in red, above) so we can calculate the value of x in the destination point, with simple trigonometry - x = d * cosA.

Since tanA = opposite/adjacent, and we know the angle, and we know the length of the adjacent side, we can re-arrange this to give us opposite (y) = tanA / x.

After all that, we have x and y values for the final destination point relative to the collision point. So we add these to the x/y values of the collision point, and we (finally) have the absolute co-ordinates of the final destination.

Wow.
That's the theory.
Time to write some code.........