2016-12-30 8 views
0

Я работаю над 2D-изометрической MORPG на основе плитки на протяжении нескольких месяцев и понял, что мой игровой экран имеет очень низкую частоту кадров. Я занимаюсь исследованиями и тестированием в течение нескольких недель и могу только сделать маргинальную прибыль для своей частоты кадров. Я использовал cProfile и тестировал свою частоту кадров, и я могу добиться 100+ FPS в программе в обычном режиме, но как только моя функция «render()» называется, она падает до 5 FPS. Вот (несколько) сжатая версия этой функции:Нужна консультация по оптимизации рендеринга python 2d для рендеринга

for y in range(0, 42): 
     for x in range(0, 42): 
      if (player.mapY + y - 21 > 0) and (player.mapY + y - 21 < 128) and (player.mapX + x - 21 > 0) and (
           player.mapX + x - 21 < 128): 
       if (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) > -64 and (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX)+halfGraphicSizeX < 1024+32 and\ 
        (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY) > -32 and (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY)+halfGraphicSizeY < 600+32: 
        if self.getGroundAtYX(player.mapY + (y - 21), player.mapX + (x - 21)) is not 0: 

         canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX), 
              (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY), 
              image=groundGraphics[ 
               self.getGroundAtYX(player.mapY + (y - 21), player.mapX + (x - 21))], 
              anchor=NW) 

        if (self.getObjectAtYX(player.mapY + (y - 21), player.mapX + (x - 21)) is not 0): 
         canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX), 
              (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34), 
              # -34 for img height diff between ground & objects 
              image=objectGraphics[ 
               self.getObjectAtYX(player.mapY + (y - 21), player.mapX + (x - 21))], 
              anchor=NW) 


      ghostCopy = list(gameState.itemsOnGround) 
      for i in range(0, len(ghostCopy)): 
       if ghostCopy[i].idNum > 0: 
        if (player.mapX - 21 + x == ghostCopy[i].mapX and player.mapY - 21 + y == 
         ghostCopy[i].mapY): 
         canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX), 
              (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY), 
              image=itemGraphics[ghostCopy[i].idNum], 
              anchor=NW) 

      ghostCopy = "" 
      ghostCopy = list(gameState.monster) 
      for i in range(0, len(ghostCopy)): 
       if ghostCopy[i].active == True and ghostCopy[i].hp > 0: 
        if (player.mapX - 21 + x == ghostCopy[i].mapX and player.mapY - 21 + y == 
         ghostCopy[i].mapY): 
         canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX), 
              (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34), 
              # -34 for img height diff between ground & objects 
              image=monsterGraphics[ghostCopy[i].type], 
              anchor=NW) 
         canvas.create_rectangle(
          (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) + 15, 
          (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 35), 
          (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) + 16 + 33, 
          (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 29), fill="black", 
          width=0) 
         canvas.create_rectangle(
          (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) + 16, 
          (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 30), 
          (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX) + 16 + (
           32 * (ghostCopy[i].hp/ghostCopy[i].maxHp)), 
          (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34), fill="green", 
          width=0) 

      ghostCopy = "" 
      ghostCopy = list(gameState.sprite) 
      for i in range(0, len(ghostCopy)): 
       if ghostCopy[i].graphic[0:1] == "0": 

       if ghostCopy[i].active == True and ghostCopy[i].username != "ME": 
        if (player.mapX - 21 + x == ghostCopy[i].mapX and player.mapY - 21 + y == 
         ghostCopy[i].mapY): 
         #"graphicToDraw" variable is derived from an animation state but has 
         #been removed from here to make it easier to read 
         canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX), 
              (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34), 
              # -34 for img height diff between ground & objects 
              image=graphicToDraw, 
              anchor=NW) 

      if (y == 21): 
       if (x == 21): 
        #"graphicToDraw" variable is derived from an animation state but has 
        #been removed from here to make it easier to read 
        canvas.create_image(
         (startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX), 
         (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34), 
         # -34 for img height diff between ground & sprites 
         image=graphicToDraw, 
         anchor=NW) 

      ghostCopy = "" 
      ghostCopy = list(gameState.spells) 
      for i in range(0, len(ghostCopy)): 
       if ghostCopy[i].active: 
        if (player.mapX - 21 + x == ghostCopy[i].mapX and player.mapY - 21 + y == 
         ghostCopy[i].mapY): 
         canvas.create_image((startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX), 
              (startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY - 34), 
              image=spellGraphics[ghostCopy[i].id], 
              anchor=NW) 

функция render() принадлежит map объекта (self относится к карте в этом сегменте кода). Он эффективно проходит через плитки -21 .. 21 по оси x, y и если плитка находится в пределах границ плитки карт (0 .. 128), а плитка находится в пределах размера экрана (1024x600), она рисует ее на экране ,

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

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

Я попробовал только вручную рисовать количество изображений, которые обычно отображались бы в общей сцене в цикле for и получались около 30 + fps. Таким образом, моя функция рендеринга на 25 кадров в секунду медленнее, чем могла бы быть.

Я предполагаю, что константа проверяет каждую итерацию цикла на то, можно ли оптимизировать плиту внутри экрана, но я не уверен, как это сделать, не используя такой цикл.

Если у кого есть какие-либо рекомендации, я бы очень признателен .. я торчу на эту проблему в течение нескольких недель и не сделали никакого реального прогресса в моей игре на всех :(

** [EDIT] ** Большинство рекомендаций, по-видимому, направлены на ограничение количества математических выражений. У меня не было возможности проверить это, но возможно ли, что только ограничение количества математики будет значительно оптимизировать частоту кадров?

+0

Это в Python2 или Python3? –

+0

Я использую python3 –

+0

Являются ли объекты (игроки, призраки) каждый целиком в пределах определенной плитки? Чтобы вы могли определить, какая плитка для каждого объекта? –

ответ

0

Вы можете потянуть все выражения, содержащие константы и y, должны быть вычислены только внутри внешней (for y) петли, например, player.mapY + y - 21, y * halfGraphicSizeX, y * halfGraphicSizeY и т. Д .: компьютер каждый раз один раз, спрятать переменную и использовать весь код. Аналогично для x, но не так эффективно.

+0

Так много жатвы математики в этом коде. –

+0

Использование рекомендаций по оптимизации Emett и удаление всей математики с участием «y» в верхний для цикла «y» дает некоторую прибыль от частоты кадров (0.5 кадров в секунду для перемещения выражений «y» в верхний цикл цикла) –

0

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

for y in range(0, 42): 
    for x in range(0, 42): 
     player_y = player.mapY + y - 21 
     player_x = player.mapX + x -21 

     if player_y > 0 and player_y < 128 and player_x > 0 and player_x < 128: 
      start_drawing_x_half_graphic_size = startDrawingPosX + x * halfGraphicSizeX - y * halfGraphicSizeX 
      start_drawing_y_half_graphic_size = startDrawingPosY + x * halfGraphicSizeY + y * halfGraphicSizeY 

      if start_drawing_x_half_graphic_size > -64 and start_drawing_x_half_graphic_size + halfGraphicSizeX < 1024+32 and\ 
       start_drawing_y_half_graphic_size > -32 and start_drawing_y_half_graphic_size + halfGraphicSizeY < 600+32: 

       if self.getGroundAtYX(player.mapY + (y - 21), player.mapX + (x - 21)) is not 0: 

        canvas.create_image(start_drawing_x_half_graphic_size, 
             start_drawing_y_half_graphic_size, 
             image=groundGraphics[ 
              self.getGroundAtYX(player.mapY + (y - 21), player.mapX + (x - 21))], 
             anchor=NW) 
+0

Я проверил это, и в объектных плотных сценариях он улучшает частоту кадров от 5 кадров в секунду до 6 кадров в секунду, а в сценах с несколькими объектами улучшает частоту кадров от 15 кадров в секунду до 17-20 кадров в секунду. Спасибо за рекомендуемую оптимизацию, я не думал, что математика вызовет такое замедление! –

+0

Есть намного больше, что можно сделать, чтобы улучшить скорость вашего кода. Это только первое логическое место для начала. –

Смежные вопросы