主屏幕上的按钮在 pygame 中闪烁

Button on main screen flickering in pygame

我正在制作游戏。因此,在我的主菜单上,我有两个用于播放和退出的按钮。 但是,无论我稍后调用哪个按钮,即,如果我调用开始按钮的功能然后调用退出按钮,反之亦然,第二个调用的按钮开始闪烁。 这是我的代码:

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

关于如何解决这个问题或问题出在哪里有什么想法吗?

仅在应用程序循环结束时执行一次显示更新,而不是在帧期间执行多次更新。显示的多次更新会导致闪烁。帧末尾一个pygame.display.update() or pygame.display.flip()就足够了。

删除 Start_button Exit_Buttonpygame.display.update() 的所有 (6) 次调用,并在 welcome.

中调用一次 pygame.display.update()

Start_button Exit_Button 中的代码重复是一种代码味道。避开他们。实现一个按钮 class,它可以表示按钮并为其设置动画。删除 start_countexit_count,但将 count 属性添加到 Button:

class Button:
    def __init__(self, color, x, y, width, height, text=''):
        self.color = color
        self.text = text
        self.rect = pygame.Rect(x, y, width, height)
        self.normal = pygame.Surface((width, height))
        self.normal.fill(self.color) 
        self.highlight = [pygame.Surface((width, height)), pygame.Surface((width + 10, height + 10))]
        self.highlight[0].fill((255, 255, 255))
        self.highlight[1].fill((255, 255, 255))
        small_font = pygame.font.Font('Stargaze Regular.otf', 20)
        large_font = pygame.font.Font('Stargaze Regular.otf', 23)
        text_small = small_font.render(self.text, 1, (0, 0, 0))
        text_large = large_font.render(self.text, 1, (0, 0, 0))
        self.normal.blit(text_small, text_small.get_rect(center = self.normal.get_rect().center))
        self.highlight[0].blit(text_small, text_small.get_rect(center = self.highlight[0].get_rect().center))
        self.highlight[1].blit(text_large, text_large.get_rect(center = self.highlight[1].get_rect().center))
        self.count = 0

    def draw(self, win, pos, outline = None):
        text_surf = self.normal
        if self.isOver(pos):
            text_surf = self.highlight[0]
            self.count += 1
            if self.count < 200:
                text_surf = self.highlight[self.count // 20 % len(self.highlight)]              
        else:
           self.count = 0
        text_rect = text_surf.get_rect(center = self.rect.center) 
        if outline:
            pygame.draw.rect(win, outline, text_rect.inflate(2, 2), 2)
        win.blit(text_surf, text_rect)

    def isOver(self, pos):
        return self.rect.collidepoint(pos)

有了这个 class 你可以简化函数 welcome 而你根本不需要函数 Start_button Exit_Button:

def welcome():
    exit_button = Button((255, 0, 255), 10, 425, 80, 40, 'EXIT')
    start_button = Button((255, 0, 255), 10, 370, 80, 40, 'PLAY')

    run = True
    while run:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                if exit_button.isOver(event.pos):
                    run = False
                if start_button.isOver(event.pos):
                    gameloop()
        mouse = pygame.mouse.get_pos()

        screen.fill((0, 0, 0))
        exit_button.draw(screen, mouse)
        start_button.draw(screen, mouse)
        pygame.display.update()

    pygame.quit()
    quit()