During our initial brainstorming sessions we acknowledged the need to
spend some time exploring the physics-related aspects of the game.
However, looking back, I think it's fair to say that we underestimated
the complexity of the required computations. Naively thinking that our
faint recollection of various formulas from old physics classes would
suffice, we charged forward and planned to tackle all physics-related
requirements as they arose. This resulted in quite a few learning
experiences and code refactoring sessions as the overall complexity
gradually rose over time. In the end we decided to integrate a robust,
third party physics engine into the game. But before arriving at this
final conclusion we implemented a number of our own home-brewed
solutions. In this post I will describe our original method for collision
detection (such as between two cars). Although we are now using a
powerful physics engine to handle much of this work for us, the engine
itself also uses this method (among others) underneath the hood. My
intention is to perhaps offer some guidance to anyone wishing to
implement their own game physics or to just give a quick look at some of
the inner-workings of what you see in the game. At the core of our method of collision detection is the Separating Axis Theorem (Wikipedia link).
The theorem simply states that if two convex objects are not
intersecting, then there exists an axis for which the projections of the
two objects do not overlap. Note that if you wish to test a concave
object you must first break it down to multiple convex objects and
individually test each one. SAT is an efficient, generic algorithm for
detecting overlap between two entities and its ability to execute
relatively quickly makes it often ideal for collision detection in
games. To picture this concept of "overlapping projections", take a look at the image below. In diagram a
you see two squares which are clearly not intersecting. If you were to
project their respective images onto the horizontal axis as pictured,
their projections would not intersect either. As soon as you have found
an axis in which these projections do not overlap, you can assert that
the objects do not intersect. Conversely, the two squares are clearly
intersecting in diagram b. For every possible axis, the
projections of the two objects will also overlap, allowing you to assert
that the objects themselves are overlapping. For any two polygons, the
only axes you must test against are the normals of each edge. To obtain
the normal of a vector you simply swap the coordinates and negate one
(ie. [5,3] becomes [-3,5]).Unlike
the previous image, objects being tested for collision won't usually be
simple, un-rotated squares. You might be dealing with a complex polygon
with many vertices. You may be wondering then, what the best way to
programatically project these polygons onto an axis is. The answer,
luckily, isn't very difficult. You would simply loop over all of a
polygon's vertices, perform a dot product
between the vertex and the normalized axis to be projected on, and
store the minimum and maximum result. Remember that there is a
difference between a vector's normal and a normalized vector! In simple pseudocode, this would look similar to the foll
We
now know enough to be able to assemble a simple function for
determining whether or not two objects are intersecting. But in our
racing game, we needed more than simply knowing if two cars are
colliding. We also wanted to prevent them from simply driving right over
the top of each other. For this reason it is beneficial to calculate
the Minimum Translation Vector - a vector which tells us the shortest
path to push the two objects apart to keep them from intersecting. Your
final collision detection code might look similar to the following
pseudocode:
Don't forget that the array of axes in the above code would have to consist of both objects' axes needing testing.This
was a very simplistic overview of the algorithm we employed for
collision detection in our beginning stages of development. Before using
it yourself, you would likely want to spend some time making the
algorithm more efficient (such as by not testing parallel axes) and
adding additional functionality to handle curved features or to properly
handle the case in which one object is completely engulfed by the
other.
Collision detection is just one facet of our physics-related computations. Things seem to get exponentially more complex when you begin to account for resultant forces due to a collision, cancelling out a car's lateral movement due to traction, applying the appropriate amount of torque to turn a car just the right amount, etc. In future blog posts we will discuss our move to the JBox2D physics engine, how much easier this made our lives, and all the cool physics stuff we've been able to put into the game. |