Нет причин использовать многопоточность в вашем коде. Это только сделает ваш код труднее для чтения, сложнее для отладки и подвержен ошибкам.
Обычно вы хотите, чтобы в вашей игре было какое-то состояние , которое вы используете для определения того, что должно происходить в кадре. Вы можете найти пример класса здесь .
Другой способ справиться с этим, который немного похож на ваш код, - использовать сопрограммы.
Посмотрите на код анимации и вместо вызова 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()
Посмотрите, как основной цикл чист и прост, а все игровое состояние обрабатывается в сопрограммах. Часть кода в титульном экране не заботится о полноэкранном режиме или о том, как переключаться в полноэкранный режим, а основной цикл не заботится о том, что делает сопрограмма титульного экрана. И нам не нужны потоки.
На практике это не сильно отличается от примера на основе классов, который я связал выше, но использование сопрограмм упрощает реализацию анимации экрана заголовка.
По сути, у вас есть бесконечный цикл, вы изменяете некое состояние (например, цвет буквы), а затем говорите: «Теперь нарисуйте это!» просто позвонив yield
.