Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pygame problem: how to execute conditional on collision?

I have a problem with pygame and collision detection. The program I wrote should draw a rectangle on click, change alpha on mousover. These two work. But I am also trying to implement functionality that only executes if a rectangle already exists/if mouse is over an existing rectangle.

For now I tried to get the program not to draw a new marker/rectangle if it detects collision, but I can't think of a way to do that... The way I tried in my code example does not work, it only returns collision status for the last marker added...

I only started coding 2 Months ago, so maybe I am missing some fundamental idea regarding python. I tried to post as little of my code as possible without loosing the gist of what it does. Sorry if it is still too long.

So is there a way to get the functionality I want?

Thanks for any help!

import pygame


pygame.init()

clock = pygame.time.Clock()
clock.tick(20)

BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)

screen = pygame.display.set_mode((500, 500))
running = True


LEFT = 1



class Marker(pygame.sprite.Sprite):

  """
used to draw the marker at a given position
  """

  def __init__(self, x, y, key, function):
    pygame.sprite.Sprite.__init__(self)

    self.image = pygame.Surface((50, 50))
    self.image.fill(RED)
    self.image.set_alpha(50)

    self.rect = self.image.get_rect()
    self.rect.x = x
    self.rect.y = y

    self.function = function

  def check_click(self, mouse):
    if self.rect.collidepoint(mouse):
      self.image.set_alpha(200)
      return True

    if not self.rect.collidepoint(mouse):
      self.image.set_alpha(50)


  def collide_check(self, mouse):
    return self.rect.collidepoint(mouse)

  def get_function(self):
    return self.function


class Connections:

  def __init__(self):
    self.switch = False
    self.con_dict = {}
    self.key_dict = []

  def generate_key(self, position): #generates a dictionary key out of mouse position
    position_x, position_y = position
    instance_name = str(position_x) + str(position_y)
    return instance_name

  def add_entrance_or_exit(self, position): 
#sets marker/rectangle at mouse position(depending on switch state)
    if not self.switch:
      key = self.generate_key(position)
      self.key_dict.append(key)
      self.con_dict[key] = position
      pos_x, pos_y = position
      new_key = key + "_entrance"
      new_key = Marker(pos_x, pos_y, key, "entrance")
      all_sprites.add(new_key)
      self.switch = True
    else:
      key = self.key_dict[-1]
      old_pos = self.con_dict[key]
      pos_x, pos_y = position
      new_key = key + "_exit"
      new_key = Marker(pos_x, pos_y, key, "exit")
      all_sprites.add(new_key)
      self.con_dict[key] = [old_pos, position]
      self.switch = False


all_sprites = pygame.sprite.Group()

connections = Connections()

running = True


while running:
  collide = None
  for s in all_sprites:
    mouse_pos = pygame.mouse.get_pos()
    s.check_click(mouse_pos)
    collide = s.check_click(mouse_pos)

  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      running = False
    elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT:
      if collide == None:
        mouse = pygame.mouse.get_pos()
        connections.add_entrance_or_exit(mouse)

  screen.fill(BLACK)
  all_sprites.update()
  all_sprites.draw(screen)
  pygame.display.update()


pygame.quit()


like image 302
Daniel Kleissl Avatar asked Jan 26 '26 19:01

Daniel Kleissl


1 Answers

Since check_click changes the alpha channel of the image, it has to be called for each sprite (s). collide should be a boolean value (either True or False) and has to be set if s.check_click(mouse_pos) evaluates True:

running = True
while running:

  mouse_pos = pygame.mouse.get_pos()

  collide = False
  for s in all_sprites:
    if s.check_click(mouse_pos):
      collide = True

  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      running = False
    elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT:
      if not collide:
        mouse = pygame.mouse.get_pos()
        connections.add_entrance_or_exit(mouse)

Note, this can be further simplified by creating a list of the sprites which are colliding and verifying if the list contains not any element:

running = True
while running:

  mouse_pos = pygame.mouse.get_pos()
  collide_list = [s for s in all_sprites if s.check_click(mouse_pos)]

  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      running = False
    elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT:
      if not any(collide_list):
        mouse = pygame.mouse.get_pos()
        connections.add_entrance_or_exit(mouse)

Furthermore, it is not sufficient to evaluate if the mouse is on the rectangle, you've to evaluate if the rectangle, which would be drawn at the mouse position would intersect any other rectangle by colliderect(). The alpha channel of the image has to be set dependent on the mouse position collidepoint():
(Note, collidepoint respectively colliderect() returns either True or False)

class Marker(pygame.sprite.Sprite):
  # [...]

  def check_click(self, mouse):
    self.image.set_alpha(200 if self.rect.collidepoint(mouse) else 50)
    return self.rect.colliderect((*mouse, 50, 50)) 

like image 75
Rabbid76 Avatar answered Jan 29 '26 09:01

Rabbid76