Search code examples
pythonarrayspygamecollision-detection

pygame-How do I check if there is collision with any rect in an array?


I have an array of Box objects that each have a rect attribute. I'm trying to check if my player collides with any of the rects in the array. The way it's working now, the logic only works on the last element of the array, so my player will only collide with one of the rects and all the other ones he just walks over them. I think there might be a better way to check for collision with any of the rects, but here is what I have:

class Box:
def __init__(self, a, b, c, d):
    self.rect = pygame.Rect(a, b, c, d)

...

def detect_collisions(self, player_obj):
    for b in range(len(self.collision_boxes)):
        if pygame.Rect.colliderect(self.collision_boxes[b].rect, player_obj.top_rect):
            player_obj.can_move_up = False
        else:
            player_obj.can_move_up = True
        if pygame.Rect.colliderect(self.collision_boxes[b].rect, player_obj.bottom_rect):
            player_obj.can_move_down = False
        else:
            player_obj.can_move_down = True
        if pygame.Rect.colliderect(self.collision_boxes[b].rect, player_obj.left_rect):
            player_obj.can_move_left = False
        else:
            player_obj.can_move_left = True
        if pygame.Rect.colliderect(self.collision_boxes[b].rect, player_obj.right_rect):
            player_obj.can_move_right = False
        else:
            player_obj.can_move_right = True

Basically, the player has a rect on each edge, and if one of the edges collides with any of the other rects, it stops the player from moving in that direction. The array collision_boxes does contain all the box objects and not just the last one, I have checked it.

When I call the detect_collisions function, I do it like this:

    keys = pygame.key.get_pressed()

if keys[pygame.K_w]:  # check for keystrokes
    player.faceDirection = "U"
    if player.y_pos > current_map.map_bounds_upper:  # check bounds
        current_map.detect_collisions(player)
        if player.can_move_up:
            player.move_up()

Is there a better way to check every element in the array in this case? Where am I going wrong with this loop that causes it to only run the if statements on the last element in collision_boxes?


Solution

  • Every time the code is executed in the loop, all 4 states (can_move_up, can_move_down , can_move_left, can_move_right ) are set. In each loop iteration, the results of the previous loop are overwritten. At the end only the results of the last run are set.

    Set the status variables before the loop, but reset them inside the loop:

    def detect_collisions(self, player_obj):
    
        player_obj.can_move_up = True
        player_obj.can_move_down = True
        player_obj.can_move_left = True
        player_obj.can_move_right = True
    
        for b in self.collision_boxes:
            if player_obj.top_rect.colliderect(b.rect):
                player_obj.can_move_up = False
            if player_obj.bottom_rect.colliderect(b.rect):
                player_obj.can_move_down = False
            if player_obj.left_rect.colliderect(b.rect):
                player_obj.can_move_left = False
            if player_obj.right_rect.colliderect(b.rect):
                player_obj.can_move_right = False
    

    You can simplify your code by creating a list of pygame.Rect objects and using pygame.Rect.collidelist:

    collidelist(list) -> index
    Test whether the rectangle collides with any in a sequence of rectangles. The index of the first collision found is returned. If no collisions are found an index of -1 is returned.

    def detect_collisions(self, player_obj):
    
        rect_list = [b.rect for b in self.collision_boxes]
    
        player_obj.can_move_up =  player_obj.top_rect.collidelist(rect_list) == -1
        player_obj.can_move_down = player_obj.bottom_rect.collidelist(rect_list) == -1
        player_obj.can_move_left = player_obj.left_rect.collidelist(rect_list) == -1
        player_obj.can_move_right = player_obj.right_rect.collidelist(rect_list) == -1