Python: Tkinter лаг - PullRequest
       6

Python: Tkinter лаг

0 голосов
/ 30 мая 2018

У меня есть вопрос.Я делаю платформерную игру в tkinter, и у меня есть проблема: у меня пока есть: игрок, блоки и монеты.Я обновляю ход игрока, его анимацию и анимацию монеты, и по какой-то причине, когда я кладу слишком много монет, движение игрока начинает отставать.Примечание: я использую функцию после tkinter для анимации движения игрока + анимация и то же самое касается монет.Для других вещей, таких как гравитация и т. Д. Я использую только нити.

код обновления монет:

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)

В любом случае, вопрос вкратце: почему, когда я обновляю несколько вещей, которые на самом деле не «связаны» друг с другом, графический интерфейс пользователяначинает отставать?

1 Ответ

0 голосов
/ 30 мая 2018

Кажется, ваш дизайн ожидает, что ваши функции будут работать каждые 40 мс.Может быть +/- несколько мс, но в среднем 25 раз в секунду.

Но это не то, что происходит.


Во-первых, сколько у вас монет и насколько это сложно?collisionDetect функция?Если для прохождения этого цикла требуется всего лишь небольшая доля в 1 мс, это не страшно, но подумайте, что произойдет, если, скажем, 15 мс: вы ждете 40 мс, затем делаете 15 мс, затем ждете еще 40 мс, а затем делаете15 мс работы и т. Д. Итак, ваша работа выполняется только 15 раз в секунду вместо 25.

Теперь представьте, что каждая монета занимает, скажем, 0,2 мс.При 3 монетах наблюдается отставание в 0,6 мс, что едва заметно.Но при 100 монетах наблюдается отставание в 20 мс.Это замедляет монеты на 50%, что довольно заметно.


Во-вторых, как документы говорят:

Tkinter гарантирует только то, чтообратный вызов не будет вызываться раньше этого;если система занята, фактическая задержка может быть намного больше.

Может быть нормально отключиться на несколько мс в произвольном направлении в любом направлении;в итоге все бы усреднилось.Но after всегда на несколько мс позже, а не на несколько мс раньше, поэтому вместо усреднения он просто накапливается, и вы все больше и больше отстаете.

И, что еще хуже, если одна из ваших функцийотстает, будет иметь тенденцию увеличивать задержку в каждом after немного дольше - так что не только анимация вашей монеты замедляется на 50%, но и вся игра замедляется на какую-то непредсказуемую величину произвольно между 0-50%, но, вероятно, достаточно, чтобы быть заметным.


Чтобы решить обе проблемы, вам нужно иметь при себе что-то вроде времени, которое вы ожидали запустить, затем вместо after(40) вы делаете что-то вродеэто:

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

Чтобы выразить это в конкретных (хотя и не проверенных) условиях, используя модуль datetime :

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 мс, вы не сможете идти в ногу.Вы должны либо:

  • Найти способ оптимизировать свой код (например, возможно, вы можете использовать лучший алгоритм или использовать numpy вместо цикла for),
  • Переработайте свою игру, чтобы выполнять меньше работы, или
  • Уменьшите частоту кадров до того уровня, с которым вы действительно сможете справиться.

Возможно, лучшим решением будет прекратить попыткисогнуть Tkinter в игровую среду.Он не предназначен для этого, не помогает вам правильно продумать все мелкие детали и не очень хорошо работает, даже если вы правильно их поняли.

Напротив, что-то вроде Pygame Zero, как следует из названия, предназначен для создания игр.И разработан для того, чтобы сделать его достаточно простым, чтобы его могли использовать люди с гораздо меньшим опытом работы с Python, чем у вас.

Например, вместо цикла обработки событий, который запускается с той скоростью, с которой ваша ОС хочет его запустить,возлагая на вас ответственность за то, чтобы все было правильно рассчитано, Pygame Zero запускает цикл кадров, который вызывает вашу update функцию N раз в секунду, как можно ближе к равномерному, насколько это возможно.И он имеет встроенные функции для таких вещей, как обнаружение столкновений, рисование анимированных спрайтов и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...