I’m making a Space Invaders game, and I’m having trouble with the home and pause screens. The main thing I’m unable to do is going back to the former from the latter, because the home screen button in the pause screen and the start game button in the home screen are located at roughly the same coordinates. That means that whenever I click to go back to the home screen from the pause screen, Pygame keeps the pygame.mouse.get_pressed()[0] instance as True and starts the game automatically, as if I’ve clicked to start it from the home screen as well.
To solve that issue, I decided to use collidepoint() to try and make Pygame understand that the collision was in the rect from the pause screen, not in the one from the home screen (though it doesn’t change anything because once I’m back to the home screen, Pygame detects a collision again and clicks the start game button).
But now I’ve ran into another issue: collidepoint() is returning False when it should return True. The x coordinates of the start game button are 149 -> 621 and the y coordinates are 370 -> 403. Any point located inside that interval should return a collision, right? But for some points, it just returns False, for example: collidepoint(458, 389).
In my game, I’m blitting both the home and the pause screens over a dark rectangle that is smaller than the game window:
What I’ve found out is that whenever I do that (blitting a rect A over another rect B), Pygame defines the coordinates of the rect A based on the rect B instead of the entire game screen, while it doesn’t do the same for the mouse. So, in the example above, the point (458, 389) is outside of the “CLICK OR PRESS ENTER TO START” rect because this rect y axis starts at 355 of the background rect (370 of the screen) and has a height of 33, ending on 388 (403 of the screen).
Here's a snippet of my code:
class Pause(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.paused = False
# Pause screen
self.image = pygame.Surface((760, 570), pygame.SRCALPHA)
self.image.fill(MENU_BACKGROUND)
self.rect = self.image.get_rect()
self.rect.center = screen_rect.center
title_font = pygame.font.Font(FONT_FILE, 96)
title_surface = title_font.render('PAUSED', True, (255, 255, 255))
title_rect = title_surface.get_rect()
title_rect.center = self.rect.center
link_font = pygame.font.Font(FONT_FILE, 48)
# Home screen button
main_surface = link_font.render('MAIN MENU', True, (255, 255, 255))
self.main_rect = pygame.Rect((310, 365), (215, 61))
self.main_rect.midtop = title_rect.midbottom
self.image.blits(((title_surface, title_rect), (main_surface, self.main_rect)))
def update(self):
if pygame.mouse.get_pressed()[0]:
main_click = self.main_rect.collidepoint(pygame.mouse.get_pos())
if main_click:
global game_start
game_start = False
self.paused = False
render_sprites.empty()
render_sprites.add(main_screen)
def pause(self):
player.dx = 0
render_sprites.empty()
render_sprites.add(self)
def resume(self):
render_sprites.empty()
# Redraws the gameplay sprites
if bullet.shot:
render_sprites.add(player, bullet, score, enemies) # Redraws the bullet if it was moving before pausing
else:
render_sprites.add(player, score, enemies)
self.paused = False
class MainScreen(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((760, 570), pygame.SRCALPHA)
self.image.fill(MENU_BACKGROUND)
self.rect = self.image.get_rect()
self.rect.center = screen_rect.center
self.last = pygame.time.get_ticks()
self.cooldown = 400
self.add(render_sprites)
# Title text
title_font = pygame.font.Font(FONT_FILE, 96)
self.title_image = title_font.render('SPACE INVADERS', True, (255, 255, 255))
self.title_rect = self.title_image.get_rect()
self.title_rect.center = screen_rect.center
link_font = pygame.font.Font(FONT_FILE, 32)
# Start game blinking button
self.start_text = link_font.render('CLICK OR PRESS ENTER TO START', True, (255, 255, 255))
self.start_rect = pygame.Rect((149, screen_rect.top + 370), (472, 33))
self.start_rect.center = (385, 371.5)
self.no_text = pygame.Surface(self.start_rect.size, pygame.SRCALPHA)
self.no_text.fill((0, 0, 0, 0))
self.start_surfaces = cycle([self.start_text, self.no_text])
# Options screen button
self.opt_surface = link_font.render('OPTIONS', True, (255, 255, 255))
self.opt_rect = pygame.Rect((149, 388), (126, 33))
self.opt_rect.center = (385, 404.5)
self.image.blits(((self.title_image, self.title_rect),
(self.start_text, self.start_rect),
(self.opt_surface, self.opt_rect)))
def update(self):
global game_start
if game_start:
self.kill()
else:
# Blinking text
now = pygame.time.get_ticks()
if (now - self.last) >= self.cooldown:
self.image.fill(MENU_BACKGROUND)
self.image.blits(((self.title_image, self.title_rect),
(next(self.start_surfaces), self.start_rect),
(self.opt_surface, self.opt_rect)))
self.last = pygame.time.get_ticks()
rect_click = self.start_rect.collidepoint(pygame.mouse.get_pos())
# Starts game
if pygame.key.get_pressed()[pygame.K_RETURN] or (
pygame.mouse.get_pressed()[0] and
rect_click):
game_start = True
render_sprites.add(player, enemies, score)
TL;DR: I’m having two issues:
- By clicking on the home screen menu button on the pause screen I’m accidentally clicking on the start game button because both buttons are located at the same place;
collidepoint()is returningFalsewhen it should returnTrue.