Pygame – Getting enemy sprites to move towards player

Question:

As of currently the enemy sprites just spawn on the axis, and “move” with the player which is actually a scrolling background, and I’d like for the enemies to move only along the X axis so it doesn’t destroy the immersion.

I’d also like for the sprites to spawn “Off the map” and whenever the map scrolls towards them they move towards the players set X axis? I think that would keep things simple, but isn’t really the question at hand right now.

The current code I was trying to get working for the movement was :

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
        self.rect.x += dx * self.speed
        self.rect.y += dy * self.speed

But it wouldn’t work, this is with importing the math module.
(I know I don’t need the y movements just wanted to get it working first)

Here is the rest of the code —

Zombie.py:

import pygame
from pygame.locals import *
import random
import math

class ZombieEnemy(pygame.sprite.Sprite):
    def __init__(self, x=300, y=360):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('images/zombie.png')
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)

all_zombies = pygame.sprite.Group()


for i in range( 50 ):
    new_x = random.randrange( 0, 10000)       # random x-position
    # new_y = random.randrange( 0, )      # random y-position
    all_zombies.add(ZombieEnemy(new_x))         # create, and add to group

I wasn’t sure if I should create a whole new class for enemy movement, or what my partner is currently working on the Player, and getting the sprite animations and movement working right.

But here is the main code I have as of now:

import pygame
from Zombie import *
import math
from pygame.locals import *
pygame.init()
​
win = pygame.display.set_mode((900,567))
​
pygame.display.set_caption("Power Rangers ZOMBIES")
​
walkRight = [pygame.image.load('images/walk1.png'), pygame.image.load('images/walk2.png'), pygame.image.load('images/walk3.png'), pygame.image.load('images/walk4.png'), pygame.image.load('images/walk5.png'), pygame.image.load('images/walk6.png')]
walkLeft = [pygame.image.load('images/leftwalk2.png'), pygame.image.load('images/leftwalk3.png'), pygame.image.load('images/leftwalk4.png'), pygame.image.load('images/leftwalk5.png'), pygame.image.load('images/leftwalk6.png'), pygame.image.load('images/leftwalk7.png')]
bg = pygame.image.load('images/background.png')
char = pygame.image.load('images/standingstill.png')
clock = pygame.time.Clock()
​
class Player(pygame.sprite.Sprite):
    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.vel = 0
        self.isJump = False
        self.left = False
        self.right = False
        self.walkCount = 0
        self.jumpCount = 10
​
    def draw(self, win):
        if self.walkCount + 1 >= 18:
            self.walkCount = 0
​
        if self.left:
            win.blit(walkLeft[self.walkCount//3], (self.x,self.y))
            self.walkCount += 1
        elif self.right:
            win.blit(walkRight[self.walkCount//3], (self.x,self.y))
            self.walkCount +=1
        else:
            win.blit(char, (self.x,self.y))
​
class Background(pygame.sprite.Sprite):
    def __init__(self, image_file, location):
        pygame.sprite.Sprite.__init__(self)  #call Sprite initializer
        self.image = pygame.image.load('images/background.png')
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = location
​
BackGround = Background('images/background.png', [0,0])
​
# def redrawGameWindow():
#     win.blit(bg, (0,0))
#     man.draw(win)

#     pygame.display.update()
​
#mainloop
man = Player(100, 340, 40, 60)
run = True
while run:
    clock.tick(27)
​
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
​
    keys = pygame.key.get_pressed()
​
    if keys[pygame.K_LEFT] and man.x > man.vel:
        BackGround.rect.left = BackGround.rect.left + int(10)
        man.x -= man.vel
        man.left = True
        man.right = False
    elif keys[pygame.K_RIGHT]: #and man.x < 500 - man.width - man.vel:
        BackGround.rect.left = BackGround.rect.left - int(10)
        man.x += man.vel
        man.right = True
        man.left = False
    else:
        man.right = False
        man.left = False
        man.walkCount = 0

    if not(man.isJump):
        if keys[pygame.K_SPACE]:
            man.isJump = True
            man.right = False
            man.left = False
            man.walkCount = 0
    else:
        if man.jumpCount >= -10:
            neg = 1
            if man.jumpCount < 0:
                neg = -1
            man.y -= (man.jumpCount ** 2) * 0.5 * neg
            man.jumpCount -= 1
        else:
            man.isJump = False
            man.jumpCount = 10

    # redrawGameWindow()
    for zombie in all_zombies:
        zombie.move_towards_player(Player)
    all_zombie.update()
    win.blit(BackGround.image, BackGround.rect)
    man.draw(win)
    pygame.display.flip()
    all_zombies.draw(screen)
​
pygame.quit()
Asked By: Blue

||

Answers:

I can’t run code but I see two problems

1 – move_towards_player has to be inside class ZombieEnemy

# --- classes ---

class ZombieEnemy(pygame.sprite.Sprite):

    def __init__(self, x=300, y=360):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('images/zombie.png')
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)

    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
        self.rect.x += dx * self.speed
        self.rect.y += dy * self.speed 

 # --- other ---

 all_zombies = pygame.sprite.Group()

2 – you have to use it in main loop

    for zombie in all_zombies:
        zombie.move_towards_player(player)

in main loop

while running:

    # --- events ---

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    # If keystroke is pressed check right, left.
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_LEFT:
            #playerX_change = -2.0
            BackGround.rect.left = BackGround.rect.left + 2.5
        if event.key == pygame.K_RIGHT:
            #playerX_change = 2.0
            BackGround.rect.left = BackGround.rect.left - 2.5
    # if event.type == pygame.KEYUP:
    #     if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
    #         BackGround.rect.left = 0

    # --- updates/moves ---

    playerX += playerX_change

    for zombie in all_zombies:
        zombie.move_towards_player(player)
    all_zombies.update()

    # --- draws ---

    screen.blit(BackGround.image, BackGround.rect)
    player(playerX,playerY)
    all_zombies.draw(screen)
    pygame.display.flip()

But here I see other problem – your function expects player as class instance with self.rect inside (similar to ZombieEnemy) but you keep player as separated variables playerImg, playerX, playerY, playerX_change

So you have to create class Player or you have to use playerx, playery in move_towards_player instead of player.rect.x, player.rect.y

Answered By: furas
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.