I'm creating a game. So, on my main menu, I have two buttons for play and exit. But, whichever button i call later, i.e, if i call my func for Start button and then call the Exit button, or vice versa, the second called button starts flickering. Here's my code:
import math
import pygame
import random
import time
from pygame import mixer
# Initialising PyGame
pygame.init()
# Creating Screen
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Space Invaders')
# Global Variables
playerIMG = pygame.image.load('player.png')
alienIMG = []
bulletIMG = pygame.image.load('bullet.png')
bullet_state = 'ready'
clock = pygame.time.Clock()
start_count = 0
exit_count = 0
# Background
bgIMG = pygame.image.load('C:bgIMG.png')
bgIMG = pygame.transform.scale(bgIMG, (800, 600))
# Font
def text_on_screen(text, colour, x, y, size):
font = pygame.font.Font('Stargaze Regular.otf', size)
screen_text = font.render(text, True, colour)
screen.blit(screen_text, [x, y])
def player(x, y):
screen.blit(playerIMG, (x, y))
def alien(x, y, i):
screen.blit(alienIMG[i], (x, y))
def fire_bullet(x, y):
global bullet_state
bulletSound = mixer.Sound('laser.wav')
bulletSound.play()
bullet_state = 'fire'
screen.blit(bulletIMG, (x + 15, y - 30))
def isCollision(x1, x2, y2, y1):
dist = math.sqrt(((x1 - x2) ** 2) + ((y1 - y2) ** 2))
if dist < 23:
collisionSound = mixer.Sound('explosion.wav')
collisionSound.set_volume(120)
collisionSound.play()
return True
else:
return False
class Button:
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, win, font_size, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.Font('Stargaze Regular.otf', font_size)
text = font.render(self.text, 1, (0, 0, 0))
win.blit(text, (
self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def isOver(self, pos):
# Pos is the mouse position or a tuple of (x,y) coordinates
if self.x < pos[0] < self.x + self.width:
if self.y < pos[1] < self.y + self.height:
return True
return False
# Game Over
def game_over():
for i in range(3000):
text_on_screen('GAME OVER!', (255, 255, 0), 190, 220, 60)
text_on_screen('PRESS R TO RESTART', (255, 255, 0), 270, 310, 20)
text_on_screen('PRESS Q TO QUIT', (255, 255, 0), 300, 335, 20)
pygame.display.update()
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_r:
gameloop()
break
elif e.key == pygame.K_q:
pygame.quit()
quit()
def Start_button():
global start_count
mouse = pygame.mouse.get_pos()
start_button = Button((255, 0, 255), 10, 370, 80, 40, 'PLAY')
startwidth = start_button.width
startheight = start_button.height
start_text = start_button.text
if start_button.isOver(mouse):
start_button.color = (255, 255, 255)
start_button.width += 10
start_button.height += 10
loop = 0
i = 1
if start_count == 0:
while loop < 4 and i < 5:
start_button.text = start_text[:i]
start_button.draw(screen, 23)
time.sleep(0.08)
loop += 1
i += 1
start_count += 1
pygame.display.update()
else:
start_button.draw(screen, 23)
pygame.display.update()
else:
start_button.height = startheight
start_button.width = startwidth
start_button.draw(screen, 20)
pygame.display.update()
if not start_button.isOver(mouse):
start_count = 0
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and start_button.isOver(mouse):
gameloop()
def Exit_Button():
global exit_count
mouse = pygame.mouse.get_pos()
exit_button = Button((255, 0, 255), 10, 425, 80, 40, 'EXIT')
exitwidth = exit_button.width
exitheight = exit_button.height
exit_text = exit_button.text
if exit_button.isOver(mouse):
exit_button.color = (255, 255, 255)
exit_button.width += 10
exit_button.height += 10
loop = 0
i = 1
if exit_count == 0:
while i < 5:
exit_button.text = exit_text[:i]
exit_button.draw(screen, 23)
time.sleep(0.08)
loop += 1
i += 1
exit_count += 1
pygame.display.update()
else:
exit_button.draw(screen, 23)
pygame.display.update()
else:
exit_button.height = exitheight
exit_button.width = exitwidth
exit_button.draw(screen, 20)
pygame.display.update()
if not exit_button.isOver(mouse):
exit_count = 0
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and exit_button.isOver(mouse):
pygame.quit()
quit()
# Welcome
def welcome():
run = True
global clock
while run:
screen.fill((0, 0, 0))
Start_button()
Exit_Button()
pygame.display.update()
clock.tick(60)
# Pause
def pause_game():
pause = True
while pause:
mixer.music.pause()
screen.fill((0, 0, 0))
text_on_screen('PAUSE', (255, 255, 255), 0, 0, 40)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
mixer.music.unpause()
pause = False
# Game Loop
def gameloop():
mixer.music.load('background.wav')
mixer.music.set_volume(50)
mixer.music.play(-1)
# Player
global bullet_state
playerX = 370
playerY = 500
player_changeX = 0
fps = 30
# Alien Invader
alienX = []
alienY = []
alien_changeX = []
number_of_aliens = 5
for i in range(number_of_aliens):
alienIMG.append(pygame.image.load('alien.png'))
alienX.append(random.randint(0, 735))
alienY.append(random.randint(0, 100))
alien_changeX.append(7)
# Bullet
bulletX = 0
bulletY = 500
bullet_changeY = 20
score = 0
running = True
while running:
screen.fill((0, 0, 0))
screen.blit(bgIMG, (0, 0))
for e in pygame.event.get():
if e.type == pygame.QUIT:
running = False
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_F4 and pygame.key.get_mods() & pygame.KMOD_ALT:
running = False
if e.key == pygame.K_LEFT or e.key == pygame.K_a:
player_changeX = -7
if e.key == pygame.K_RIGHT or e.key == pygame.K_d:
player_changeX = 7
if e.key == pygame.K_SPACE or e.key == pygame.K_RETURN:
bulletX = playerX
fire_bullet(bulletX, bulletY)
if e.key == pygame.K_ESCAPE:
pause_game()
if e.type == pygame.KEYUP:
if e.key == pygame.K_LEFT or e.key == pygame.K_RIGHT or e.key == pygame.K_a or e.key == pygame.K_d:
player_changeX = 0
playerX += player_changeX
if playerX < 0:
playerX = 0
elif playerX >= 736:
playerX = 736
if bulletY <= 0:
bulletY = 500
bullet_state = 'ready'
if bullet_state == 'fire':
fire_bullet(bulletX, bulletY)
bulletY -= bullet_changeY
for i in range(number_of_aliens):
if abs(alienY[i] - playerY) < 50 and abs(alienX[i] - playerX) < 67:
for h in range(number_of_aliens):
alienY[h] = 2000
game_over()
break
alienX[i] += alien_changeX[i]
if alienX[i] < 0:
alienX[i] = 0
alien_changeX[i] = 7
alienY[i] += 40
elif alienX[i] > 736:
alienX[i] = 736
alien_changeX[i] = -7
alienY[i] += 40
collisionbullet = isCollision(alienX[i], bulletX, alienY[i], bulletY)
if collisionbullet:
bulletY = 480
bullet_state = 'ready'
alienX[i] = random.randint(0, 735)
alienY[i] = random.randint(0, 120)
score += 10
alien(alienX[i], alienY[i], i)
player(playerX, playerY)
text_on_screen('Score:' + str(score), (0, 255, 0), 0, 0, 20)
clock.tick(fps)
pygame.display.update()
pygame.quit()
quit()
welcome()
Any ideas on how to fix this or where the problem is?
Only perform one display update at the end of the application loop, not multiple updates during the frame. Multiple updates of the display cause flickering. One single
pygame.display.update()
orpygame.display.flip()
at the end of the frame is sufficient.Remove all the (6) calls to
pygame.display.update()
fromStart_button
Exit_Button
and callpygame.display.update()
once inwelcome
.Code duplications like in
Start_button
Exit_Button
are a code smell. Avoid them. Implement a button class which can represent and animate the button. Removestart_count
andexit_count
, but add acount
attrubute toButton
:With this class you can simplify the function
welcome
a nd you don't need the functionsStart_button
Exit_Button
at all: