首页 > 解决方案 > Pygame的线程问题

问题描述

我正在开发一个用于学习目的的小游戏。我为标题屏幕创建了一个简单的动画。由于代码中还有一个全屏功能,我想创建一个标题屏幕:

  1. 显示了动画
  2. 激活键时变成全屏
  3. 在激活全屏之前的位置继续动画

为了做到这一点,我求助于threading. 然而,这是我第一次尝试做任何多线程,我不知道我做错了什么。结果是一个未确定的错误。

标题屏幕的代码是这样的:

try:
    GameAnimation = threading.Thread(target=GameTitleAnimation, (Window, WindowDimensions, FontDictionary, CurrentVersion))
    GameAnimation.start()
except:
    print "There was an error while loading the screen. Press one key to exit the program."
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            Quit()
        if event.type == pygame.KEYDOWN:
            if event.key == K_ESCAPE:
                Quit()
            elif event.key == K_f:
                Fullscreen(Window, WindowDimensions)
            else:
                return

动画的代码是:

TitleWhite = [255, 255, 255, 0]
Black = BASE_BLACK
TitleLetters = ("R", "O", "G", "U", "E", " ", "H", "U", "N", "T", "E", "R")
Title = FontDictionary["TitleFont"][1].render("ROGUE HUNTER", False, TitleWhite)
TextWidth = Title.get_width()
TextHeight = Title.get_height()
TitleXPosition = (WindowDimensions[0] - TextWidth) / 2
TitleYPosition = (WindowDimensions[1] / 2) - (TextHeight / 2)
for letter in TitleLetters:
    if letter == " ":
       TitleXPosition += CurrentLetterWidth
    else:
        while TitleWhite[3] < 100:
            TitleWhite[3] += 1
            CurrentLetter = FontDictionary["TitleFont"][1].render(letter, False, TitleWhite)
            CurrentLetter.set_alpha(TitleWhite[3])
            Window.blit(CurrentLetter, (TitleXPosition, TitleYPosition))
            time.sleep(0.008)
            try: 
                pygame.display.update()
            except Exception:
                traceback.print_exception
        TitleWhite[3] = 0
        CurrentLetterWidth = CurrentLetter.get_width()
        TitleXPosition += CurrentLetterWidth
FadeInSurface = pygame.Surface((WindowDimensions[0], WindowDimensions[1]))
FadeInSurface.fill(TitleWhite)
OpacityRounds = 1
while TitleWhite[3] < 100.0:
    TitleWhite[3] = 1.1 ** OpacityRounds
    FadeInSurface.set_alpha(TitleWhite[3])
    Window.blit(FadeInSurface, (0, 0))
    OpacityRounds += 1
    pygame.display.update()
    time.sleep (0.015)
time.sleep(0.7)  
TitleXPosition = (WindowDimensions[0] - TextWidth) / 2
Version = FontDictionary["BodyFont"][1].render(CURRENT_VERSION, False, TitleWhite)
VersionHeight = Version.get_height()
VersionWidth = Version.get_width()
VersionXPosition = (WindowDimensions[0] - VersionWidth) / 2
VersionYPosition = TitleYPosition + TextHeight
while True:
    pygame.draw.rect(Window, Black, (0, 0, WindowDimensions[0], WindowDimensions[1]), 0)
    Window.blit(Title, (TitleXPosition, TitleYPosition))
    Window.blit(Version, (VersionXPosition, VersionYPosition))
    pygame.display.update()

如果有人可以帮助我,我将不胜感激。我快疯了。

标签: pythonmultithreadingpygamepython-2.x

解决方案


没有理由在代码中使用线程。它只会使您的代码更难阅读,更难调试并且容易出错。

通常,您希望在游戏中拥有某种状态,用于确定帧中应该发生的事情。您可以在此处找到基于类的示例。

与您的代码有点相似的另一种处理方法是使用协程。

查看您的动画代码,而不是调用pygame.display.update(),将控制权交还给主循环。主循环将处理事件、帧限制和绘图,然后将控制权交还给协程(它跟踪自己的状态)。

这是一个简单的hacky示例:

import pygame
import pygame.freetype

pygame.init()
size = (640, 480)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()

def game_state(surf):
    rect = pygame.Rect(200, 200, 32, 32)
    while True:
        events = yield
        pressed = pygame.key.get_pressed()
        x = 1 if pressed[pygame.K_RIGHT] else -1 if pressed[pygame.K_LEFT] else 0
        rect.move_ip(x*5, 0)
        pygame.draw.rect(surf, pygame.Color('dodgerblue'), rect)
        yield

def title_state(surf):
    text = 'Awesome Game'
    colors = [[255, 255, 255, 20] for letter in text]
    font = pygame.freetype.SysFont(None, 22)
    font.origin = True
    while True:
        for color in colors:
            color[3] += 33
            if color[3] > 255: color[3] = 0
            x = 200
            for (letter, c) in zip(text, colors):
                bounds = font.get_rect(letter)
                font.render_to(surf, (x, 100), letter, c)
                x += bounds.width + 1

            font.render_to(surf, (180, 150), 'press [space] to start', pygame.Color('grey'))
            events = yield
            yield

def main():
    title = title_state(screen)
    game = game_state(screen)
    state = title

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_ESCAPE:
                    return
                if e.key == pygame.K_SPACE:
                    state = game if state == title else title
                if e.key == pygame.K_f:
                    if screen.get_flags() & pygame.FULLSCREEN:
                        pygame.display.set_mode(size)
                    else:
                        pygame.display.set_mode(size, pygame.FULLSCREEN)

        screen.fill(pygame.Color('grey12'))
        next(state)
        state.send(events)
        pygame.display.update()
        clock.tick(60)

if __name__ == '__main__':
    main()

看看主循环如何干净简单,所有游戏状态都在协程中处理。标题屏幕部分代码不关心是否全屏或如何切换到全屏,主循环也不关心标题屏幕协程做什么。而且我们不需要线程。

在此处输入图像描述

实际上,它与我上面链接的基于类的示例没有什么不同,但是使用协程可以轻松实现标题屏幕动画。

基本上你有一个无限循环,你改变一些状态(比如字母的颜色),然后说“现在画这个!” 只需调用yield.


推荐阅读