我可以用 Tkinter 流畅地写出 运行 贪吃蛇游戏吗?
Can I write a smoothly running snake game with Tkinter?
只是为了好玩,我尝试在 Python/Tkinter 中实现经典的贪吃蛇游戏。感觉很慢,我不太确定我是否可以解决这个问题。我试着给出一个简短的程序大纲:
我首先构建 canvas 并为每个箭头键添加一个事件处理程序:
root = self.root = Tk()
canvas = Canvas(root, width = width, height = height)
canvas.pack()
canvas.bind("<Left>", on_left) # on_left is a very short function
我还有一个时间步函数。它绘制图形并安排自己再次被调用。该函数效率不高,每次重绘100个矩形:
def timestep(self):
# draw the graphics here (about 100 filled rectangles)
timer = threading.Timer(interval, timestep)
timer.start()
现在,我有两个问题:
- 即使我在
timestep()
中选择 interval
大约 0.05(相当于每秒 20 帧),我也不会超过每秒 3-5 帧。
- 键盘控制输入好像延迟了0.5秒左右
我想知道三件事:
当您想在每个时间步绘制 100 个矩形时,是否有可能使用 Tkinter canvas 编写一条平滑、无滞后 (20 fps) 的蛇?
threading.Timer
是调用时间步函数的正确选择吗?
为什么我的键盘输入似乎有延迟?
如果你没有在每帧之间使用 canvas.delete(...),你就会有内存泄漏。
尝试使用内置 canvas.after(funciton,interval)
而不是 threading.timer。
现有答案并没有准确描述我最终是如何解决问题的,所以我将在这里回答我自己的问题:
主要问题是 canvas.create_rectangle(...)
这样的函数实际上会在每次调用时添加一个新的矩形。因此,您将 运行 陷入性能问题,因为即使在很短的时间内,您的矩形也太多了。
您可以使用canvas.delete(...)
删除部分或全部矩形。如果在每个时间步删除所有现有矩形并创建新矩形,性能还可以,但屏幕会闪烁。
最好的方法是在初始化时创建一次矩形,然后在每个时间步只改变它们的颜色:
# on startup:
rect[x][y] = canvas.create_rectangle(...)
# in each timestep
canvas.itemconfig(rect[x][y], fill = myColor)
有了它,我获得了良好的性能并且没有闪烁,即使我仍然每秒更改 100 个矩形的颜色 20 次。
是的,您可以创建一个流畅的 运行ning 游戏,屏幕上有很多对象,而且您可以在没有线程的情况下做到这一点。不过,这取决于您如何定义 "smooth" 和 "many",并且取决于您如何实现它。
如果您有一种算法可以在每次迭代时删除并重新创建场景中的所有内容,它将 运行 非常缓慢。做动画的正确方法是创建你想要动画一次的对象,而且只创建一次。然后,对于每一帧,您可以使用 canvas 方法 itemconfigure
、move
和 coords
重新配置或移动它们。
我已经看到 canvas 上的数百个项目都能正常工作。但是,如果您有数千个,它就会开始陷入困境。此外,canvas 在您创建数千个项目时存在已知的性能问题,即使您删除了旧项目也是如此。
因此,与许多与性能相关的问题一样,部分归结为最终算法。例如,在蛇游戏中,您可以选择在每一帧重绘整条蛇,或者您可以查看问题并意识到您只需移动蛇的一两段。第一种方法会很慢,第二种方法很快
底线是 tkinter canvas 足够强大,在使用适当的算法实现时,可以一次平滑地为几十个项目制作动画。
只是为了好玩,我尝试在 Python/Tkinter 中实现经典的贪吃蛇游戏。感觉很慢,我不太确定我是否可以解决这个问题。我试着给出一个简短的程序大纲:
我首先构建 canvas 并为每个箭头键添加一个事件处理程序:
root = self.root = Tk()
canvas = Canvas(root, width = width, height = height)
canvas.pack()
canvas.bind("<Left>", on_left) # on_left is a very short function
我还有一个时间步函数。它绘制图形并安排自己再次被调用。该函数效率不高,每次重绘100个矩形:
def timestep(self):
# draw the graphics here (about 100 filled rectangles)
timer = threading.Timer(interval, timestep)
timer.start()
现在,我有两个问题:
- 即使我在
timestep()
中选择interval
大约 0.05(相当于每秒 20 帧),我也不会超过每秒 3-5 帧。 - 键盘控制输入好像延迟了0.5秒左右
我想知道三件事:
当您想在每个时间步绘制 100 个矩形时,是否有可能使用 Tkinter canvas 编写一条平滑、无滞后 (20 fps) 的蛇?
threading.Timer
是调用时间步函数的正确选择吗?为什么我的键盘输入似乎有延迟?
如果你没有在每帧之间使用 canvas.delete(...),你就会有内存泄漏。
尝试使用内置 canvas.after(funciton,interval)
而不是 threading.timer。
现有答案并没有准确描述我最终是如何解决问题的,所以我将在这里回答我自己的问题:
主要问题是 canvas.create_rectangle(...)
这样的函数实际上会在每次调用时添加一个新的矩形。因此,您将 运行 陷入性能问题,因为即使在很短的时间内,您的矩形也太多了。
您可以使用canvas.delete(...)
删除部分或全部矩形。如果在每个时间步删除所有现有矩形并创建新矩形,性能还可以,但屏幕会闪烁。
最好的方法是在初始化时创建一次矩形,然后在每个时间步只改变它们的颜色:
# on startup:
rect[x][y] = canvas.create_rectangle(...)
# in each timestep
canvas.itemconfig(rect[x][y], fill = myColor)
有了它,我获得了良好的性能并且没有闪烁,即使我仍然每秒更改 100 个矩形的颜色 20 次。
是的,您可以创建一个流畅的 运行ning 游戏,屏幕上有很多对象,而且您可以在没有线程的情况下做到这一点。不过,这取决于您如何定义 "smooth" 和 "many",并且取决于您如何实现它。
如果您有一种算法可以在每次迭代时删除并重新创建场景中的所有内容,它将 运行 非常缓慢。做动画的正确方法是创建你想要动画一次的对象,而且只创建一次。然后,对于每一帧,您可以使用 canvas 方法 itemconfigure
、move
和 coords
重新配置或移动它们。
我已经看到 canvas 上的数百个项目都能正常工作。但是,如果您有数千个,它就会开始陷入困境。此外,canvas 在您创建数千个项目时存在已知的性能问题,即使您删除了旧项目也是如此。
因此,与许多与性能相关的问题一样,部分归结为最终算法。例如,在蛇游戏中,您可以选择在每一帧重绘整条蛇,或者您可以查看问题并意识到您只需移动蛇的一两段。第一种方法会很慢,第二种方法很快
底线是 tkinter canvas 足够强大,在使用适当的算法实现时,可以一次平滑地为几十个项目制作动画。