Daniel Kleissl
Daniel Kleissl

Reputation: 73

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()


Upvotes: 3

Views: 202

Answers (3)

Daniel Kleissl
Daniel Kleissl

Reputation: 73

Thanks to the two posts above!

I found another way that also seems to work:

#in marker class:

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


#new function:

def check_collision(pos):
  collide = False
  for s in all_sprites:
    if s.check_click(pos) == True:
      collide = True
      return collide
  return collide

#[...]

  mouse = pygame.mouse.get_pos()
  collide = check_collision(mouse)

#[...]
#new conditional:

    elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT:
      if collide == False or collide == None:
        mouse = pygame.mouse.get_pos()
        connections.add_entrance_or_exit(mouse)

Both of the answers above are also working, just wanted to add another possibility if anyone ever has the same problem and needs another solution. :) Thanks for all the help!

Upvotes: 1

Rabbid76
Rabbid76

Reputation: 210909

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

Upvotes: 1

h_r_b_y
h_r_b_y

Reputation: 193

I would make the collision detection a list of all collisions, then you get a bool (True or None) for every sprite. Then if True is in the list you cant execute the code after clicking (hope this makes sense)

while running:
  collide = [] # collide is an empty list
  for s in all_sprites:
    mouse_pos = pygame.mouse.get_pos()
    s.check_click(mouse_pos)
    collide.append( s.check_click(mouse_pos) ) 
    # add whatever check_click returns to the list

  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      running = False
    elif event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT:
      if not True in collide: # if there was not a collision in the list
        mouse = pygame.mouse.get_pos()
        connections.add_entrance_or_exit(mouse)

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

hope this helps

Upvotes: 1

Related Questions