0

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()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Nope.X
  • 57
  • 6

2 Answers2

1

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)
devender22
  • 71
  • 3
  • do I just add it in the main loop or where? Also it says that these: < > are invalid syntaxes – Nope.X Feb 17 '19 at 18:04
  • You need to replace this call(where ever you're using in your code) >> Zombie.move_towards_Char(Char) with the one suggested by me. – devender22 Feb 17 '19 at 18:07
  • Tried checking the other link in the question. I couldn't find the complete code. Looking at your question, you should change this in the main loop. – devender22 Feb 17 '19 at 18:09
  • should I insert the color of the player in that or what should I do there where these: < > are? – Nope.X Feb 17 '19 at 18:12
  • Yes, you need to provide the custom values for these arguments as per your use cases. – devender22 Feb 17 '19 at 18:14
  • So @devender22 for color players color for position players position for radius players radius and for width players width or all of those for the zombie? – Nope.X Feb 17 '19 at 18:16
  • I am not sure as there is no link for the problem statement in the question. From the code it looks like you need to pass these arguments for the zombie. – devender22 Feb 17 '19 at 18:20
  • I also changed some major parts of the code now there are more than just 2 zombies and they steadily increase in the amount. – Nope.X Feb 17 '19 at 18:51
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)

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Where it says `if start and current_time > next_zombie_time` , what is `start` supposed to be? – rahsut Feb 01 '20 at 17:32
  • @rahsut You can ignore it. It is just a state, which indicates that the zombies start to follow the player. Added `start` for grabbing the gif. I've just removed it. – Rabbid76 Feb 01 '20 at 17:37