Как заставить объекты двигаться в Pygame, не замедляя рендеринг? - PullRequest
0 голосов
/ 14 апреля 2020

В настоящее время я разрабатываю приложение с использованием pygame, в котором у меня есть несколько кругов, соединенных линиями, с написанным на них числовым текстом. Эти круги зеленого, синего и красного цвета, а остальные черные. Фон белый. (Представьте, что это сетевой график)

Моя цель : я пытаюсь запустить анимацию, в которой пользователь выбирает два круга (давайте назовем их узлами), и я нахожу из кратчайшего пути между отправляющим узлом (зеленый) к принимающему узлу (красный). Итак, в этой анимации я делаю еще один движущийся круг поверх линии (или ребра), который соединяет два соседних узла (это могут быть промежуточные узлы).

for e.g, the circle moves from node 2 to node 11 here

Пока все хорошо, вот код того, что я делаю:

def runPathAnimation(path, colortype):
    for i in range(len(path)-1):
        #Calculation of the center of the nodes
        x1, y1 = (gmd[path[i]].getNodePosition())[0], (gmd[path[i]].getNodePosition())[1]
        x2, y2 = (gmd[path[i+1]].getNodePosition())[0], (gmd[path[i+1]].getNodePosition())[1]
        #Get the slope
        m = (y1-y2)/(x1-x2) if x1 != x2 else 'undefined'
        if str(m) != 'undefined':
            c = y2-(m*x2)
            if m > 0.5 or (m <= -1 and m >= -1.5):
                for y in range(min(y1,y2),max(y1,y2)):
                    #using the equation of the line
                    x = int((y-c)/m)
                    #redrawEverything(path)                                     #OPTION 1
                    #TRY REDRAW LINE                                            #TODO
                    pyg.draw.rect(screen, (255, 255, 255), (x-10,y-10,20,20))   #OPTION 2
                    pyg.draw.circle(screen, colortype, (x,y), 10)               #Moving circle
                    pyg.display.update()                                        #Update Display
                    #NEED: Redraw!
            #The logic repeats....
            else:
                for x in range(min(x1,x2),max(x1,x2)):
                    y = int(m*x+c)
                    #redrawEverything(path)
                    #TRY REDRAW LINE
                    pyg.draw.rect(screen, (255, 255, 255), (x-10,y-10,20,20))
                    pyg.draw.circle(screen, colortype, (x,y), 10)
                    pyg.display.update()
                    #NEED: Redraw!
        else:
            cy = range(min(y1,y2),max(y1,y2))
            if y1 > y2:
                cy = reversed(cy)
            for y in cy:
                #redrawEverything(path)
                #TRY REDRAW LINE
                pyg.draw.rect(screen, (255, 255, 255), (x1-10,y-10,20,20))
                pyg.draw.circle(screen, colortype, (x1,y), 10)
                pyg.display.update()
                #NEED: Redraw!

Моя проблема : Мой метод простого обновления сильно отстает круг с другой позицией, не нарушая ничего, что он покрывает. У меня было 2 варианта:

  1. ВАРИАНТ 1 : обновить все на экране (конечно, это не дало мне хорошую производительность)
  2. ВАРИАНТ 2 : обновить только ту часть экрана, которая фактически используется. Однако даже при использовании этого метода я не могу добиться хорошей производительности при обновлении экрана. Позже я хотел бы добавить функцию для управления скоростью анимации, которая может иметь скорость, превышающую максимальную производительность моего кода прямо сейчас!

Как видите, у меня нет любой time.sleep() на данный момент. Я хотел бы повысить производительность своего кода и затем добавить time.sleep() для более контролируемой анимации. Мое текущее приложение Pygame уже работает параллельно с другим процессом, который я реализовал с использованием библиотеки multiprocessing.

Вопрос : Как мне сделать это быстрее?

Мой python версия: 3.7.0, версия pygame: 1.9.6

PS: извините за длину вопроса

Ответы [ 2 ]

0 голосов
/ 24 апреля 2020

Итак, я нашел обходной путь! По сути, я не могу сделать код быстрее из-за собственных способностей рендеринга в Pygame, даже режим HW не сильно повышает скорость.

Решение (больше обходного пути):

Я добавил слой периода ожидания, в течение которого pygame делает снимки отрендеренного экрана и сохраняет изображение в собственном кэше без обновления экрана. Позже у меня просто рабочий экран, который можно использовать для просмотра анимации.

Вот код:

def runPathAnimation(path, colortype):
    index = 0
    images = []
    for i in range(len(path)-1):
        x1, y1 = (gmd[path[i]].getNodePosition())[0], (gmd[path[i]].getNodePosition())[1]
        x2, y2 = (gmd[path[i+1]].getNodePosition())[0], (gmd[path[i+1]].getNodePosition())[1]
        m = (y1-y2)/(x1-x2) if x1 != x2 else 'undefined'

        cx, cy = range(min(x1,x2),max(x1,x2)), range(min(y1,y2),max(y1,y2))
        if y1 > y2:
            cy = reversed(cy) 
        if x1 > x2:
            cx = reversed(cx)

        if str(m) != 'undefined':
            con = y2-(m*x2)
            if m > 0.5 or (m <= -1 and m >= -1.5):
                for y in cy:
                    ev = pyg.event.get()
                    x = int((y-con)/m)
                    images.append(loadpath(x,y,path,colortype,index))
                    index += 1
                    r = pyg.draw.rect(screen, colortype, (md.WIDTH_NETWORKPLOT-250,md.PLOT_AREA[1]+30,index/5,20), 2)
                    pyg.display.update(r)
            else:
                for x in cx:
                    ev = pyg.event.get()
                    y = int(m*x+con)
                    images.append(loadpath(x,y,path,colortype,index))
                    index += 1
                    r = pyg.draw.rect(screen, colortype, (md.WIDTH_NETWORKPLOT-250,md.PLOT_AREA[1]+30,index/5,20), 2)
                    pyg.display.update(r)
        else:
            for y in cy:
                ev = pyg.event.get()
                images.append(loadpath(x1,y,path,colortype,index))
                index += 1
                r = pyg.draw.rect(screen, colortype, (md.WIDTH_NETWORKPLOT-250,md.PLOT_AREA[1]+30,index/5,20), 2)
                pyg.display.update(r)
        print('Loading...'+str((i+1)/len(path)*100)+'%')

    runAnimation(images)

def runAnimation(images):
    animate = True
    img = 0
    print('Start!')
    while animate:
        ev = pyg.event.get()
        pyg.event.pump()
        keys = pyg.key.get_pressed()
        if keys[pyg.K_LEFT]:
            img -= 1
            if img < 0:
                img = 0
        if keys[pyg.K_RIGHT]:
            img += 1
            if img >= len(images) - 2:
                img = len(images) - 2
        if keys[pyg.K_q]:
            animate = False
        screen.blit(images[img],(0,0))
        pyg.display.update((0, 0, md.WIDTH_NETWORKPLOT, md.PLOT_AREA[1]))

PS: В моем коде md.xxx - это размеры для Мой экран matplotlib и pygame.

ВАЖНО: Это всего лишь обходной путь, , а не решение !!

0 голосов
/ 15 апреля 2020

Попробуйте использовать pygame.time.Clock().tick(**) Это команда, которая позволяет вам выбрать FPS, с которым вы хотите запустить вашу программу, позволяя вам увеличить скорость рендеринга. Если вы решите использовать это, поставьте целое число, представляющее FPS, где я написал звездочки.

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