Game Development Reference

In-Depth Information

large penalties are rarely encountered because ships have to alter course to avoid land!

This makes “as the crow flies” examples unrealistic.

If your game is providing navigation information to anyone but pilots, it will probably

be using rhumb lines. The following are the formulas used to calculate distance and

bearing between two coordinates on a rhumb line. The easiest way to begin is to flatten

the globe. In a Mercator projection, rhumb lines are straight. In fact, this makes graph‐

ically solving the problem very simple. You use a ruler. Mathematically, things get a bit

more complicated. The following equation gives Δφ, which is the difference in latitude

after taking into account that we have stretched them in order to flatten the sphere:

Δφ = ln[tan(lat2/2 + π/4) / tan(lat1/2 + π/4)]

The distance between two points on a rhumb line is given by:

D
=
Δ
la
2
+
q
2
*
Δ
lon
2
*
R

The variable
q
is a value whose formula depends on Δφ. If Δφ is equal to 0, that means

that the calculated course is going to be either directly east or west. If that is the case,

then the intermediate value of
q
is:

q = cos(lat1)

if Δφ is not equal to 0, then:

q = Δ
lat
/Δφ

You can see that if not properly implemented, a direct east or west course would result

in division by 0. Finally, the constant bearing is:

Θ
rhumb
= atan2(Δ
long
, Δφ)

There are, in fact, an infinite number of rhumb lines that will get us to our end point.

However, the longer ones will either take us the wrong way around the globe, or spiral

around the globe before hitting our end point. At any rate, the shortest route will be the

one in which Δ
long
is less that 180°. The preceding is implemented in Objective-C as

follows:

float rhumbBearing ( Coordinate2D startPoint, Coordinate2D endPoint){

//Convert location from degrees to radians

float lat1 = (M_PI/180.) * startPoint.lat;

float lon1 = (M_PI/180.) * startPoint.longi;

float lat2 = (M_PI/180.) * endPoint.lat;

float lon2 = (M_PI/180.) * endPoint.longi;