Kromydas
Kromydas

Reputation: 111

Pygame delete object

I need help deleting an object, and I mean delete, not draw over or other things. My code so far:

def detect_collision(player_pos, enemy_pos):
    p_x = player_pos[0]
    p_y = player_pos[1]

    e_x = enemy_pos[0]
    e_y = enemy_pos[1]

    if (e_x >= p_x and e_x < (p_x + player_size)) or (p_x >= e_x and p_x < (e_x+enemy_size)):
        if (e_y >= p_y and e_y < (p_y + player_size)) or (p_y >= e_y and p_y < (e_y+enemy_size)):
            return True
    return False

def bullets():
    b_x = player_pos[0]
    b_y = player_pos[1]
    keep_going = True
    pygame.draw.rect(screen, TEAL, (b_x, b_y, 15, 50))
    while keep_going:
        b_y += 75
        if detect_collision(player_pos, enemy_pos):
            # deleting part here

Here is what makes my player and enemy:

enemy_size = 50
enemy_pos = [random.randint(0,WIDTH-enemy_size), 0]
enemy_list = [enemy_pos]


def drop_enemies(enemy_list):
    delay = random.random()
    if len(enemy_list) < 10 and delay < 0.1:
        x_pos = random.randint(0,WIDTH-enemy_size)
        y_pos = 0
        enemy_list.append([x_pos, y_pos])

def draw_enemies(enemy_list):
    for enemy_pos in enemy_list:
        pygame.draw.rect(screen, RED, (enemy_pos[0], enemy_pos[1], 
        enemy_size, enemy_size))

def update_enemy_positions(enemy_list, score):
    for idx, enemy_pos in enumerate(enemy_list):
        if enemy_pos[1] >= 0 and enemy_pos[1] < HEIGHT:
            enemy_pos[1] += SPEED
        else:
            enemy_list.pop(idx)
            score += 1
     return score

Player part:

player_size = 50
player_pos = [WIDTH/2, HEIGHT-2*player_size]

pygame.draw.rect(screen, TEAL, (player_pos[0], player_pos[1], player_size, 
player_size))

Upvotes: 3

Views: 3716

Answers (4)

Vijay
Vijay

Reputation: 65

I'm assuming there's only one bullet in screen at a time since you didn't mention a bullet list. You probably create bullet by hitting space or something which I'll leave out here. One thing to consider in you bullets() function is your doing moving, drawing and checking for collision in the same function. Note, it is always a good idea to make a function do only one thing.

def draw_bullet():
    move_bullet()
    pygame.draw.rect(screen, TEAL, (b_x, b_y, 15, 50))
def move_bullet():
    if b_y < HEIGHT:
        b_y += 75
    else:
        create_bullet = False # explained below

So to create a bullet, you should have a Boolean create_bullet variable. So:

# mainloop
if user hits spacebar (or the key used to create bullet) : 
     create_bullet = true
     b_x = player_pos[0] # initialise bullet
     b_y = player_pos[1]
if create_bullet:
    draw_bullet()
if detect_collision((b_x, b_y), enemy_pos): 
    # assuming b_x, b_y are global variable or something, so that they are actually defined and equal to bullet's x and y pos
    # if bullet and enemy collide, remove them both
    enemy_list.remove(enemy)
    create_bullet = False
if detect_collision(player_pos, enemy_pos): 
    # reset player pos, or whatever you want to do

you say you want to delete it and not just draw over it. However the way pygame generates a 'moving/ video-like' screen is by continuously drawing over. E.g: player is drawn at (10, 10) and then is drawn at (20, 10) so it appears like player moved. However, this is done very fast so you don't see him 'disappear' and 'reappear'. So here's what the above code does.

When spacebar is hit, it 'creates' the bullet by setting it's x and y value to the current player position and sets create_bullet = true. Then, on each iteration of the main loop, if create_bullet is true, it moves then draws the bullet. If the bullet moves outside the screen or collides with enemy, create_bullet = False, so it will stop drawing it and on the next iteration of main loop, bullet will be drawn over by the background and would 'disappear'.

Upvotes: 0

Kingsley
Kingsley

Reputation: 14906

Right, so you have a list of enemy positions in enemy_list. That's a good start. I don't see a bullet_list, so I will assume only a single bullet at a time, positioned at b_x,b_y.

So a main loop for this program might look something like:

### Main Loop
while not game_over:
    
    # handle user input

    # move the player
    # move the bullet (if any)
    # move the enemies

    # if there's a bullet, did it hit an enemy?
       # remove enemy hit from enemy_list

    # Did an enemy in enemy_list, hit the player?
       # do game-over

    # clear the screen
    # paint the player 
    # paint the bullet (if any)
    # paint every enemy in enemy_list

Where the enemy collision, and list removal might look something like the below. (I've tried to match the look of your code as much as possible.)

# if a bullet is on-screen (non 0,0), see if it hits an enemy
if ( b_x != 0 and b_y != 0 ):
    hit_enemy_idx = None
    for idx, enemy_pos in enumerate( enemy_list ):
        # check for collision
        if detect_collision( ( b_x, b_y ), enemy_pos ):
            hit_enemy = idx      # note the enemy index
            b_x, b_y  = 0, 0     # erase the bullet
            break                # exit the loop when hit found

    # If the bullet hit an enemy, remove from list
    if hit_enemy_idx != None:
        del( enemy_list[ hit_enemy_idx ] )      # delete hit enemy from list

We do an iteration through the enemy list, checking for a collision. Once a hit is found, it save the index of which enemy was hit, and stops the loop.

The next step is to delete the enemy from the list. I have written this in a separate block, rather than placing it before the break in the loop. This is because unexpected results can happen when you change a list as you're iterating over it. In this particular case it would have been OK, but it's something to be wary of as a beginner.

Upvotes: 0

import random
import random

Reputation: 3245

It would be beneficial to look into Sprites and SpriteGroups to track entities in your game. They have a bunch of functionality built in that will make things easier.

Here's a demo I have that groups sprites, removing those that collide with the mouse pointer:

import random
import pygame

screen_width, screen_height = 640, 480

def get_random_position():
    """return a random (x,y) position in the screen"""
    return (
        random.randint(0, screen_width - 1),  # randint includes both endpoints.
        random.randint(0, screen_height - 1),
    )

color_list = ["red", "orange", "yellow", "green", "cyan", "blue", "blueviolet"]
colors = [pygame.color.Color(c) for c in color_list]

class PowerUp(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        width, height = 64, 32
        self.color = random.choice(colors)
        self.image = pygame.Surface([width, height])
        self.image.fill(self.color)
        # Fetch the rectangle object that has the dimensions of the image
        self.rect = self.image.get_rect()
        # then move to a random position
        self.update()

    def update(self):
        # move to a random position
        self.rect.center = get_random_position()

if __name__ == "__main__":
    pygame.init()
    screen = pygame.display.set_mode((screen_width, screen_height))
    pygame.display.set_caption("Sprite Group Collision Demo")
    clock = pygame.time.Clock()  # for limiting FPS
    FPS = 60
    exit_demo = False

    # create a sprite group to track the power ups.
    power_ups = pygame.sprite.Group()
    for _ in range(10):
        power_ups.add(PowerUp())  # create a new power up and add it to the group.

    # main loop
    while not exit_demo:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                exit_demo = True
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    exit_demo = True
                elif event.key == pygame.K_SPACE:
                    power_ups.update()
            elif event.type == pygame.MOUSEBUTTONUP:
                for _ in range(10):
                    power_ups.add(PowerUp())

        # Update State: check for collisions
        for p in power_ups:
            if p.rect.collidepoint(pygame.mouse.get_pos()):
                power_ups.remove(p)
        # draw background
        screen.fill(pygame.Color("black"))  # use black background
        # draw sprites
        power_ups.draw(screen)
        # update screen
        pygame.display.update()
        clock.tick(FPS)
    pygame.quit()
    quit()

Clicking a mouse button creates more sprites and pressing the space bar randomises their position.

The update() method of a bullet sprite would adjust the sprite position by its speed, e.g. self.rect.x += SPEED and you would need to call the bullet sprite group .update() method every game loop.

Upvotes: 0

Blckknght
Blckknght

Reputation: 104712

The best way to solve your issue is to learn how to use Sprite objects in pygame. Together with Group objects, they can already do what you want, right out of the box.

In brief, your "enemy" should be an instance of some Sprite subclass, and you'd add it to an instance of Group (rather than building your own enemy_list). When you want the enemy to die, you can call the kill() method on it, which will remove it from the Group. This serves to delete it from the game, since you should be using methods on the Group object to update and draw all the sprites it contains (but not ones that have been killed).

Upvotes: 2

Related Questions