Pygame的线程问题

时间:2018-11-08 16:15:45

标签: python multithreading pygame python-2.x

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

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

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

2 个答案:

答案 0 :(得分:1)

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

通常,您要在游戏中使用某种状态来确定帧中应该发生的情况。您可以找到基于类的示例here

处理此问题的另一种方法(与您的代码有点类似)是使用协程。

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

这是一个简单的示例:

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

查看主循环如何简洁明了,所有游戏状态都在协程中进行处理。该代码的标题屏幕部分不关心是否全屏显示或如何切换到全屏显示,并且主循环不关心标题屏幕协程的作用。而且我们不需要线程。

enter image description here

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

基本上,您有一个无限循环,您改变了某种状态(例如字母的颜色),然后说“现在画这个!”只需致电yield

答案 1 :(得分:0)

这是要调试的大量代码。

我对pygame或python线程不熟悉,但是在我看来,您需要包括一些调试行才能确定游戏动画线程中错误发生的确切位置(甚至根本就没有发生)。 / p>

类似这种模式应该可以帮助确定问题的根源:

import logging

logging.info("start animation initialization")
...
logging.info("begin animation loop")
...
logging.info("end animation loop")

https://docs.python.org/2/howto/logging.html