# Python/Pygame trigonometry: Following the hypotenuse?

624
February 07, 2017, at 9:10 PM

I have a method in my `Enemy` class called `huntPlayer`. It takes a player object `p`. Here it is:

``````def huntPlayer(self, p):
if self.dist2p < 200:
self.hunting = True
if p.x > self.rect.x:
self.rect.x += self.speed #this is a constant at value 1
elif p.x < self.rect.x:
self.rect.x -= self.speed
else:
self.rect.x += 0
if p.y > self.rect.y:
self.rect.y += self.speed
elif p.y < self.rect.y:
self.rect.y -= self.speed
else:
self.rect.y += 0
else:
self.rect.x += 0
self.rect.y += 0
``````

The enemies are randomly placed around a 2d top down plain, they randomly roam this plain. I have calculated the hypotenuse which is shortest distance to the player = `Enemy.dist2p` -- When the `dist2p` value < 200. The enemy will move towards player, `p.x` and `p.y` respectively.

My solution above is crude so therefore my problem is the enemy equally moves 1 place on the x or y axis resulting in a diagonal movement to each axis, then sliding along the axis until it reaches the player. (The player is in a fixed position near the centre screen.)

Can you help me fix the `huntPlayer` method/algorithm so the enemy follows the hypotenuse path to the player, rather than quickest path to x/y axis?

Thanks guys.

EDIT: If you need any further info I may have left out, let me know.

Moving on the hypotenuse will most likely require your object to move less than one pixel each frame in either the y or x-axis, and since `rects` only can hold integer you'd need a new attribute `position` which contains the position of the sprite in float precision. You can use `pygame.math.Vector2` to create a vector with useful methods as `normalize()` and adding, subtracting, multiplying with other vectors etc.

Assuming you create an attribute `self.position = pygame.math.Vector2(0, 0)` (or whatever position it starts on) you could do something like this:

``````def hunt_player(self, player):
player_pos = pygame.math.Vector2(player.rect.topleft)
direction = player_pos - self.position
velocity = direction.normalize() * self.speed
self.position += velocity
self.rect.topleft = self.position
``````

By subtracting the player's position with the enemies current position, you'll get a vector that points from the enemy to the player. If we would to add the direction vector ro our position we would teleport to the player immediately. Instead we normalize the vector (making it to length 1 pixel) and multiply our speed attribute. The new created vector will be an arrow pointing towards the player with the length of our speed.

## Full example

``````import pygame
pygame.init()

SIZE = WIDTH, HEIGHT = 720, 480
screen = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()
FPS = 60

class Hunter(pygame.sprite.Sprite):
def __init__(self, pos):
super(Hunter, self).__init__()
self.image = pygame.Surface((32, 32))
self.image.fill(pygame.Color('red'))
self.rect = self.image.get_rect(topleft=pos)
self.position = pygame.math.Vector2(pos)
self.speed = 2
def hunt_player(self, player):
position = player.rect.topleft
direction = position - self.position
velocity = direction.normalize() * self.speed
self.position += velocity
self.rect.topleft = self.position
def update(self, player):
self.hunt_player(player)

class Player(pygame.sprite.Sprite):
def __init__(self, pos):
super(Player, self).__init__()
self.image = pygame.Surface((32, 32))
self.image.fill(pygame.Color('blue'))
self.rect = self.image.get_rect(topleft=pos)
self.position = pygame.math.Vector2(pos)
self.velocity = pygame.math.Vector2(0, 0)
self.speed = 3
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.velocity[0] = -self.speed
elif keys[pygame.K_RIGHT]:
self.velocity[0] = self.speed
else:
self.velocity[0] = 0
if keys[pygame.K_UP]:
self.velocity[1] = -self.speed
elif keys[pygame.K_DOWN]:
self.velocity[1] = self.speed
else:
self.velocity[1] = 0
self.position += self.velocity
self.rect.topleft = self.position
player = Player(pos=(350, 220))
monster = Hunter(pos=(680, 400))
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
player.update()
monster.update(player)
screen.fill(pygame.Color('white'))
screen.blit(player.image, player.rect)
screen.blit(monster.image, monster.rect)
pygame.display.update()
``````

## Result

Just calculating the distance based on the hypotenuse is not enough. You must pass the coordinates of the enemy into the function and calculate the slope or pass the slope also into the function by value. Then you should move to one of 8 pixels around about your current position where the one you move to is most representative of the path of direction to the enemy. Essentially you move diagonally if the tan of the angle is less than 2 or great that 1/2 otherwise you move in a vertical or horizontal direction. You need to draw a 3x3 set of pixels to see what is actually going on if you can't visualise it.

Since we want to move along the hypotenuse we can use Pythagoras theorem. Here's a brief snippet the should give you the general idea.

I'll use `p.x, p.y` for the player's position and `e.x, e.y` for the enemy's position.

``````# Find the horizontal & vertical distances between player & enemy
dx = p.x - e.x
dy = p.y - e.y
#Get the hypotenuse
d = sqrt(dx*dx + dy*dy)
#Calculate the change to the enemy position
cx = speed * dx / d
cy = speed * dy / d
# Note that sqrt(cx*cx + cy*cy) == speed
# Update enemy position
e.x += cx
e.y += cy
``````

You need to add some extra code to this to make sure that `d` isn't zero, or you'll get a division by zero error, but that only happens when the enemy reaches the player, so I assume you want to do something special when that happens anyway. :)

I should mention that this technique works best if the positions are floats, not integer pixel coordinates.

POPULAR ONLINE

### pandas replace dataframe value by other columns value in the same row

I have this pandas dataframe

664

### Processing objects in order in docx

I want to process objects in the order they are written in a word documentObjects I have encountered are paragraphs, text in paragraphs, runs in paragraphs, text in runs, tables, and paragraphs in a table's cells

433

### Change package import path

I have the following situation:

311

### Asking the user for input until they give a valid response

I am writing a program that must accept input from the user

373