Python tkinter 性能问题

标签 python tkinter

我想创建一个基于 block 物理的游戏,但是 fps 下降得非常快。 我尝试尽可能地优化它,但它的 fps 仍然相当低(200 block 给我大约 20 fps)。有谁知道如何优化我的 block 游戏?我本来打算添加更多内容,但看到这个问题我可能会放弃。

这是我的代码:

from tkinter import *
import math
import random
import time


master = Tk()

w = Canvas(master,width=600,height=600)
w.pack()

tileSize = 20

def randomColor():
    r = math.floor(random.random() * 255)
    g = math.floor(random.random() * 255)
    b = math.floor(random.random() * 255)
    return '#%02x%02x%02x' % (r, g, b)

class newElement:
    def __init__(self,x,y):
        x = math.floor(x/tileSize) * tileSize
        y = math.floor(y/tileSize) * tileSize
        self.x = x
        self.y = y
        self.vx = 0
        self.vy = 0
        self.width = tileSize
        self.height = tileSize
        self.id = random.random()
        self.color = randomColor()

elements = []

def mouse(event):
    global mouseX,mouseY
    mouseX,mouseY = event.x,event.y

    canPlace = True

    for y in range(0,len(elements)):
        e = elements[y]
        dx = e.x + e.width/2 - mouseX
        dy = e.y + e.height/2 - mouseY
        if math.sqrt(dx*dx+dy*dy) < 20:
            canPlace = False
            break

    if canPlace:
        elements.append(newElement(mouseX,mouseY))

mouseX = 0
mouseY = 0

w.bind("<B1-Motion>",mouse)

elements.append(newElement(300,50))
elements.append(newElement(300,100))

def collision(rect1,rect2):
   return rect1.x < rect2.x + rect2.width and rect1.x + rect1.width > rect2.x and rect1.y < rect2.y + rect2.height and rect1.height + rect1.y > rect2.y

def distance(rect1,rect2):
    dx = rect1.x - rect2.x
    dy = rect2.y - rect2.y
    return math.sqrt(dx*dx+dy*dy)

lastCall = time.time()

def engine():
    global lastCall
    w.delete("all")

    w.create_line(0,590,600,590)

    for i in range(0,len(elements)):
        e = elements[i]

        gravity = 0.1

        e.vy += gravity

        e.x += e.vx
        e.y += e.vy

        if e.y + e.height >= 590:
            e.y = 590-e.height
            e.vy = 0

        for x in range(0,len(elements)):
            e1 = elements[x]
            if e.id == e1.id or distance(e,e1) > 14.14:
                continue
            col = collision(e,e1)
            if col:
                e.y = e1.y - e1.height
                e.vy = 0

        w.create_rectangle(e.x,e.y,e.x+e.width,e.y+e.height,fill=e.color)

    w.create_text(10,10,anchor=NW,text=len(elements))
    fps = 60
    if time.time() - lastCall != 0:
        fps = round(1/(time.time() - lastCall))
    w.create_text(600,10,anchor=NE,text=fps)

    lastCall = time.time()

    master.after(16,engine)
engine()

mainloop()

最佳答案

Tkinter 能够为这样的简单程序实现 60fps,但性能会根据您选择的算法而降低。我概述了您可以采取的几项措施来提高代码的性能。

当我编写一个与您的程序类似的程序但进行了下面概述的更改时,我能够在包含超过 1000 个项目的情况下获得 60fps 的速度。

不要删除和重画

最大的问题是您每秒要多次删除和创建矩形。这是非常低效的。更糟糕的是,一旦您创建了数千个项目,即使您后来删除了它们, Canvas 也会出现性能问题。我已经成功地为数千个项目制作了动画,但如果您创建了数万个项目,它可能会开始变得缓慢。

您应该做的是创建一次项目,然后为每个项目提供一个 move 方法,以便移动现有项目而不是删除然后重新创建它。

不要移动不需要移动的项目

第二个问题是您在每一帧中移动所有对象,即使它们的速度为零。不需要对不会移动的物体进行计算。

让 tkinter 为您找到碰撞

第三个问题是您查找冲突的算法效率极低。如果您有 300 个对象,则需要进行 300x300 的比较。例如,您将检查项目 1 是否与项目 2 碰撞,然后是项目 3,然后是项目 4,等等。然后,您检查项目 2 是否与项目 1、项目 3、项目 4 等发生碰撞。因为您已经已经确定项目 1 和 2 是否发生碰撞,没有理由查看项目 2 和 1 是否发生碰撞。另外,由于项目总是直接向下移动,因此您实际上只需要检查与当前项目正下方的项目的碰撞。

您可以通过让 tkinter 为您完成工作来解决碰撞问题。给定一个对象,您可以获得与 find_overlapping 重叠的所有对象的列表。 Canvas 的方法。这可能比将每个对象与每个对象进行比较要快几个数量级,因为它是由 tkinter 在内部 Canvas 数据结构上内部完成的(即:它是使用 C 代码而不是 Python 代码)。

关于Python tkinter 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47556992/

相关文章:

python - D-lib 物体检测器训练

python - 虽然不在列表中

python - 在同一脚本中读取和写入

perl - 限制滚动条长度

python - 为什么我的程序使用 .py 扩展名但不能使用 .pyw 扩展名?

python - 如何删除复选框小部件 tkinter 周围的浅灰色?

python - 除非我手动调整窗口大小,否则 Selenium 将看不到元素

python - 变量的符号链接(symbolic link)

python - tkinter.Button 在退出事件处理后保留按下状态的外观

python - 关闭文件对话框后 Tkinter 窗口不关闭