Потоковая проблема с Pygame - PullRequest
0 голосов
/ 08 ноября 2018

Я разрабатываю небольшую игру для учебных целей. Я создал простую анимацию для титульного экрана. Поскольку в коде также есть функция для полноэкранного режима, я хотел создать титульный экран, который:

  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 голосов
/ 09 ноября 2018

Нет причин использовать многопоточность в вашем коде. Это только сделает ваш код труднее для чтения, сложнее для отладки и подвержен ошибкам.

Обычно вы хотите, чтобы в вашей игре было какое-то состояние , которое вы используете для определения того, что должно происходить в кадре. Вы можете найти пример класса здесь .

Другой способ справиться с этим, который немного похож на ваш код, - использовать сопрограммы.

Посмотрите на код анимации и вместо вызова 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.

0 голосов
/ 08 ноября 2018

Это большой кусок кода для отладки.

Я не знаком с pygame или python threading, но мне кажется, что вам нужно включить несколько строк отладки, чтобы точно определить, где происходит ошибка во время игрового потока анимации (если она вообще возникает там).

Что-то вроде этого шаблона должно помочь определить источник проблемы:

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

...