Reputation: 73
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
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
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
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