Pygame 的线程问题 [英] Threading issue with Pygame

查看:61
本文介绍了Pygame 的线程问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  1. 显示动画
  2. 当按键被激活时变成全屏
  3. 在激活全屏之前继续播放动画

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

标题画面的代码是这样的:

尝试:GameAnimation = threading.Thread(target=GameTitleAnimation, (Window, WindowDimensions, FontDictionary, CurrentVersion))GameAnimation.start()除了:打印加载屏幕时出错.按一键退出程序."为真:对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:放弃()如果 event.type == pygame.KEYDOWN:如果 event.key == K_ESCAPE:放弃()elif event.key == K_f:全屏(窗口,窗口尺寸)别的:返回

动画代码为:

TitleWhite = [255, 255, 255, 0]黑色 = BASE_BLACKTitleLetters = ("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)/2TitleYPosition = (WindowDimensions[1]/2) - (TextHeight/2)对于 TitleLetters 中的字母:如果字母==":TitleXPosition += CurrentLetterWidth别的:而 TitleWhite[3] <100:标题白[3] += 1CurrentLetter = FontDictionary["TitleFont"][1].render(letter, False, TitleWhite)CurrentLetter.set_alpha(TitleWhite[3])Window.blit(CurrentLetter, (TitleXPosition, TitleYPosition))时间.睡眠(0.008)尝试:pygame.display.update()除了例外:traceback.print_exception标题白[3] = 0CurrentLetterWidth = CurrentLetter.get_width()TitleXPosition += CurrentLetterWidthFadeInSurface = pygame.Surface((WindowDimensions[0], WindowDimensions[1]))FadeInSurface.fill(TitleWhite)OpacityRounds = 1而 TitleWhite[3] <100.0:TitleWhite[3] = 1.1 ** OpacityRoundsFadeInSurface.set_alpha(TitleWhite[3])Window.blit(FadeInSurface, (0, 0))OpacityRounds += 1pygame.display.update()睡眠时间 (0.015)时间.睡眠(0.7)TitleXPosition = (WindowDimensions[0] - TextWidth)/2版本 = FontDictionary["BodyFont"][1].render(CURRENT_VERSION, False, TitleWhite)VersionHeight = Version.get_height()VersionWidth = Version.get_width()VersionXPosition = (WindowDimensions[0] - VersionWidth)/2VersionYPosition = TitleYPosition + TextHeight为真:pygame.draw.rect(Window, Black, (0, 0, WindowDimensions[0], WindowDimensions[1]), 0)Window.blit(Title, (TitleXPosition, TitleYPosition))Window.blit(版本, (VersionXPosition, VersionYPosition))pygame.display.update()

如果有人能帮助我,我将不胜感激.我快疯了.

解决方案

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

通常,您希望在您的游戏中拥有某种状态,用于确定帧中应该发生什么.您可以在

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

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

I am developing a small game for learning purposes. I have created a simple animation for the title screen. Since there is also a function for full screen in the code, I wanted to create a title screen that:

  1. Displayed the animation
  2. Turned into full screen when the key was activated
  3. Continued the animation at the point it was before activating full screen

In order to do this, I resorted to threading. However, this is the first time I tried to do any multi-threading, and I don´t know what did I do wrong. The result is an undetermined error.

The code for the title screen is this:

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

The code for the animation is:

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

I'd be very grateful if anyone could help me with this. I am going crazy.

解决方案

There's no reason to use threading in your code. It will only make your code harder to read, harder to debug and error prone.

Usually you want to have some kind of state in your game that you use to determinate what should happen in a frame. You can find a class based example here.

Another way to handle this, which is a bit similar to your code, is to use coroutines.

Look at your animation code and instead of calling pygame.display.update(), give the control back to the main loop. The main loop will handle events, frame limiting and drawing, then give control back to the coroutine (which keeps track of it's own state).

Here's a simple hacky example:

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

See how the main loop is clean and simple, and all of the game state is handled in the coroutines. The title screen part of the code does not care about fullscreen or not or how to switch to fullscreen, and the main loop does not care of what the title screen coroutine does. And we don't need threading.

In practice it's not that different from the class based example I linked above, but using coroutines makes it easy to implement the title screen animation.

Basically you have an endless loop, you mutate some state (like the color of a letter), and then say "now draw this!" by just calling yield.

这篇关于Pygame 的线程问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆