Monday, 29 September 2014

Testing vector collision detection

So we had a little think about using vectors for collision detection. Then we decided rather than dismiss it out of hand, to consider the implications of using vectors instead of bitmap-based collision detection. And it turns out, it could be a Really Good Thing (capital R, capital G, capital T).

We rooted about online and tried to dredge up some basic GCSE-level trigonometry (at least it was GCSE-level when most of us sat our exams about a million years ago - it's probably Masters Degree level or something now!) and came up with some ideas for calculating the rebound angle for things thrown/fired at a wall, even if it has been placed at an angle.

This evening was all about the code.
Here are some screenshots of our testing:

The triangles simply help us to see which way the vector was drawn: in this case, our starting point is to the right and below the point of collision with the wall, which is angled to face the top-right corner of the screen.

Our code seems to have correctly calculated the angle of reflection - and placed the final destination point at the correct distance from the collision.

If we fire our object in a straight (horizontal) line at an angled surface, the angle of reflection (and distance to the final destination point) are once again correctly calculated.

If we use the same angled wall, but draw it as an opposite vector (i.e. from bottom-right to top-left, instead of from top-left to bottom-right) we still get the same result, as expected.

Coming at the wall from the other side gave a peculiar result.
Maybe it's to do with the ambiguity that negative angles create. Or maybe it's something else. Or maybe we've not actually fully understood how to convert polar vectors into cartesian co-ordinates. But something isn't quite right here.

On further inspection, it seems that the vector representing the angle of reflection is simply pointing 180 degrees the wrong way. Before we rip apart our code to find the bug, and spend hours and hours trying to work out if there are two possibilities for a destination if the angle is negative, we threw in a quick and dirty hack: if start.x < collision.x, rotation = rotation+180

This seemed to fix the problem!

 And even worked when the vector was drawn from "underneath" the point of collision - the rebound in this example is still correct: if we draw the perpendicular to the wall, at the point of collision, we'll see that the red line approaches from an angle just less than 90 degrees (and so should bounce off at a steep angle)

If we fire the same shot, but this time change the angle of the wall, the object still bounces off at the correct angle. It looks like our quick-n-dirty hack seems to have done the job!

Lastly, and for completeness, some testing using a vertical approach, just to make sure than a 90 degree or a 180 degree angle doesn't cause the cos/sin/tan functions to bork and throw out some division by zero errors, or something like that!

So there we have it - some successful testing - and in each case, not only do we get the correct angle, but also the distance of the deflected object also looks about right too.

What if we were to introduce a second wall?

Of course, we can see just from looking at the image that the object should hit the first wall, bounce upwards (as shown in the diagram), then hit the second wall, and be deflected back towards the bottom of the screen.

There's no need for us to write a routine which can handle multiple reflections - we can simply use the same one function, and call it recursively, until the path of the object no longer intersects with any vector that makes up an obstacle.

In this case, we could work out where on the first wall we can expect the object to collide. We then create a second, independent path, starting from the collision point, with the final destination point now being our new destination point. We put this vector/series of points into our collision detection routine and see if there is an intersection with any other obstacle vector.
If there is, we simply set the new starting point as the new collision point, and the new destination point as the newly calculated, final (deflected) destination point, and keep repeating until the object no longer collides with any obstacles and simply reaches the natural end of it's path.

It's probably a bit late now to be trying to code up a recursive function (they make your brain ache at the best of times!) but at least we're making some progress.....