使用 Pygame 进行多线程 [英] Multithreading with Pygame

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

问题描述

我在编写一个使用线程的简单 Pygame 应用程序时遇到了一些问题.请记住,这是我编写的第一个多线程代码.

I'm having some trouble writing a simple Pygame application that uses threads. Please keep in mind that this is the first multithreaded code I've ever written.

情况是这样的.我正在编写一个简单的应用程序,它将在屏幕上绘制一些时髦的线条.我的问题是,当我绘制线条时,应用程序无法处理输入,因此我无法(例如)关闭窗口,直到线条完成.这是我原始代码的样子:

Here's the situation. I'm writing a simple app that will draw some funky lines to the screen. My problem is that while I'm drawing the lines, the app can't handle input, so I can't (for example) close the window until the lines are finished. This is what my original code looked like:

import time
import pygame
from pygame.locals import *

SIZE = 800

def main():
    screen = pygame.display.set_mode((SIZE, SIZE))
    for interval in xrange(50, 1, -5):
        screen.fill((0, 0, 0))
        for i in xrange(0, SIZE, interval):
            pygame.draw.aaline(screen, (255, 255, 255), (i+interval, 0), (0, SIZE-i))
            pygame.draw.aaline(screen, (255, 255, 255), (i, 0), (SIZE, i+interval))
            pygame.draw.aaline(screen, (255, 255, 255), (SIZE, i), (SIZE-i-interval, SIZE))
            pygame.draw.aaline(screen, (255, 255, 255), (SIZE-i, SIZE), (0, SIZE-i-interval))
            pygame.display.update()
            time.sleep(0.03)
        time.sleep(3)
    while True:
        for evt in pygame.event.get():
            if evt.type == QUIT:
                return

if __name__ == '__main__':
    pygame.init()
    main()
    pygame.quit()

如您所见,事件循环仅在绘制完成后运行,因此在此之前窗口关闭按钮无响应.我认为把绘图代码放到自己的线程中可能会有帮助,所以我把代码改成了这样:

As you can see, the event loop is only run when the drawing is done, so until then the window close button is unresponsive. I thought that putting the drawing code into its own thread might help, so I changed the code to this:

import threading, time
import pygame
from pygame.locals import *

SIZE = 800

def draw():
    screen = pygame.display.set_mode((SIZE, SIZE))
    for interval in xrange(50, 1, -5):
        screen.fill((0, 0, 0))
        for i in xrange(0, SIZE, interval):
            pygame.draw.aaline(screen, (255, 255, 255), (i+interval, 0), (0, SIZE-i))
            pygame.draw.aaline(screen, (255, 255, 255), (i, 0), (SIZE, i+interval))
            pygame.draw.aaline(screen, (255, 255, 255), (SIZE, i), (SIZE-i-interval, SIZE))
            pygame.draw.aaline(screen, (255, 255, 255), (SIZE-i, SIZE), (0, SIZE-i-interval))
            pygame.display.update()
            time.sleep(0.03)
        time.sleep(3)

def main():
    threading.Thread(target=draw).start()
    while True:
        for evt in pygame.event.get():
            if evt.type == QUIT:
                return

if __name__ == '__main__':
    pygame.init()
    main()
    pygame.quit()

但我得到的只是一个黑屏,它也不响应输入.我在这里做错了什么?

But all I get is a black screen which doesn't respond to input either. What am I doing wrong here?

推荐答案

尽管我从未使用过 pygame,但我怀疑您是否可以(或应该)从不同线程调用其 API.您的所有绘图都应在主事件循环中完成.

Although I have never used pygame, I doubt that you can (or should) call its API from different threads. All your drawing should be done in the main event loop.

我想您必须改变游戏开发的思维方式.不要使用 time.sleep() 来暂停绘图,而是创建一个可以定期更新的对象.通常,这分两遍完成——update() 及时推进对象状态,以及 draw() 渲染对象的当前状态.在主循环的每次迭代中,更新所有对象,然后绘制所有对象.请注意,draw() 应该假设屏幕是空白的,并绘制到当前时间所需的每一行.

I guess you have to change the way you are thinking for game development. Instead of using time.sleep() to pause the drawing, create an object that can be updated in regular intervals. Typically, this is done in two passes -- update() to advance the object state in time, and draw() to render the current state of the object. In every iteration of your main loop, all objects are updated, then all are drawn. Note that draw() should assume that the screen is blank and draw every line that is needed up to the current time.

在您的简单情况下,您可以使用更简单的方法.将 draw() 函数中的 time.sleep() 替换为 yield.通过这种方式,您将获得一个迭代器,该迭代器将产生推荐的等待下一次迭代的时间.在主循环之前,通过调用 draw() 创建一个迭代器并初始化下一次绘制发生的时间:

In your simple case, you could get away with something simpler. Replace the time.sleep() in your draw() function with yield. This way you get an iterator that will yield the recommended amount of time to wait until the next iteration. Before the main loop, create an iterator by calling draw() and initialize the time when the next draw should occur:

draw_iterator = draw()
next_draw_time = 0  # Draw immediately

然后,在主循环中,处理用户输入后,检查是否该绘制了:

Then, in the main loop, after handling user input, check if it's time to draw:

current_time = time.time()
if current_time >= next_draw_time:

如果是,运行下一次迭代并安排下一次绘制时间:

If so, run the next iteration and schedule the next draw time:

    try:
        timeout = next(draw_iterator)
    except StopIteration:
        # The drawing is finished, exit the main loop?
        break
    next_draw_time = current_time + timeout

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

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