How to make an enemy follow the player in pygame?
Question:
I made part of a game. It runs well but I would like to add enemies in my game. So far I add the image of the enemies in pygame sprites.
But how do I make the enemies follow the player? I tried do this but it just made the image of the enemy direct to the player:
def moveEnemy(self):
enemies.rect.x = player.rect.x
enemies.rect.y = player.rect.y
all_sprites_list.add(enemies)
enemies_list.add(enemies)
I thought this would make the image of the enemy follow the player. Instead it just overlaped the player’s image.
I read though many pygame sprites examples but the examples saids to replace the enemies.rect.x = player.rect.x
with enemies.rect.x = -5
or something around that. I also tried this but it just move the image up instead of following the player.
Do I have to formulate an equation? If so I do not know how to.
How do I make the enemy move but as well make it follow the player? Can someone help me solve this problem?
Any help would be appreciated.
Answers:
At a high level you need to work out the vector from the enemy to the player. The x
and y
components of the direction vector would look like:
enemies.rect.x - player.rect.x
enemies.rect.y - player.rect.y
You then add a multiple of the direction to the enemy position to make it move towards the player.
However: You’ll notice that this vector will have a large magnitude when the player and enemy are far apart and a small magnitude when they are closer together. So to avoid having the enemy move at super-fast speeds, you would could normalise the direction vector and multiply by a speed s
to keep it under control.
Here, px
& py
are the position (globals) of the object the enemy is following/chasing.
class Enemy(object):
def __init__(self,x,y): # initial position
self.x = x
self.y = y
def move(self, speed=5): # chase movement
# Movement along x direction
if self.x > px:
self.x -= speed
elif self.x < px:
self.x += speed
# Movement along y direction
if self.y < py:
self.y += speed
elif self.y > py:
self.y -= speed
If you want, you can customize the speed along x & y directions too.
You need to reduce the distance between the enemy and the player by changing the enemy’s position. This can be done by finding the difference between their positions and then using that vector to compute a normalized (unit length) direction vector. With that, change the enemy’s position by adding its speed times the direction vector to it.
One way of doing this would be by adding a method like the following to your Enemy
class. The mathmatical operations can be done using the built-in math
module or the pygame.math
module. The latter also has support for a 2D Vector2
class, so it would probably be the better one to actually use.
import math
import pygame
from pygame.locals import *
class Enemy(object):
...
def move_towards_player(self, player):
# Find direction vector (dx, dy) between enemy and player.
dx, dy = player.rect.x - self.rect.x, player.rect.y - self.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist # Normalize.
# Move along this normalized vector towards the player at current speed.
self.rect.x += dx * self.speed
self.rect.y += dy * self.speed
# Same thing using only pygame utilities
def move_towards_player2(self, player):
# Find direction vector (dx, dy) between enemy and player.
dirvect = pygame.math.Vector2(player.rect.x - self.rect.x,
player.rect.y - self.rect.y)
dirvect.normalize()
# Move along this normalized vector towards the player at current speed.
dirvect.scale_to_length(self.speed)
self.rect.move_ip(dirvect)
You will need to add checks to determine whether the enemy object will overshoot and thereby hit the player along the way if it moves this distance, and react accordingly. Hint: A collision will occur whenever the amount to be moved—the length of the speed vector, i.e. the object’s speed—is greater or equal to the distance between them.
A very simple method is
if x_player > x_enemy:
x_enemy = x_enemy + 0.1
if x_player < x_enemy:
x_enemy = x_enemy - 0.1
if y_player > y_enemy:
y_enemy = y_enemy + 0.1
if y_player < y_enemy:
y_enemy = y_enemy - 0.1
Obviously, change the variables to the variables you are using and change the value you are changing the x and y to change the speed at which the enemy moves.
Getting an enemy to follow a player takes some math. I suggest to use pygame.math.Vector2
for the computation.
Calculate the Euclidean distance between the enemy (follower_x
, follower_y
) to the player (player_x
, player_y
) and the unit direction vector from the enemy to the palyer. The Unit Vector can be computed by dividing the direction vector by the distance or by normalizing (normalize()
) the direction vector:
target_vector = Vector2(player_x, player_y)
follower_vector = Vector2(follower_x, follower_y)
distance = follower_vector.distance_to(target_vector)
direction_vector = target_vector - follower_vector
if distance > 0:
direction_vector /= distance
Define an exact step_distance
and move in the direction of the sprite along the direction vector from the enemy to the player
if distance > 0:
new_follower_vector = follower_vector + direction_vector * step_distance.
Define a minimum_distance
and a maximum_distance
between the player and the enemy. The minimum distance can be 0 and the maximum distance can be very large:
minimum_distance = 0
maximum_distance = 10000
Use this minimum and maximum to restrict the movement of the enemy:
min_step = max(0, distance - maximum_distance)
max_step = distance - minimum_distance
The enemy’s movement speed can be constant
VELOCITY = 5
step_distance = min(max_step, max(min_step, VELOCITY))
or smoothed with an interpolation factor:
LERP_FACTOR = 0.05
step_distance = min_step + (max_step - min_step) * LERP_FACTOR
Minimal example:
repl.it/@Rabbid76/PyGame-FollowMouseSmoothly
import pygame
VELOCITY = 5
LERP_FACTOR = 0.05
minimum_distance = 25
maximum_distance = 100
def FollowMe(pops, fpos):
target_vector = pygame.math.Vector2(*pops)
follower_vector = pygame.math.Vector2(*fpos)
new_follower_vector = pygame.math.Vector2(*fpos)
distance = follower_vector.distance_to(target_vector)
if distance > minimum_distance:
direction_vector = (target_vector - follower_vector) / distance
min_step = max(0, distance - maximum_distance)
max_step = distance - minimum_distance
#step_distance = min(max_step, max(min_step, VELOCITY))
step_distance = min_step + (max_step - min_step) * LERP_FACTOR
new_follower_vector = follower_vector + direction_vector * step_distance
return (new_follower_vector.x, new_follower_vector.y)
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
follower = (100, 100)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
player = pygame.mouse.get_pos()
follower = FollowMe(player, follower)
window.fill(0)
pygame.draw.circle(window, (0, 0, 255), player, 10)
pygame.draw.circle(window, (255, 0, 0), (round(follower[0]), round(follower[1])), 10)
pygame.display.flip()
pygame.quit()
exit()
I made part of a game. It runs well but I would like to add enemies in my game. So far I add the image of the enemies in pygame sprites.
But how do I make the enemies follow the player? I tried do this but it just made the image of the enemy direct to the player:
def moveEnemy(self):
enemies.rect.x = player.rect.x
enemies.rect.y = player.rect.y
all_sprites_list.add(enemies)
enemies_list.add(enemies)
I thought this would make the image of the enemy follow the player. Instead it just overlaped the player’s image.
I read though many pygame sprites examples but the examples saids to replace the enemies.rect.x = player.rect.x
with enemies.rect.x = -5
or something around that. I also tried this but it just move the image up instead of following the player.
Do I have to formulate an equation? If so I do not know how to.
How do I make the enemy move but as well make it follow the player? Can someone help me solve this problem?
Any help would be appreciated.
At a high level you need to work out the vector from the enemy to the player. The x
and y
components of the direction vector would look like:
enemies.rect.x - player.rect.x
enemies.rect.y - player.rect.y
You then add a multiple of the direction to the enemy position to make it move towards the player.
However: You’ll notice that this vector will have a large magnitude when the player and enemy are far apart and a small magnitude when they are closer together. So to avoid having the enemy move at super-fast speeds, you would could normalise the direction vector and multiply by a speed s
to keep it under control.
Here, px
& py
are the position (globals) of the object the enemy is following/chasing.
class Enemy(object):
def __init__(self,x,y): # initial position
self.x = x
self.y = y
def move(self, speed=5): # chase movement
# Movement along x direction
if self.x > px:
self.x -= speed
elif self.x < px:
self.x += speed
# Movement along y direction
if self.y < py:
self.y += speed
elif self.y > py:
self.y -= speed
If you want, you can customize the speed along x & y directions too.
You need to reduce the distance between the enemy and the player by changing the enemy’s position. This can be done by finding the difference between their positions and then using that vector to compute a normalized (unit length) direction vector. With that, change the enemy’s position by adding its speed times the direction vector to it.
One way of doing this would be by adding a method like the following to your Enemy
class. The mathmatical operations can be done using the built-in math
module or the pygame.math
module. The latter also has support for a 2D Vector2
class, so it would probably be the better one to actually use.
import math
import pygame
from pygame.locals import *
class Enemy(object):
...
def move_towards_player(self, player):
# Find direction vector (dx, dy) between enemy and player.
dx, dy = player.rect.x - self.rect.x, player.rect.y - self.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist # Normalize.
# Move along this normalized vector towards the player at current speed.
self.rect.x += dx * self.speed
self.rect.y += dy * self.speed
# Same thing using only pygame utilities
def move_towards_player2(self, player):
# Find direction vector (dx, dy) between enemy and player.
dirvect = pygame.math.Vector2(player.rect.x - self.rect.x,
player.rect.y - self.rect.y)
dirvect.normalize()
# Move along this normalized vector towards the player at current speed.
dirvect.scale_to_length(self.speed)
self.rect.move_ip(dirvect)
You will need to add checks to determine whether the enemy object will overshoot and thereby hit the player along the way if it moves this distance, and react accordingly. Hint: A collision will occur whenever the amount to be moved—the length of the speed vector, i.e. the object’s speed—is greater or equal to the distance between them.
A very simple method is
if x_player > x_enemy:
x_enemy = x_enemy + 0.1
if x_player < x_enemy:
x_enemy = x_enemy - 0.1
if y_player > y_enemy:
y_enemy = y_enemy + 0.1
if y_player < y_enemy:
y_enemy = y_enemy - 0.1
Obviously, change the variables to the variables you are using and change the value you are changing the x and y to change the speed at which the enemy moves.
Getting an enemy to follow a player takes some math. I suggest to use pygame.math.Vector2
for the computation.
Calculate the Euclidean distance between the enemy (follower_x
, follower_y
) to the player (player_x
, player_y
) and the unit direction vector from the enemy to the palyer. The Unit Vector can be computed by dividing the direction vector by the distance or by normalizing (normalize()
) the direction vector:
target_vector = Vector2(player_x, player_y)
follower_vector = Vector2(follower_x, follower_y)
distance = follower_vector.distance_to(target_vector)
direction_vector = target_vector - follower_vector
if distance > 0:
direction_vector /= distance
Define an exact step_distance
and move in the direction of the sprite along the direction vector from the enemy to the player
if distance > 0:
new_follower_vector = follower_vector + direction_vector * step_distance.
Define a minimum_distance
and a maximum_distance
between the player and the enemy. The minimum distance can be 0 and the maximum distance can be very large:
minimum_distance = 0
maximum_distance = 10000
Use this minimum and maximum to restrict the movement of the enemy:
min_step = max(0, distance - maximum_distance)
max_step = distance - minimum_distance
The enemy’s movement speed can be constant
VELOCITY = 5
step_distance = min(max_step, max(min_step, VELOCITY))
or smoothed with an interpolation factor:
LERP_FACTOR = 0.05
step_distance = min_step + (max_step - min_step) * LERP_FACTOR
Minimal example:
repl.it/@Rabbid76/PyGame-FollowMouseSmoothly
import pygame
VELOCITY = 5
LERP_FACTOR = 0.05
minimum_distance = 25
maximum_distance = 100
def FollowMe(pops, fpos):
target_vector = pygame.math.Vector2(*pops)
follower_vector = pygame.math.Vector2(*fpos)
new_follower_vector = pygame.math.Vector2(*fpos)
distance = follower_vector.distance_to(target_vector)
if distance > minimum_distance:
direction_vector = (target_vector - follower_vector) / distance
min_step = max(0, distance - maximum_distance)
max_step = distance - minimum_distance
#step_distance = min(max_step, max(min_step, VELOCITY))
step_distance = min_step + (max_step - min_step) * LERP_FACTOR
new_follower_vector = follower_vector + direction_vector * step_distance
return (new_follower_vector.x, new_follower_vector.y)
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
follower = (100, 100)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
player = pygame.mouse.get_pos()
follower = FollowMe(player, follower)
window.fill(0)
pygame.draw.circle(window, (0, 0, 255), player, 10)
pygame.draw.circle(window, (255, 0, 0), (round(follower[0]), round(follower[1])), 10)
pygame.display.flip()
pygame.quit()
exit()