How to circle a target at a constant distance

I have an AI ship that stops approaching when it gets within firing range, and starts strafing around its target. The problem is that it doesn't circle its target very well. The following code attempts to do this, and for short periods of time, it's convincing, but as soon as you let it run for ~5-10 seconds, the AI ship drifts out of range of the target and is forced into the approach state again.

Vector2 tangent = Vector2.Normalize(new Vector2(TargetVector.Y, -TargetVector.X));
velocity += 0.8f * tangent * accelerationMagnitude * (float)gameTime.ElapsedGameTime.TotalSeconds;
velocity += 0.2f * TargetVector * accelerationMagnitude * (float)gameTime.ElapsedGameTime.TotalSeconds;


The above doesn't work for obvious reasons, so I attempted to bring the target into my frame of reference using the following:

Vector2 tangent = new Vector2(TargetVector.Y, -TargetVector.X);
Vector2 asdf = tangent + (velocity * (float)gameTime.ElapsedGameTime.TotalSeconds);
velocity += Vector2.Normalize(asdf) * accelerationMagnitude * (float)gameTime.ElapsedGameTime.TotalSeconds;


Here is a quick video on how it behaves using the second method while trying to maintain a firing distance of 350: https://www.youtube.com/watch?v=16Bc46ekJI0 The white line and number indicate the Ship->Station vector and distance, and red circles are the weapon ranges.

How do I make my AI ship circle its prey convincingly?

2 Answers

Rather than attempting to calculate acceleration so that the physics system will generate the desired motion, it's probably better to calculate the desired motion directly, like

angle += rotationRate * (float)gameTime.ElapsedGameTime.TotalSeconds;
Vector2 desiredPos = new Vector2(radius * cos(angle), radius * sin(angle));


You can then apply some type of steering behavior so that the AI ship will attempt to stay on top of desiredPos. Or, simply set the AI ship's position to desiredPos if it's close enough. You can calculate velocity and acceleration by taking derivatives of desiredPos if you need them for other reasons (for instance, showing thruster sprites based on acceleration).

If your ship gets too far from the target, you can switch off this behavior; and in the approach phase, you can set angle based on the angle your ship is coming in from, so the desiredPos will be in the right place when the circling behavior switches on.

• Much better now. I used a desiredPosition with a steering algorithm to get it to orbit: i.sstatic.net/Sf7kO.gif Thanks! Commented May 8, 2014 at 4:20

Nathan Reed's answer is probably the right solution for you. That said, you could also model the distance constraint you're imposing on the ships and solve for an acceleration that keeps your ship rigidly tethered to its prey, while still allowing you to apply acceleration as you are. This paper shows how to do this at the acceleration level; it's not terribly complicated.