Game Development 11 min read

How to Build a Missile Auto‑Tracking System in Python with Pygame

This article explains how to implement an automatic missile‑tracking system for a shooting game using Python’s Pygame library, covering the underlying differential‑equation‑based algorithm, step‑by‑step trigonometric calculations, handling of coordinate systems, image rotation challenges, and provides complete source code.

21CTO
21CTO
21CTO
How to Build a Missile Auto‑Tracking System in Python with Pygame

Automatic tracking algorithms are commonly used in shooting games; they solve differential equations rather than being exclusive to military applications.

The method relies on dividing time into tiny slices (for example 1/1000 s), constructing a right‑angled triangle for each slice to compute the missile’s direction angle and travel distance vt = |AC|, then updating the target’s position and repeating the process.

Assuming initial missile coordinates (x1, y1) and target coordinates (x, y), a right triangle ABE provides the sine and cosine of angle a; the missile moves AD = vt·cos a horizontally and CD = vt·sin a vertically.

The distance between two points is calculated with the standard formula, allowing the computation of sin a and cos a:

Consequently, the new missile position after one time slice is:

And the coordinates of point C (the missile’s new location) are given by:

Below is a minimal Pygame implementation that tracks the mouse cursor as the target:

import pygame, sys
from math import *
pygame.init()
screen = pygame.display.set_mode((800, 700), 0, 32)
missile = pygame.image.load('element/red_pointer.png').convert_alpha()
x1, y1 = 100, 600  # missile start position
velocity = 800        # missile speed
time = 1/1000        # length of each time slice
clock = pygame.time.Clock()
old_angle = 0
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
    clock.tick(300)
    x, y = pygame.mouse.get_pos()  # target position
    distance = sqrt(pow(x1 - x, 2) + pow(y1 - y, 2))
    section = velocity * time
    sina = (y1 - y) / distance
    cosa = (x - x1) / distance
    angle = atan2(y - y1, x - x1)
    x1, y1 = (x1 + section * cosa, y1 - section * sina)
    d_angle = degrees(angle)
    screen.blit(missile, (x1 - missile.get_width(), y1 - missile.get_height() / 2))
    dis_angle = d_angle - old_angle
    old_angle = d_angle
    pygame.display.update()

When the missile image is rotated to follow the target, the pivot point shifts because the rotated surface’s bounding box changes size. The following image shows the rotated missile and its enlarged bounding box.

Rotating by 90° further illustrates how the image dimensions vary:

To keep the missile tip aligned with the calculated position, the algorithm computes the new head point after each rotation and offsets the blit coordinates accordingly. The following diagram shows the corrected alignment.

The head‑point calculation differs by quadrant; the article provides separate formulas for quadrants 1‑2 and 3‑4 (shown below).

The complete, refined code that handles rotation, pivot correction, and displays the target as a red asterisk is shown below:

import pygame, sys
from math import *
pygame.init()
font1 = pygame.font.SysFont('microsoftyaheimicrosoftyaheiui', 23)
textc = font1.render('*', True, (250, 0, 0))
screen = pygame.display.set_mode((800, 700), 0, 32)
missile = pygame.image.load('element/rect1.png').convert_alpha()
height = missile.get_height()
width = missile.get_width()
pygame.mouse.set_visible(0)
x1, y1 = 100, 600  # missile start position
velocity = 800
time = 1/1000
clock = pygame.time.Clock()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
    clock.tick(300)
    x, y = pygame.mouse.get_pos()
    distance = sqrt(pow(x1 - x, 2) + pow(y1 - y, 2))
    section = velocity * time
    sina = (y1 - y) / distance
    cosa = (x - x1) / distance
    angle = atan2(y - y1, x - x1)
    fangle = degrees(angle)
    x1, y1 = (x1 + section * cosa, y1 - section * sina)
    missiled = pygame.transform.rotate(missile, -fangle)
    if 0 <= -fangle <= 90:
        A = (width * cosa + x1 - width, y1 - height / 2)
        B = (A[0] + height * sina, A[1] + height * cosa)
    if 90 < -fangle <= 180:
        A = (x1 - width, y1 - height / 2 + height * (-cosa))
        B = (x1 - width + height * sina, y1 - height / 2)
    if -90 <= -fangle < 0:
        A = (x1 - width + missiled.get_width(), y1 - height / 2 + missiled.get_height() - height * cosa)
        B = (A[0] + height * sina, y1 - height / 2 + missiled.get_height())
    if -180 < -fangle < -90:
        A = (x1 - width - height * sina, y1 - height / 2 + missiled.get_height())
        B = (x1 - width, A[1] + height * cosa)
    C = ((A[0] + B[0]) / 2, (A[1] + B[1]) / 2)
    screen.fill((0, 0, 0))
    screen.blit(missiled, (x1 - width + (x1 - C[0]), y1 - height / 2 + (y1 - C[1])))
    screen.blit(textc, (x, y))
    pygame.display.update()

The final animation shows a missile that smoothly follows the mouse cursor, correctly rotated and anchored at its tip.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

algorithmGame DevelopmentPygametrigonometryMissile Tracking
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.