## [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 pygameimport sysimport mathdef 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)    return inout(myurl.read())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.mask = pygame.mask.from_surface(self.surf)        self.speed = 8class Enemy:    def __init__(self):        link = 'http://i1297.photobucket.com/albums/ag23/metulburr/spaceship_enemy3_zpscfeb13bf.png'        self.orig_image = pygame.image.load(image_from_url(link)).convert()        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.mask = pygame.mask.from_surface(self.image)        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        link = 'http://i1297.photobucket.com/albums/ag23/metulburr/spaceship2_zps095c332a.png'        self.orig_image = pygame.image.load(image_from_url(link)).convert()        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.mask = pygame.mask.from_surface(self.image)        self.rect = self.image.get_rect()        self.bullets = []    def move(self, x, y):        self.rect[0] += x * self.speed        self.rect[1] += y * self.speedclass 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                    if obj.mask.overlap(self.enemy.mask, (offset_x, offset_y)):                        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 pygameimport sysimport mathdef 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)    return inout(myurl.read())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.mask = pygame.mask.from_surface(self.surf)        self.speed = 8class Enemy:    def __init__(self):        link = 'http://i1297.photobucket.com/albums/ag23/metulburr/spaceship_enemy3_zpscfeb13bf.png'        self.orig_image = pygame.image.load(image_from_url(link)).convert()        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.mask = pygame.mask.from_surface(self.image)        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        link = 'http://i1297.photobucket.com/albums/ag23/metulburr/spaceship2_zps095c332a.png'        self.orig_image = pygame.image.load(image_from_url(link)).convert()        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.mask = pygame.mask.from_surface(self.image)        self.rect = self.image.get_rect()        self.bullets = []    def move(self, x, y):        self.rect[0] += x * self.speed        self.rect[1] += y * self.speedclass 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                    if obj.mask.overlap(self.enemy.mask, (offset_x, offset_y)):                        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 pygameimport sysimport mathimport randomdef 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)    return inout(myurl.read())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.mask = pygame.mask.from_surface(self.surf)        self.speed = 8class Enemy:    count = 3    def __init__(self, start_loc, orig_image):        self.orig_image = orig_image        #link = 'http://i1297.photobucket.com/albums/ag23/metulburr/spaceship_enemy3_zpscfeb13bf.png'        #self.orig_image = pygame.image.load(image_from_url(link)).convert()        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.mask = pygame.mask.from_surface(self.image)        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        link = 'http://i1297.photobucket.com/albums/ag23/metulburr/spaceship2_zps095c332a.png'        self.orig_image = pygame.image.load(image_from_url(link)).convert()        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.mask = pygame.mask.from_surface(self.image)        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.speedclass 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))        link = 'http://i1297.photobucket.com/albums/ag23/metulburr/spaceship_enemy3_zpscfeb13bf.png'        self.enemy_image = pygame.image.load(image_from_url(link)).convert()                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                        if obj.mask.overlap(enem.mask, (offset_x, offset_y)):                            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        if obj.mask.overlap(enem.mask, (offset_x, offset_y)):            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                if obj.mask.overlap(enem.mask, (offset_x, offset_y)):                    self.player.bullets.remove(obj)                    self.enemies.remove(enem)                    enem.is_hit = True                    break`
• Use code tags when posting code.
• Include any errors with your post (in code tags).
• 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

### Who is online

Users browsing this forum: No registered users and 1 guest