python - Pygame的线程问题
问题描述
我正在开发一个用于学习目的的小游戏。我为标题屏幕创建了一个简单的动画。由于代码中还有一个全屏功能,我想创建一个标题屏幕:
- 显示了动画
- 激活键时变成全屏
- 在激活全屏之前的位置继续动画
为了做到这一点,我求助于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()
如果有人可以帮助我,我将不胜感激。我快疯了。
解决方案
没有理由在代码中使用线程。它只会使您的代码更难阅读,更难调试并且容易出错。
通常,您希望在游戏中拥有某种状态,用于确定帧中应该发生的事情。您可以在此处找到基于类的示例。
与您的代码有点相似的另一种处理方法是使用协程。
查看您的动画代码,而不是调用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
.
推荐阅读
- c - 如何在C中获取数组参数的实际大小?
- android - 使用 Firebase 作业调度程序运行服务直到下一个计划
- avl-tree - 如何平衡 AVL 树中的悬空节点
- javascript - 更改每个选项的占位符文本
- javascript - 如何使用struts和jquery将行的值传递给单选按钮
- android-studio - 更新到 Android Oreo 后出现奇怪的编辑框
- xamarin.forms - 如何更改 Xamarin.forms 中选项卡项的大小
- asp.net-core - 来自 jsonschema 文件的 Swashbuckle 远程定义
- c# - C# Gradebook 程序,从文本文件中读取数据
- java - Java JSP servlet 500 错误