Flukz — open-source shmup community resource open-source GPL devlog  ·  about
// indie shmup & game-dev resource

Shmup Design · June 30, 2026

Homing Weapon Math in Shmups: Pursuit Curves, Intercept Targeting, and Tuning

Homing weapons are among the most satisfying to fire and the hardest to implement correctly. The simple version — rotate toward the target every frame — produces a pure pursuit curve that spirals toward a stationary target but catastrophically overshoots a moving one at close range. The correct version predicts where the target will be and aims there instead, which requires a bit more math and a lot of careful tuning to feel fair rather than unavoidable.

Algorithm 1: Pure pursuit

Pure pursuit is the simplest homing implementation. Every frame, the missile rotates its velocity vector toward the current position of the target by a maximum turn rate, then moves forward at its constant speed. The code is straightforward:

desired_dir = (target.position - missile.position).normalized() current_dir = missile.velocity.normalized() missile.velocity = current_dir.rotated( clamp(current_dir.angle_to(desired_dir), -max_turn_rate, max_turn_rate) ) * missile_speed

Pure pursuit works well when the missile is significantly faster than the target, or when the target is stationary. Against a strafing target, pure pursuit produces the characteristic overshoot spiral: the missile passes the target, swings back, oscillates, and eventually catches it — or runs out of fuel if there is a range limit. The visual result reads as "dumb" to players who understand why it is happening.

Algorithm 2: Intercept targeting

Intercept targeting solves the overshoot problem by aiming at where the target will be when the missile arrives, rather than where the target is now. The math derives a lead vector based on current target velocity and missile travel time.

The simplest form uses a single-iteration estimate:

to_target = target.position - missile.position distance = to_target.length() travel_time = distance / missile_speed predicted_pos = target.position + target.velocity * travel_time aim_dir = (predicted_pos - missile.position).normalized() missile.velocity = aim_dir * missile_speed

This is a first-order approximation. If the target continues moving at its current velocity without turning, the missile will hit it exactly. Against a target that changes direction after firing, the missile will miss — but it will miss much less dramatically than pure pursuit, and the resulting near-miss usually looks intentional rather than broken.

A second iteration (recalculate travel_time using the updated predicted_pos) improves accuracy further and is still cheap enough to run every frame per missile.

Algorithm 3: Proportional navigation

Proportional navigation (PN) is the algorithm used in real guidance systems and produces the most natural-looking homing paths in games. Instead of pointing at the target, the missile measures the rate at which the line-of-sight angle to the target is rotating, and applies a lateral acceleration proportional to that rotation rate. Targets moving at constant velocity produce a straight-line intercept path with no oscillation.

PN is more complex to implement because it requires tracking the previous line-of-sight angle between frames. The gain constant N (typically 3–5 in real systems) controls aggressiveness. Higher N catches more evasive targets but produces wider turns that can look mechanical. For shmup player weapons, N of 2–3 usually feels best: capable but beatable by a fast sidestepping player.

Turn rate cap: making homing fair

Whatever algorithm you use, a maximum turn rate per frame is the most important parameter for fairness. Unlimited turn rate means the missile always catches a player regardless of distance or direction — it is inescapable. A capped turn rate means close-range targets in sharp angle positions can outmanoeuvre it by reversing direction.

Turn rate (deg/frame at 60fps) Feel Counterplay
2–3 degreesSluggish, easily dodgedCircle-strafe at medium range
5–7 degreesCapable threat, gives fair warningQuick direction reversal at close range
10–12 degreesAggressive, requires early reactionBait and juke before the missile closes
UnlimitedUnavoidableNone

Range and lifetime limits

Homing missiles that travel indefinitely are a common balance problem. An unlimited-range missile launched at the start of a boss fight will chase the player across the entire arena for the full duration of the boss phase if it misses. Adding a maximum lifetime (after which the missile fades and disappears) or a maximum range limit solves this without changing the homing math.

A lifetime-based approach also enables a visual design opportunity: the missile can visually fade or smoke more heavily near the end of its lifespan, communicating to the player that it will expire if they survive a few more seconds.

Lock-on vs fire-and-forget

Lock-on weapons delay firing until a lock is acquired (usually shown as a target indicator on the enemy), then fire a tracking missile. Fire-and-forget weapons fire immediately and home independently without requiring a maintained lock.

Lock-on adds counterplay options: the target can break lock by moving out of the lock cone, or the player can deliberately not fire to delay telegraphing their target. Fire-and-forget is simpler to implement and feels more responsive. For player weapons in a fast game, fire-and-forget is usually better. For enemy weapons that the player must dodge, lock-on with a visible indicator is preferred because it gives the player a warning period before the missile launches.

The most important test of any homing system: have a playtester try to dodge missiles intentionally. If they cannot find any counterplay at reasonable skill levels, reduce turn rate. If they never feel threatened, increase missile speed or reduce lifetime so the window of vulnerability is shorter and sharper.