我有一个问题。 我在tkinter做一个平台游戏,我有一个问题: 我现在有:玩家,积木和硬币。 我正在更新玩家的移动,它是动画和硬币的动画,出于某种原因,当我投入太多硬币时,玩家的动作开始滞后。 注意:我正在使用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会开始滞后?
答案 0 :(得分:2)
您的设计似乎希望您的功能每40毫秒运行一次。也许+/-几毫秒,但平均每秒25次。
但事实并非如此。
首先,你有多少硬币,collisionDetect
功能有多复杂?如果只需要1ms的一小部分来完成该循环,那就没什么大不了的了,但想想如果需要15毫秒会发生什么:你等待40毫秒,然后做15毫秒的工作,然后等待另一个40ms,然后做15ms的工作等等。所以你的工作每秒只运行15次,而不是25次。
现在假设每枚硬币花费0.2毫秒。 3枚硬币的延迟为0.6毫秒,几乎没有明显的变化。但是在100枚硬币的情况下,它的延迟时间为20毫秒。这会使硬币减速50%,这显然是显而易见的。
第二,正如the docs所说:
Tkinter只保证不会在此之前调用回调;如果系统繁忙,实际延迟可能会更长。
在任一方向上随机关闭几毫秒可能没问题;它最终会平均化。但是after
总是迟到几毫秒,从来没有提前几毫秒,所以不是平均,而是建立起来,你会越走越远。
而且,更糟糕的是,如果你的某个功能落后,它会使每个after
的延迟时间更长 - 所以它不会让你的硬币动画减速50%,但整场比赛在0-50%之间随意放慢了一些不可预测的数量,但可能足以引人注意。
要解决这两个问题,你需要随身携带一些类似你预期运行的时间,然后,不要做after(40)
,你可以这样做:
expected_time += 40
delay = expected_time - current_time
after(max(0, delay), func)
将其置于具体(虽然未经测试)的术语中
def __init__(self):
self.next_frame_time = datetime.datetime.now()
self.schedule()
def schedule(self):
self.next_frame_time += datetime.timedelta(seconds=0.040)
now = datetime.datetime.now()
delta = max(datetime.timedelta(), now - self.next_frame_time)
self.frame.frame.after(delta.total_seconds * 1000, self.coinsUpdateAnimations)
def coinsUpdateAnimations(self):
# all the existing code before the last two lines
self.coinsCheckCollision()
self.schedule()
当你完成的工作总时间超过40毫秒时,这仍然无法解决问题。想象一下,你花费50毫秒,然后做一个after(0, func)
,它至少触发10毫秒,然后再花50毫秒,然后下一个after(0, func)
触发至少20毫秒,依此类推。如果你不能在通常远低于40毫秒的时间内完成所有工作,那么你将无法跟上。你必须要么:
for
循环),一个可能更好的解决方案是停止尝试将Tkinter弯曲到游戏框架中。它不是为此而设计的,并不能帮助你正确地获得所有细节,即使你做对了也不能很好地工作。
相比之下,顾名思义,像Pygame Zero这样的东西是为创造游戏而设计的。并且旨在让那些拥有比你似乎拥有的Python经验少得多的人能够轻松使用它。
例如,不是一个以你的操作系统想要运行它的任何速度运行的事件循环,而是让你有责任让所有时间都正确,Pygame Zero运行一个框架循环,调用你的update
函数N次每秒,尽可能接近均匀。它还具有内置函数,用于碰撞检测,绘制动画精灵等等。