SpaghettiCoder
SpaghettiCoder

Reputation: 91

Python pygame movement question about classes

I am trying to implement movement from my enemy class to follow the player class. Currently, the enemy class only moves in a straight line to the left. Here is some code to better understand my question.

Here is the player class

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 40))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.left = 25
        self.rect.bottom = HEIGHT / 2
        self.speedx = 0
        self.speedy = 0

Here is the enemy class

class Enemy(pygame.sprite.Sprite):
    def __init__(self2):
        pygame.sprite.Sprite.__init__(self2)
        self2.image = pygame.Surface((50, 40))
        self2.image.fill(RED)
        self2.rect = self2.image.get_rect()
        self2.rect.x = random.randrange(680, 750)
        self2.rect.y = random.randrange(HEIGHT - self2.rect.height)
        self2.speedx = random.randrange(-5, -3)
        self2.speedy = random.randrange(-5, 5)
        self2.min_dist = 200

    def update(self2):
        self2.speedy = 0
        self2.rect.x += self2.speedx
        self2.rect.y += self2.speedy
        if self2.rect.left < -25 or self2.rect.top < -25 or self2.rect.bottom > HEIGHT + 10:
            self2.rect.x = random.randrange(680, 750)
            self2.rect.y = random.randrange(HEIGHT - self2.rect.height)
            self2.speedx = random.randrange(-5, -3)

    def move_towards_Player(self2, Player):
        delta_x = Player.rect.x - self2.rect.x
        delta_y = Player.rect.y - self2.rect.y
        if abs(delta_x) <= self2.min_dist and abs(delta_y) <= self2.min_dist:
            enemy_move_x = abs(delta_x) > abs(delta_y)
        if abs(delta_x) > self2.speedx and abs(delta_x) > self2.speedx:
            enemy_move_x = random.random() < 0.5
        if enemy_move_x:
            self2.rect.x += min(delta_x, self2.speedx) if delta_x > 0 else max(delta_x, -self2.speedx)
        else:
            self2.rect.y += min(delta_y, self2.speedy) if delta_y > 0 else max(delta_y, -self2.speedy)
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
player = Player()
all_sprites.add(player)

for i in range(3):
    e = Enemy()
    all_sprites.add(e)
    enemies.add(e)

running = True
while running:

    clock.tick(FPS)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    all_sprites.update()
    #Draw render
    screen.fill(BLACK)
    all_sprites.draw(screen)
    pygame.display.flip()

pygame.quit()
quit()

Sorry for the large post in code just would like to get the best answer for this.

Question 1) This is probably a very simple question but, how come when the all_sprites.Group get updated in the loop why doesn't the move_towards player get updated?

Question 2) I tried letting the update inherit Player, like this

def update(self2, Player):

why doesn't this work?

Question 3) How can I make the move_towards_Player get updated in the loop?

Upvotes: 4

Views: 58

Answers (1)

sloth
sloth

Reputation: 101052

how come when the all_sprites.Group get updated in the loop why doesn't the move_towards player get updated?

You ask why the move_towards_Player function is not called? Because you never call it, and it isn't magically called by anything. The update function of Group will call the update function of all its sprites. Nothing more.

I tried letting the update inherit Player, like this ... why doesn't this work?

The update function of Group will pass all arguments to the update function of all its sprites. So you could call it like this:

all_sprites.update(player)

and ensure that all sprite classes have their update function take an extra argument beside self.

How can I make the move_towards_Player get updated in the loop?

Just call it from the Enemy's update function.

You could start with something like this:

import pygame
import random
GREEN=(0,255,0)
RED=(255,0,0)
BLACK=(0,0,0)
HEIGHT=600
WIDTH=800
FPS=120

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 40))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.left = 25
        self.rect.bottom = HEIGHT / 2
        self.speedx = 0
        self.speedy = 0

class Enemy(pygame.sprite.Sprite):
    def __init__(self, target):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 40))
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(680, 750)
        self.rect.y = random.randrange(HEIGHT - self.rect.height)
        self.target = target
        self.speed = random.randint(4, 6)

    def update(self, dt):
        self.move_towards_Player(dt)

    def move_towards_Player(self, dt):
        pos = pygame.Vector2(self.rect.center)
        v = pygame.Vector2(self.target.rect.center) - pos
        if (v.length() < 5):
            self.kill()
        else:
            v.normalize_ip()
            v *= self.speed * dt/10
            pos += v
            self.rect.center = round(pos.x), round(pos.y)

all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
player = Player()
all_sprites.add(player)

for i in range(3):
    e = Enemy(player)
    all_sprites.add(e)
    enemies.add(e)
clock=pygame.time.Clock()

pygame.init()
screen=pygame.display.set_mode([WIDTH, HEIGHT])
running = True
while running:

    dt=clock.tick(FPS)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    all_sprites.update(dt)
    #Draw render
    screen.fill(BLACK)
    all_sprites.draw(screen)
    pygame.display.flip()

pygame.quit()
quit()

Note that:

  • I renamed self2 to self. By convention, the first argument of a method should be called self. Stick to it.
  • I pass the Player instance to the Enemy-class' __init__ function and store it in a class member. This way, we don't have to "pollute" the update function
  • I use pygame's Vector2 class to handle the math.
  • I pass the delta time to the update function so the movement speed is constant even when the framerate is not

Upvotes: 1

Related Questions