pythonpython-3.xpygamecollisionportfolio

Pygame 2 problem with collisions in first portfolio game


I have created this account only to ask this question :D I am learning python and for my portfolio I am trying to create some game in OOP approach in python.

I have a problem with collisions and I don't even know how to check where the mistake is.

When player's bullets go offscreen, they disappear as they should and there is a limiter for max of 3 bullets. So I guess this part is good for sure.

But when bullet's rect meets green alien's rect, it doesn't disappear nor reduce alien's hp, because alien doesn't disappear...

I am already so frustrated, because this bug stopped me for 2 days... I have my game in 2 files: main.py and static.py for images, classes etc.

I think there is a problem in section #PLAYER'S BULLET BEHAVIOR in Main.py

Please, help :)

Ps. I use VSC if it matters

THIS IS MY Main.py:

import pygame as pg
from static import *

pg.init()


# ------------- BACKGROUND AND LVL SETTINGS ------------- #
bg1 = Background(bg_img1scaled)
lvl = 1
red_enemies_count = 5
green_enemies_count = 5


# ------------- ENEMIES SETTINGS ------------- #
redEnemyList = [RedEnemy() for _ in range(red_enemies_count)]
greenEnemyList = [GreenEnemy() for _ in range(green_enemies_count)]


# ------------- GAME LOOP ------------- #
clock = pg.time.Clock()
run = True
while run:
    # FPS HANDLER
    clock.tick(FPS)

    # EVENT HANDLER
    for event in pg.event.get():
        if event.type == pg.QUIT:
            run = False
        if event.type == pg.KEYDOWN:
            if event.key == pg.K_SPACE and len(p_bulletList) < 3:
                p_bulletList.append(P_Bullet())

        # ENEMY BEHAVIOR
    for red_enemy in redEnemyList:
        if red_enemy.x > 0 and red_enemy.x + red_enemy.img_width < WIDTH:
            red_enemy.x += red_enemy.speed
        if red_enemy.x + red_enemy.img_width >= WIDTH or red_enemy.x <= 0:
            red_enemy.speed = -red_enemy.speed
            red_enemy.x += red_enemy.speed

    for green_enemy in greenEnemyList:
        if green_enemy.x > 0 and green_enemy.x + green_enemy.img_width < WIDTH:
            green_enemy.x += green_enemy.speed
        if green_enemy.x + green_enemy.img_width >= WIDTH or green_enemy.x <= 0:
            green_enemy.speed = -green_enemy.speed
            green_enemy.x += green_enemy.speed
        if green_enemy.hp == 0:
            greenEnemyList.remove(green_enemy)

        # PLAYER'S BULLETS BEHAVIOR
    for bullet in p_bulletList:
        bullet.y -= P_BULLET_SPEED
        if bullet.y <= 0:
            p_bulletList.remove(bullet)

        if bullet.rect.colliderect(green_enemy.rect):
            green_enemy.hp -= 1
            p_bulletList.remove(bullet)

        # PLAYER MOVEMENT
    key_pressed = pg.key.get_pressed()
    if key_pressed[pg.K_UP] and (player_rect.y > 0):
        dy = -P_SPEED
        player_rect.y += dy
    if key_pressed[pg.K_DOWN] and (player_rect.y < HEIGHT - player_rect.height):
        dy = P_SPEED
        player_rect.y += dy
    if key_pressed[pg.K_LEFT] and (player_rect.x > 0):
        dx = -P_SPEED
        player_rect.x += dx
    if key_pressed[pg.K_RIGHT] and (player_rect.x < WIDTH - player_rect.width):
        dx = P_SPEED
        player_rect.x += dx

    # DISPLAY HANDLER
    window.fill(BLACK)
    bg1.draw_bg(window)

    for red_enemy in redEnemyList:
        red_enemy.draw_enemy(window)

    for green_enemy in greenEnemyList:
        green_enemy.draw_enemy(window)

    draw(window, player_scaled, player_rect)
    for bullet in p_bulletList:
        bullet.draw_bullet(window)

    pg.display.update()

pg.quit()

THIS IS MY Static.py:

import pygame as pg
import random

# ------------- COLORS ------------- #
BLACK = 0, 0, 0
WHITE = 255, 255, 255
RED = 255, 0, 0
GREEN = 0, 255, 0
BLUE = 0, 0, 255

# ------------- SCREEN SETTINGS ------------- #
WIDTH = 1200
HEIGHT = 800
window = pg.display.set_mode((WIDTH, HEIGHT))
FPS = 60

# ------------- IMAGES ------------- #
bg_img1 = pg.image.load('images/backgrounds/background_0.png')
bg_img1scaled = pg.transform.scale(bg_img1, (WIDTH, HEIGHT)).convert_alpha()

player_img = pg.image.load('images/player/player_ship.png')
player_scaled = pg.transform.scale(player_img,
                                   (player_img.get_width() * 0.5, player_img.get_height() * 0.5)).convert_alpha()


# ------------- PLAYER SETTINGS ------------- #
player_rect = player_scaled.get_rect()
player_rect.center = (WIDTH // 2, HEIGHT - player_rect.height // 2)
p_width = player_rect.width
p_heigh = player_rect.height
P_SPEED = 6
dx = 0
dy = 0
P_BULLET_SPEED = 10
max_p_bullets = 3
p_bulletList = []


# ------------- CLASSES ------------- #
class Background():
    def __init__(self, image):
        self.img = image
        self.x = 0
        self.y = 0

    def draw_bg(self, screen_var_name):
        screen_var_name.blit(self.img, (self.x, self.y))


class P_Bullet():
    def __init__(self):
        self.img = pg.image.load(
            'images/player/player_bullet2.png').convert_alpha()
        self.img_width = self.img.get_width()
        self.img_height = self.img.get_height()

        self.rect = self.img.get_rect()
        self.rect.x = player_rect.x + player_rect.width // 2 - self.img_width // 2
        self.rect.y = player_rect.y - self.img_height

        self.x = self.rect.x
        self.y = self.rect.y

    def draw_bullet(self, screen_var_name):
        screen_var_name.blit(self.img, (self.x, self.y))


class RedEnemy():
    def __init__(self):
        self.img = pg.image.load('images/enemies/red_alien.png')
        self.img_scaled = pg.transform.scale(self.img,
                                             (player_rect.width, player_rect.height)).convert_alpha()
        self.img_width = self.img_scaled.get_width()
        self.img_height = self.img_scaled.get_height()

        self.rect = self.img_scaled.get_rect()
        self.rect.x = random.randint(1, WIDTH - self.img_width)
        self.rect.y = random.randint(0, 170)

        self.x = self.rect.x
        self.y = self.rect.y

        self.speed = random.randint(3, 6)
        self.hp = 3

    def draw_enemy(self, screen_var_name):
        screen_var_name.blit(self.img_scaled, (self.x, self.y))


class GreenEnemy(RedEnemy):
    def __init__(self):
        self.img = pg.image.load('images/enemies/green_alien.png')
        self.img_scaled = pg.transform.scale(self.img,
                                             (player_rect.width, player_rect.height)).convert_alpha()
        self.img_width = self.img_scaled.get_width()
        self.img_height = self.img_scaled.get_height()

        self.rect = self.img_scaled.get_rect()
        self.rect.x = random.randint(1, WIDTH - self.img_width)
        self.rect.y = random.randint(270, 370)

        self.x = self.rect.x
        self.y = self.rect.y

        self.speed = random.randint(2, 4)
        self.hp = 2


# ------------- DEFs ------------- #
def draw(screen_var_name, img, rect: pg.Rect):
    screen_var_name.blit(img, rect)


Solution

  • Why do you need the .x and .y attributes? You have rect.x and rect.y. Anyway, when you change x, and y you also need to change rect.x, and rect.y. Note, bullet.rect.colliderect(green_enemy.rect): uses green_enemy.rect, but green_enemy.x and green_enemy.y. rect is not tied to x and y, rect.x and rect.y don't magically update when you change x and y.

    Update the rect attribute before the collision test:

    while run:
       # [...]
    
       for bullet in p_bulletList:
            bullet.y -= P_BULLET_SPEED
            if bullet.y <= 0:
                p_bulletList.remove(bullet)
    
            else:
    
                # update bullet.rect and green_enemy.rect
                bullet.rect.topleft = bullet.x, bullet.y
                green_enemy.rect.topleft = green_enemy.x, green_enemy.y
    
                if bullet.rect.colliderect(green_enemy.rect):
                    green_enemy.hp -= 1
                    p_bulletList.remove(bullet)
    

    See also How do I detect collision in pygame?.