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?
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