Thursday, November 10, 2016

2d - How can I Intercept object with a circular motion


I am creating a 2d space game and need to make the spaceship intercept a planet. I have working code for straight line intercepts but cannot figure out how to calculate the planets location in a circular orbit.


The game is not scientifically accurate so I am not worried about inertia, gravity, elliptical orbits, etc.


I know the spaceships location and speed and also the planets orbit (Radius) and speed


enter image description here



Answer



An analytic solution to this is difficult, but we can use binary search to find a solution to within the required accuracy.


The ship can reach the closest point on the orbit in time t_min:


shipOrbitRadius = (ship.position - planet.orbitCenter).length;
shortestDistance = abs(shipOrbitRadius - planet.orbitRadius);

t_min = shortestDistance/ship.maxSpeed;

The ship can reach ANY point on the orbit in time less than or equal to t_max:


(Here, for simplicity, I assume the ship can drive through the sun. If you want to avoid this then you will need to switch to non-straight-line paths for at least some cases. "Kissing circles" may look nice and orbital mechanics-y, without changing the algorithm by more than a constant factor)


if(shipOrbitRadius > planet.orbitRadius)
{
t_max = planet.orbitRadius * 2/ship.maxSpeed + t_min;
}
else
{

t_max = planet.orbitRadius * 2/ship.maxSpeed - t_min;
}

If our orbital period is short, we might be able to improve on this upper bound by choosing the t_max to be the first time after t_min that the planet makes its closest approach to the ship's start position. Take whichever of these two values of t_max is smaller. See this later answer for a derivation of why this works.


Now we can use binary search between these extremes, t_min and t_max. We'll search for a t-value that gets the error close to zero:


error = (planet.positionAtTime(t) - ship.position).squareMagnitude/(ship.maxSpeed*ship.maxSpeed) - t*t;

(Using this construction, error @ t_min >= 0 and error @ t_max <= 0, so there must be at least one intercept with error = 0 for a t-value in-between)


where, for completeness, the position function is something like...


Vector2 Planet.positionAtTime(float t)

{
angle = atan2(startPosition - orbitCenter) + t * orbitalSpeedInRadians;
return new Vector2(cos(angle), sin(angle)) * orbitRadius + orbitCenter;
}

Note that if the planet's orbital period is very short compared to the ship's speed, this error function may change signs several times over the span from t_min to t_max. Just keep track of the earliest +ve & -ve pair you encounter, and continue searching between them until the error is close enough to zero ("close enough" being sensitive to your units and gameplay context. The square of half the frame duration may work well - that ensures the interception is accurate to within a frame)


Once you have a nice error-minimizing t, you can just point the ship at planet.positionAtTime(t) and go full throttle, confident that the planet will reach that point at the same time you do.


You can always find a solution within Log_2((2 * orbitRadius/ship.maxSpeed)/errorThreshold) iterations. So for example, if my ship can traverse the orbit in 60 frames, and I want an intercept accurate to within one frame, I'll need about 6 iterations.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...