1

I am trying to make a simple game with pygame, and I am running into the following general issue.

Suppose we have two sprites, call them sprite1 and sprite2, each with their corresponding rect and image attributes. These sprites are "clickable", in the sense that when the user clicks on them something should happen. I usually implement this by adding the following into the game loop:

if event.type == pygame.MOUSEBUTTONDOWN:
    mouse_pos = pygame.mouse.get_pos()
    if spritex.collidepoint(mouse_pos):
        (whatever clicking on spritex triggers)

In my game sprite1 and sprite2 can occasionally overlap, and let's suppose that I always want to draw sprite2 on top of sprite1. I understand that I can achieve this by having screen.blit(sprite1.image, sprite1.rect) before screen.blit(sprite2.image, sprite2.rect). However, I would like that whenever sprite1 and sprite2 are overlapping, and the user clicks on a point that is both above sprite1.rect and sprite2.rect only the action of sprite2 should trigger.

In this simple example, I understand that one could do this with

if event.type == pygame.MOUSEBUTTONDOWN:
    mouse_pos = pygame.mouse.get_pos()
    if sprite2.collidepoint(mouse_pos):
       (whatever clicking on spritex triggers)
    elif sprite1.collidepoint(mouse_pos):
       (whatever clicking on sprite1 triggers)

but this solution does not generalize easily to more complicated situations (more sprites, more types of sprites, pygame_gui elements, etc.).

I wonder: is there a more simple and elegant solution to get around problems like these?

equin
  • 133
  • 4
  • 1
    Add the sprites to a list and sort the sprites according to their priority. If you click, then find the first sprite in the list. – Rabbid76 Jun 07 '21 at 13:01

1 Answers1

1

You can create a list of all sprites in order of z-index/how far front they are, starting with closer sprites.

sprites = [sprite2, sprite1]

Then iterate until you find the first match:

def find_clicked(mouse_pos):
    for sprite in sprites:
        if sprite.collidepoint(mouse_pos):
            return sprite

You still have to process based on the sprite that is clicked. That will still require some sort of if tree unless you have a clever implementation, such as a dictionary of function references (which only works if you never recreate the sprite objects!). However, this is much more robust than having a hardcoded if tree in the order of sprite position, because you can easily reorder the list and everything will work fine.

thshea
  • 1,048
  • 6
  • 18