Enemy doesn't follow player (pygame) [duplicate]

Solution 1:

The major issue is, that you do the zombie movement calculations with integral data types. If the movement of a zombie is 1 pixel and the movement is diagonal, then the x and y component of the movement is < 1. Using an integral data type, this may results in 0 movement, because of truncating to int. Note the members of pygame.Rect are integral values.

You've to switch to floating point values to solve the issue. Use pygame.math.Vector2 to do the calculations.

Add a member pos of type Vector2 to the class Zombie which stores the floating point position of the zombie:

class Zombie(pygame.sprite.Sprite):

    def __init__(self2, color, pos, radius, width):
        super().__init__()
        self2.image = pygame.Surface([radius*2, radius*2])
        self2.image.fill(WHITE)
        self2.image.set_colorkey(WHITE)
        pygame.draw.circle(self2.image, color, [radius, radius], radius, width)
        self2.rect = self2.image.get_rect() 
        self2.speed = 1
        self2.pos = pygame.Vector2(pos[0], pos[1])

    # [...]

Add a new method draw to the class Zombie, which draws (blit) a zombie at the position pos:

class Zombie(pygame.sprite.Sprite):

    # [...]

    def draw(self2):
        self2.rect.center = (int(round(self2.pos.x)), int(round(self2.pos.y)))
        screen.blit(self2.image, self2.rect)  

Do the calculation of the movement of the zombie based on Vector2. Ensure that the distance between the player and the zombie is greater than 0 and that the zombie does not step over of the position of the player (min(len, self2.speed)):

class Zombie(pygame.sprite.Sprite):

    # [...]

    def move_towards_Char(self2, Char):
        deltaVec = pygame.Vector2(Char.rect.center) - self2.pos
        len = deltaVec.length()
        if len > 0:
            self2.pos += deltaVec/len * min(len, self2.speed)

Call the methods move_towards_Char and draw for each zombie, in the main loop of the application:

while carryOn:
    for event in pygame.event.get():
        if event.type==pygame.QUIT:
            carryOn=False
        elif event.type==pygame.KEYDOWN:
            if event.key==pygame.K_x:
                carryOn=False

    keys = pygame.key.get_pressed()
    if keys[pygame.K_a]:
        playerChar.moveLeftP(MOVE)
    if keys[pygame.K_d]:
        playerChar.moveRightP(MOVE)
    if keys[pygame.K_w]:
        playerChar.moveUpP(MOVE)
    if keys[pygame.K_s]:
        playerChar.moveDownP(MOVE)

    current_time = pygame.time.get_ticks()
    if current_time > next_zombie_time:
        next_zombie_time = current_time + 2000

        on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
        zombie_pos = (-1, -1)
        while not on_screen_rect.collidepoint(zombie_pos):
            dist  = random.randint(*zombie_dist)
            angle = random.random() * math.pi * 2 
            p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
            zombie_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))

        new_pos = (random.randrange(0, size[0]), random.randrange(0, size[1]))
        new_zombie = Zombie(RED, zombie_pos, zombie_rad, 0)
        zombie_list.append(new_zombie)

    # update all the positions of the zombies
    for zombie in zombie_list:
        zombie.move_towards_Char(playerChar)

    screen.fill(BGColor)
    screen.blit(playerChar.image,playerChar.rect)

    # draw all the zombies
    for zombie in zombie_list:
        zombie.draw()

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

Solution 2:

L{Zombie.move_towards_Char} is a self method. You need to create object of Zombie class passing the required args mentioned in L{Zombie.init}.

Something like below:

zm = Zombie(color=<color>, pos=<pos>, radius=<radius>, width=<width>)
zm.move_towards_Char(Char)