Enemy doesn't follow player (pygame)

2019-08-16 17:06发布

So I followed the answers in another question asked on StackOverflow but it seems that I have missed something. I went ahead after reading the answer and copied the code and adjusted it to my variables and class names.

The following is the error code that Idle gives me:

Traceback (most recent call last):
  File "D:\Programme (x86)\Python\Games\Zombie Game\Zombie Game_Test1.py", line 133, in <module>
  Zombie.move_towards_Char(Char)
TypeError: move_towards_Char() missing 1 required positional argument: 'Char'

This is where I looked: How to make an enemy follow the player in pygame?

import pygame
import turtle
import time
import math
import random
import sys
import os
pygame.init()

WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)

BGColor = (96,128,56)
ZColor = (225,0,0)
PColor = (0,0,255)

MOVE = 2.5

size = (1920, 1080)

screen = pygame.display.set_mode(size)
pygame.display.set_caption("Zombie Game")

class Char(pygame.sprite.Sprite):
    def __init__(self, color, pos, radius, width):
        super().__init__()
        self.image = pygame.Surface([radius*2, radius*2])
        self.image.fill(WHITE)
        self.image.set_colorkey(WHITE)
        pygame.draw.circle(self.image, color, [radius, radius], radius, width)
        self.rect = self.image.get_rect()

    def moveRightP(self, pixels):
        self.rect.x += pixels
        pass

    def moveLeftP(self, pixels):
        self.rect.x -= pixels
        pass

    def moveUpP(self, pixels):
        self.rect.y -= pixels
        pass

    def moveDownP(self, pixels):
        self.rect.y += pixels
        pass


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.rect.center = pos

    def move_towards_Char(self2, Char):
        dx, dy = self2.rect.x - Char.rect.x, self2.rect.y - Char.rect.y
        dist = math.hypot(dx, dy)
        dx, dy = dx / dist, dy / dist
        self2.rect.x += dx * self2.speed
        self2.rect.y += dy * self2.speed


    def moveRightZ(self2, pixels):
        self2.rect.x += pixels
        pass

    def moveLeftZ(self2, pixels):
        self2.rect.x -= pixels
        pass

    def moveUpZ(self2, pixels):
        self2.rect.y -= pixels
        pass

    def moveDownZ(self2, pixels):
        self2.rect.y += pixels
        pass


all_sprites_list = pygame.sprite.Group()

playerChar = Char(PColor, [0, 0], 15, 0)
playerChar.rect.x = 960
playerChar.rect.y = 505

all_sprites_list.add(playerChar)

carryOn = True
clock = pygame.time.Clock()

zombie_list = []
zombie_rad = 15   
zombie_dist = (200, 900)
next_zombie_time = pygame.time.get_ticks() + 10000

zombie_list = []
zombie_rad = 15   
zombie_dist = (200, 900)
next_zombie_time = 10000

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)

    screen.fill(BGColor)    
    screen.blit(playerChar.image,playerChar.rect)   
    for zombie in zombie_list:
        screen.blit(zombie.image,zombie.rect)      

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

标签: python pygame
2条回答
走好不送
2楼-- · 2019-08-16 17:23

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 start and 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)

查看更多
Emotional °昔
3楼-- · 2019-08-16 17:38

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)
查看更多
登录 后发表回答