Python:Tkinter滞后

2024-09-20 06:28:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我有个问题。 我正在tkinter制作一个platformer游戏,我有一个问题: 我现在有:玩家,方块和硬币。 我正在更新玩家的移动,它的动画和硬币的动画,由于某些原因,当我放太多硬币时,玩家的移动开始滞后。 注:我使用了tkinter的after函数来制作玩家的动作+动画,硬币也一样。 对于其他的事情,比如重力等等,我只使用线程。在

硬币代码更新:

def coinsCheckCollision(self):
    cRemove = None
    indexRemove = -1
    count = 0
    for c in self.frame.coins:
        x, y , width , height = c.getRectangle()
        xP = self.player.getX; yP = self.player.getY; wP = self.player.getWidth; hP = self.player.getHeight
        if collisionDetect(xP , x, yP  , y, wP , width, hP , height) or collisionDetect(x , xP , y , yP , width , wP , height , hP):
            if count not in coinsRemoved:
                indexRemove = count
        if indexRemove != -1:
            if indexRemove not in coinsRemoved:
                coinsRemoved.append(indexRemove)
        count +=1

def coinsUpdateAnimations(self):
    count = 0
    for c in self.frame.coins:
        if count not in coinsRemoved:
            self.img = c.getAnimation()
            self.img = ImageTk.PhotoImage(self.img)
            self.frame.coinsImages[count] = self.img
        else:
            if self.frame.coinsImages[count] is not '' :
                self.frame.coinsImages[count] = ''
                self.frame.canvas.delete('coinB'+str(count))
        what = self.frame.canvas.itemconfig('coin' + str(count), image=self.frame.coinsImages[count])
        count += 1
    self.coinsCheckCollision()
    self.frame.frame.after(40 , self.coinsUpdateAnimations)

总之,问题是:为什么当我更新多个彼此不“相关”的东西时,gui开始滞后?在


Tags: inselfimgifcount玩家not动画
1条回答
网友
1楼 · 发布于 2024-09-20 06:28:32

你的设计似乎希望你的功能每40毫秒运行一次,也许+/-几毫秒,但平均每秒运行25次。在

但事实并非如此。在


首先,你有多少硬币,这个函数有多复杂?如果只需要1毫秒的一小部分时间来完成这个循环,那没什么大不了的,但是想想如果它需要15毫秒,会发生什么:你先等40毫秒,然后做15毫秒的工作,再等40毫秒,然后再做15毫秒的工作,等等。所以你的工作每秒只运行15次,而不是25次。在

现在想象一下,每枚硬币需要0.2毫秒,在3枚硬币上,有0.6毫秒的滞后,这几乎是不明显的。但是在100个硬币时,会有20毫秒的延迟,这会使硬币减速50%,这是非常明显的。在


其次,正如the docs所说:

Tkinter only guarantees that the callback will not be called earlier than that; if the system is busy, the actual delay may be much longer.

在任何一个方向上随机关闭几毫秒都没问题,最终都会是平均值。但是after总是晚几毫秒,从来不会早几毫秒,所以它不是平均值,而是不断累积,你会越来越落后。在

更糟糕的是,如果你的某个函数落后了,它会使每个after的延迟时间变长,所以它不仅仅是你的硬币动画减速50%,而是整个游戏的速度在0-50%之间任意地降低一些不可预测的数量,但可能足以引起注意。在


要解决这两个问题,您需要保留一些类似于您预期运行的时间,然后,您不必执行after(40)的操作,而是执行以下操作:

expected_time += 40
delay = expected_time - current_time
after(max(0, delay), func)

使用the ^{} module将其具体化(尽管未经测试):

^{pr2}$

当然,如果你所做的总工作量超过40毫秒,这仍然不能解决问题。假设你花了50毫秒,然后做了一个after(0, func),它至少晚了10毫秒,然后又花了50毫秒,然后下一个after(0, func)至少晚了20毫秒,依此类推。如果你不能在小于40ms的时间内完成所有的工作,你将无法跟上。你必须:

  • 找到一种方法来优化你的代码(例如,也许你可以使用一个更好的算法,或者用numpy代替for循环)
  • 重新设计游戏以减少工作量,或者
  • 把你的帧速率降到你能跟上的速度。在

一个可能更好的解决方案是停止试图把Tkinter变成一个游戏框架。它不是为这个而设计的,不能帮助你把所有的细节都弄好,即使你把它们弄对了,也不能很好地工作。在

相比之下,像Pygame Zero这样的东西,顾名思义,是为创建游戏而设计的。而且设计的目的是使它足够简单,让那些对Python的经验比你想象的要少得多的人可以使用它。在

例如,PyGameZero不是一个事件循环,它以任何你的操作系统想要的速度运行,让你的责任是让所有的时间都正确,PyGameZero运行一个帧循环,该循环每秒调用你的update函数N次,尽可能接近均匀。它有内置的功能,如碰撞检测,绘制动画精灵等

相关问题 更多 >