## [Pygame] moving enemy towards player

### [Pygame] moving enemy towards player

I am very noob when it comes to putting this with programming. The math i havent used since high school, and when i did learn it, i thought i would never need it, and low and behold, here is somewhere that i would use it daily. And now 8 years later after not using it, i am like, "hmm i think i remember that math"

Now i wish i listened in school more, lol.

I was attempting to get the enemy player to move towards the player at hte speed of 2 pixel per frame, while the player moves at 4 pixels per frame.

This was my method of getting hte new postition
Code: Select all
def pos_towards_player(self, player_rect):
'''get new coords towards player'''
c = math.sqrt((player_rect.x - self.rect[0]) ** 2 + (player_rect.y - self.rect[1]) ** 2)
x = (player_rect.x - self.rect[0]) / c
y = (player_rect.y - self.rect[1]) / c
return (x,y)

and
in the update()
Code: Select all
new_pos = self.enemy.pos_towards_player(self.player.rect)
self.enemy.rect = (self.player.rect.x + new_pos[0] * self.enemy.speed, self.player.rect.y + new_pos[1] * self.enemy.speed)

full code:
Code: Select all
import pygame
import sys
import math

def image_from_url(url):
try:
from urllib2 import urlopen
from cStringIO import StringIO as inout
except ImportError:
from urllib.request import urlopen
from io import BytesIO as inout
myurl = urlopen(url)

class Projectile:
limit = 3
def __init__(self, loc):
self.surf = pygame.Surface((5,10)).convert()
self.surf.set_colorkey((255,0,255))
self.surf.fill((255,255,255))
self.rect = self.surf.get_rect()
self.rect.center = loc
self.speed = 8

class Enemy:
def __init__(self):
self.orig_image = pygame.transform.scale(self.orig_image, (40,80))
self.orig_image.set_colorkey((255,0,255))
self.image = pygame.transform.rotate(self.orig_image, 180)
self.rect = self.image.get_rect()
self.rect.topleft = (100,0)
self.bullets = []
self.speed = 2

def pos_towards_player(self, player_rect):
'''get new coords towards player'''
c = math.sqrt((player_rect.x - self.rect[0]) ** 2 + (player_rect.y - self.rect[1]) ** 2)
x = (player_rect.x - self.rect[0]) / c
y = (player_rect.y - self.rect[1]) / c
return (x,y)
#(dx, dy) = ((xp - xz)/math.sqrt((xp - xz) ** 2 + (yp - yz) ** 2),
#            (yp - yz)/math.sqrt((xp - xz) ** 2 + (yp - yz) ** 2))

class Player:
def __init__(self):
self.speed = 4

self.orig_image = pygame.transform.scale(self.orig_image, (40,80))
self.orig_image.set_colorkey((255,0,255))

self.image = pygame.transform.rotate(self.orig_image, 180)
self.rect = self.image.get_rect()

self.bullets = []

def move(self, x, y):
self.rect[0] += x * self.speed
self.rect[1] += y * self.speed

class Control:
def __init__(self):
self.screensize = (800,600)
self.screen = pygame.display.set_mode(self.screensize)
self.screenrect = self.screen.get_rect()
self.clock = pygame.time.Clock()
self.player = Player()
self.enemy = Enemy()

self.mainloop()

def mainloop(self):
run = True
while run:
self.screen.fill((0,0,0))

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if len(self.player.bullets) <= Projectile.limit:
self.player.bullets.append(Projectile(self.player.rect.center))

self.update()
pygame.display.flip()
self.clock.tick(60)

def update(self):
self.keys = pygame.key.get_pressed()

if self.player.bullets:
for obj in self.player.bullets:
self.screen.blit(obj.surf, obj.rect)
obj.rect[1] -= obj.speed

#did obj move off screen
if obj.rect[1] < 0:
self.player.bullets.remove(obj)
#did obj hit enemy rect
if obj.rect.colliderect(self.enemy.rect):
offset_x =  self.enemy.rect.x - obj.rect.x
offset_y =  self.enemy.rect.y - obj.rect.y
#did object hit enemy mask
self.player.bullets.remove(obj)
self.enemy.die = True

new_pos = self.enemy.pos_towards_player(self.player.rect)
self.enemy.rect = (self.player.rect.x + new_pos[0] * self.enemy.speed, self.player.rect.y + new_pos[1] * self.enemy.speed)

if self.keys[pygame.K_UP]:
self.player.move(0,-1)
if self.keys[pygame.K_DOWN]:
self.player.move(0,1)
if self.keys[pygame.K_RIGHT]:
self.player.move(1,0)
if self.keys[pygame.K_LEFT]:
self.player.move(-1,0)

self.player.rect.clamp_ip(self.screenrect) #make sure rect does not go outside of screen
self.screen.blit(self.enemy.image, self.enemy.rect)
self.screen.blit(self.player.image, self.player.rect)

app = Control()
pygame.quit();sys.exit()

instead, the enemy follows the player underneath, which will eventually end up in both dying, but for now just wanted the enemy to slowly follow the player around. I also sometimes get an random ZeroDivisionError upon
Code: Select all
x = (player_rect.x - self.rect[0]) / c
. Sometimes i get it and sometimes i dont, not sure why c would sometimes result in 0

and also by adding this method in the Control.update() i somehow messed up the player fire method, when enabled.

EDIT:OK disregard the fire method error mess up as i was reassigning the enemy.rect instead of the coords. so fixed that:
Code: Select all
new_pos = self.enemy.pos_towards_player(self.player.rect)
self.enemy.rect.x, self.enemy.rect.y = (self.player.rect.x + new_pos[0] * self.enemy.speed, self.player.rect.y + new_pos[1] * self.enemy.speed)
we will be moving to python-forum.io on October 1 2016
more details here

metulburr

Posts: 2244
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

### Re: [Pygame] moving enemy towards player

Ok i figured it out, just a typo. put player.x and y instead of enemy.x and y,

and now the zerodivvisionerror makes sense if the enemy is exactly underneath the player as it woiuld result in 0, which i didnt htink of because i was later not even going to let them overlap at all. So i just put a try, except for that just for now, but will probably remove it after adding hte code to remove them both upon dying as they never wil get to overlap like that.

This is the final result of fixing:
Code: Select all
import pygame
import sys
import math

def image_from_url(url):
try:
from urllib2 import urlopen
from cStringIO import StringIO as inout
except ImportError:
from urllib.request import urlopen
from io import BytesIO as inout
myurl = urlopen(url)

class Projectile:
limit = 3
def __init__(self, loc):
self.surf = pygame.Surface((5,10)).convert()
self.surf.set_colorkey((255,0,255))
self.surf.fill((255,255,255))
self.rect = self.surf.get_rect()
self.rect.center = loc
self.speed = 8

class Enemy:
def __init__(self):
self.orig_image = pygame.transform.scale(self.orig_image, (40,80))
self.orig_image.set_colorkey((255,0,255))
self.image = pygame.transform.rotate(self.orig_image, 180)
self.rect = self.image.get_rect()
self.rect.topleft = (100,0)
self.bullets = []
self.speed = 2

def pos_towards_player(self, player_rect):
'''get new coords towards player'''
c = math.sqrt((player_rect.x - self.rect[0]) ** 2 + (player_rect.y - self.rect[1]) ** 2)
try:
x = (player_rect.x - self.rect[0]) / c
y = (player_rect.y - self.rect[1]) / c
except ZeroDivisionError:
return (self.rect.x, self.rect.y)
return (x,y)
#(dx, dy) = ((xp - xz)/math.sqrt((xp - xz) ** 2 + (yp - yz) ** 2),
#            (yp - yz)/math.sqrt((xp - xz) ** 2 + (yp - yz) ** 2))

class Player:
def __init__(self):
self.speed = 4

self.orig_image = pygame.transform.scale(self.orig_image, (40,80))
self.orig_image.set_colorkey((255,0,255))

self.image = pygame.transform.rotate(self.orig_image, 180)
self.rect = self.image.get_rect()

self.bullets = []

def move(self, x, y):
self.rect[0] += x * self.speed
self.rect[1] += y * self.speed

class Control:
def __init__(self):
self.screensize = (800,600)
self.screen = pygame.display.set_mode(self.screensize)
self.screenrect = self.screen.get_rect()
self.clock = pygame.time.Clock()
self.player = Player()
self.enemy = Enemy()

self.mainloop()

def mainloop(self):
run = True
while run:
self.screen.fill((0,0,0))

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if len(self.player.bullets) <= Projectile.limit:
self.player.bullets.append(Projectile(self.player.rect.center))

self.update()
pygame.display.flip()
self.clock.tick(60)

def update(self):
self.keys = pygame.key.get_pressed()

if self.player.bullets:
for obj in self.player.bullets:
self.screen.blit(obj.surf, obj.rect)
obj.rect[1] -= obj.speed

#did obj move off screen
if obj.rect[1] < 0:
self.player.bullets.remove(obj)
#did obj hit enemy rect
if obj.rect.colliderect(self.enemy.rect):
offset_x =  self.enemy.rect.x - obj.rect.x
offset_y =  self.enemy.rect.y - obj.rect.y
#did object hit enemy mask
self.player.bullets.remove(obj)
self.enemy.die = True

new_pos = self.enemy.pos_towards_player(self.player.rect)
self.enemy.rect.x, self.enemy.rect.y = (self.enemy.rect.x + new_pos[0] * self.enemy.speed, self.enemy.rect.y + new_pos[1] * self.enemy.speed)
if self.keys[pygame.K_UP]:
self.player.move(0,-1)
if self.keys[pygame.K_DOWN]:
self.player.move(0,1)
if self.keys[pygame.K_RIGHT]:
self.player.move(1,0)
if self.keys[pygame.K_LEFT]:
self.player.move(-1,0)

self.player.rect.clamp_ip(self.screenrect) #make sure rect does not go outside of screen
self.screen.blit(self.enemy.image, self.enemy.rect)
self.screen.blit(self.player.image, self.player.rect)

app = Control()
pygame.quit();sys.exit()

EDIT:
OK now after playing with this for awhile, i see the enemy jumps around after overlapping the player at times. Of course i wouldnt have to worry about this if they were to not exist if in the event they were to overlap. SO maybe i should even worry about it.
we will be moving to python-forum.io on October 1 2016
more details here

metulburr

Posts: 2244
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

### Re: [Pygame] moving enemy towards player

with the code:
Code: Select all
import pygame
import sys
import math
import random

def image_from_url(url):
try:
from urllib2 import urlopen
from cStringIO import StringIO as inout
except ImportError:
from urllib.request import urlopen
from io import BytesIO as inout
myurl = urlopen(url)

class Projectile:
limit = 3
def __init__(self, loc):
self.surf = pygame.Surface((5,10)).convert()
self.surf.set_colorkey((255,0,255))
self.surf.fill((255,255,255))
self.rect = self.surf.get_rect()
self.rect.center = loc
self.speed = 8

class Enemy:
count = 3
def __init__(self, start_loc, orig_image):
self.orig_image = orig_image
self.orig_image = pygame.transform.scale(self.orig_image, (40,80))
self.orig_image.set_colorkey((255,0,255))
self.image = pygame.transform.rotate(self.orig_image, 180)
self.rect = self.image.get_rect()
self.rect.center = start_loc
self.bullets = []
self.speed = 2
self.is_hit = False

def pos_towards_player(self, player_rect):
'''get new coords towards player'''
c = math.sqrt((player_rect.x - self.rect[0]) ** 2 + (player_rect.y - self.rect[1]) ** 2)
try:
x = (player_rect.x - self.rect[0]) / c
y = (player_rect.y - self.rect[1]) / c
except ZeroDivisionError:
return (self.rect.x, self.rect.y)
return (x,y)

def destroy(self):
...

def update(self, player_rect):
if not self.is_hit:
#move enemy towards player
new_pos = self.pos_towards_player(player_rect)
self.rect.x, self.rect.y = (self.rect.x + new_pos[0] * self.speed, self.rect.y + new_pos[1] * self.speed)

class Player:
def __init__(self, start_loc):
self.speed = 4

self.orig_image = pygame.transform.scale(self.orig_image, (40,80))
self.orig_image.set_colorkey((255,0,255))

self.image = pygame.transform.rotate(self.orig_image, 180)
self.rect = self.image.get_rect()
self.rect.center = start_loc

self.bullets = []

def move(self, x, y):
self.rect[0] += x * self.speed
self.rect[1] += y * self.speed

class Control:
def __init__(self):
self.screensize = (800,600)
self.screen = pygame.display.set_mode(self.screensize)
self.screenrect = self.screen.get_rect()
self.clock = pygame.time.Clock()
self.player = Player((0,600))
#self.enemy = Enemy((800,0))

self.enemies = []

self.mainloop()

def mainloop(self):
run = True
while run:
self.screen.fill((0,0,0))

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if len(self.player.bullets) <= Projectile.limit:
self.player.bullets.append(Projectile(self.player.rect.center))

self.update()
pygame.display.flip()
self.clock.tick(60)

def update(self):
self.keys = pygame.key.get_pressed()

#for obj in self.enemies:
#    obj.update(self.player.rect)

#make new enemies
#print(len(self.enemies))

if len(self.enemies) <= Enemy.count:
self.enemies.append(Enemy((random.randint(1,800),-100), self.enemy_image))

if self.player.bullets:
for obj in self.player.bullets:
self.screen.blit(obj.surf, obj.rect)
obj.rect[1] -= obj.speed

#did obj move off screen
if obj.rect[1] < 0:
self.player.bullets.remove(obj)
#did obj hit enemy rect
for enem in self.enemies:
if obj.rect.colliderect(enem.rect):
offset_x =  enem.rect.x - obj.rect.x
offset_y =  enem.rect.y - obj.rect.y
#did object hit enemy mask
self.player.bullets.remove(obj)
self.enemies.remove(enem)
enem.is_hit = True

if self.keys[pygame.K_UP]:
self.player.move(0,-1)
if self.keys[pygame.K_DOWN]:
self.player.move(0,1)
if self.keys[pygame.K_RIGHT]:
self.player.move(1,0)
if self.keys[pygame.K_LEFT]:
self.player.move(-1,0)

self.player.rect.clamp_ip(self.screenrect) #make sure rect does not go outside of screen
for obj in self.enemies:
obj.update(self.player.rect)
if not obj.is_hit:
self.screen.blit(obj.image, obj.rect)
#if not self.enemy.is_hit:
#   self.screen.blit(self.enemy.image, self.enemy.rect)
self.screen.blit(self.player.image, self.player.rect)

app = Control()
pygame.quit();sys.exit()

sometime i get an ValueERror for the obj not being in the list when i attempt to remove it.
Code: Select all
Traceback (most recent call last):
File "forum3.py", line 172, in <module>
app = Control()
File "forum3.py", line 100, in __init__
self.mainloop()
File "forum3.py", line 115, in mainloop
self.update()
File "forum3.py", line 148, in update
self.player.bullets.remove(obj)
ValueError: list.remove(x): x not in list

to recreate this, if you swirl around the enemies and let them overlap eachother, then move down and shoot once, this error is caused, but that is the only way that i can recreate this error
we will be moving to python-forum.io on October 1 2016
more details here

metulburr

Posts: 2244
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

### Re: [Pygame] moving enemy towards player

Try iterating through a copy of your enemy list instead of the actual list here:
Code: Select all
for enem in self.enemies:
if obj.rect.colliderect(enem.rect):
offset_x =  enem.rect.x - obj.rect.x
offset_y =  enem.rect.y - obj.rect.y
#did object hit enemy mask
self.player.bullets.remove(obj)
self.enemies.remove(enem)
enem.is_hit = True
Changing the size of a list while iterating over it is never a good idea. Not sure if this is the problem though.

-Mek

Edit: Ok that was one problem... but the main problem here is that your bullets are hitting multiple enemies at once. Therefore you try to remove the same bullet multiple times. And thus your object not in list.

This fixes it for the moment:
Code: Select all
if self.player.bullets:
for obj in self.player.bullets[:]:
self.screen.blit(obj.surf, obj.rect)
obj.rect[1] -= obj.speed

#did obj move off screen
if obj.rect[1] < 0:
self.player.bullets.remove(obj)
break

#did obj hit enemy rect
for enem in self.enemies[:]:
if obj.rect.colliderect(enem.rect):
offset_x =  enem.rect.x - obj.rect.x
offset_y =  enem.rect.y - obj.rect.y
#did object hit enemy mask
self.player.bullets.remove(obj)
self.enemies.remove(enem)
enem.is_hit = True
break
New Users, Read This
• Use code tags when posting code.
• Include any errors with your post (in code tags).
• Describe your problem; not your chosen solution.
• Make examples the minimum length to demonstrate your issue.

Mekire

Posts: 1711
Joined: Thu Feb 07, 2013 11:33 pm
Location: Tucson, Arizona

### Re: [Pygame] moving enemy towards player

ah ok, thanks mekire. I am thinking so much about pygame and other things in relation that i forget about python basics.
we will be moving to python-forum.io on October 1 2016
more details here

metulburr

Posts: 2244
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY