Содержание:


  1. Оптимизация производительности и целевые устройства
  2. Отрисовка текста и оптимизация Label
  3. Виртуальные списки и перемещение камеры

Оптимизация производительности и целевые устройства


Я знаю 3 вещи, которые никогда не заканчиваются: вселенная, ремонт BMW и оптимизация.
Вы начинаете оптимизировать на стадии проектирования архитектуры. Кажется, что этот процесс прекратится после достижения требуемой производительности на целевых устройствах. Но это миф. После того как вы добавляете новый функционал или графический контент в билд, производительность ухудшается.

Мы проанализировали статистику по всем устройствам и выделили те, на которых будем отслеживать производительность в ~30fps. Суть в том, чтобы определить самые слабые устройства, с которых совершаются множество покупок.



Чтобы выявить проблемные места, используем нативный профилировщик Unity и xCode. Постоянно анализируем, чтобы выявить недостатки, оцениваем возможности их устранения и выгоду от этого. После приоритезируем задачи и работаем, пока не достигнем желаемой производительности.

При профилировании обращаем внимание на 4 аспекта: выделение памяти в каждом кадре, пики производительности, частоту вызовов методов и самые времязатратные участки кода.

Давайте рассмотрим примеры.

Оптимизация Label и отрисовки текста


Давайте разберем, как Unity работает с текстом. Все символы и глифы при запросе для отрисовки добавляются системой в специальную текстуру, которая находится в памяти. Затем с помощью точных текстурных координат символы отрисовываются из этой текстуры. Для батчинга желательно, чтобы все Label были в одном DrawCall. Следите за этим во время сборки UI.

Мы переписали компонент работы с текстом в NGUI под наши потребности. В итоге получили ощутимое увеличение производительности. Вот список оптимизаций и переделок:

  • Разделили Label на несколько компонентов. Базовый – для отрисовки собственно текста. Компонент эффектов – для добавления BB-кода, теней, обводки и других надстроек над процессом построения текста. Если нужен текст без эффектов, базовый компонент выигрывает. В нем нет кода парсинга текста и дополнительных полей для хранения состояний. Также лейбл полностью пересчитывается только в случае изменения текста, размера шрифта и его собственного ректа.
  • Расширили список поддерживаемых BB-кодов.
  • Внедрили политику работы с размерами шрифтов. Проанализировали их использование в проекте и выбрали самые эффективные размеры: 28, 36, 40. Если разработчик выбирает 25-й размер, будет использоваться ближайший из списка – 28-й. Выбранный размер отскейлится до 25. Это позволило значительно сократить размер текстуры под шрифт.
  • Оптимизировали процесс обновления текстуры при увеличении ее размера. Раньше, если запрашивался символ, который не помещался в текстуру, она удалялась. После этого запрашивались все символы из активных лейблов в UI и строилась новая. Из текстуры просто удалялись неиспользуемые символы, и она могла не увеличиваться в размерах. На практике при открытии нового окна с другим набором символов текстура могла пересоздаваться. Мы добавили кэширование используемых символов и их размеров, чтобы гарантировать увеличение текстуры каждый раз до значения следующей степени двойки. Довольно быстро эта текстура заполнялась всеми используемыми символами и размерами. Это оказалось оптимальнее, чем постоянная перестройка с меньшей по размеру текстурой.

Виртуальные списки и перемещение камеры


В нашей игре очень много данных. Чтобы отображать их, мы реализовали виртуальные списки. В них есть определенное количество элементов, которые отображают контент. Это количество обусловлено размером видимой области списка и несколькими экстраэлементами для перестраховки. Когда элемент выходит за границы видимой области, ему определяются новые данные, и он переставляется в списке таким образом, чтобы пользователь, проскроллив дальше, увидел его следующим. Этот подход позволяет не создавать много объектов и отображать сколько угодно данных.



Мы заметили, что при активном использовании списков данных у пользователей ощутимо проседал FPS. Дело в том, что в Unity изменение позиции объектов – относительно дорогая операция, учитывая, что UI сам по себе – большое количество GameObject. Чтоб избежать изменения позиций большого количества элементов, решили изменять позицию всего одного элемента – камеры. Грубо говоря, форма отображается двумя камерами. Одна для списка, другая для остального UI. На области скролла находится контроллер, который обрабатывает перемещения камеры и сообщает списку как ему нужно перестраиваться.



Мы также можем создавать бесконечные списки, когда данные загружаются с сервера страницами и динамически добавляются в источник данных. Следующий запрос выполняется, когда пользователю остается просмотреть еще какое-то фиксированное количество объектов. Такое решение удобно использовать в рейтингах.

Это не очень удобно при сборке UI: приходится постоянно контролировать слои объектов в рамках одного префаба, но после доработки инструментария проблем с этим не возникало. Кроме списков и прочих UI-элементов, мы используем такой подход с камерами для сектора, карты, превью персонажей, войск и зданий.



Заканчиваю работу над четвертой статьей. Увидимся!
Поделиться с друзьями
-->

Комментарии (18)


  1. Vilyx
    30.01.2017 17:26

    Есть ли у uGUI те же проблемы что у NGUI с производительностью? И решаются ли они так же как описано в статье?


    1. KumoKairo
      30.01.2017 18:05
      +1

      C uGUI на самом деле всё ещё интересней. Советую обратиться к документу с Best Practices https://unity3d.com/learn/tutorials/topics/best-practices
      Товарищ с последнего юнайта так же достаточно много времени уделяет проблемам и оптимизации uGUI https://www.youtube.com/watch?v=n-oZa4Fb12U
      От себя скажу что хак с прокруткой листа в uGUI ещё больше в тему из-за особенностей построения батчей в канвасе


      1. TheShock
        30.01.2017 22:09
        +1

        Очень значительное улучшение производительности дает выключение PixelPerfect.
        Значительно — это 20 раз разница на больших списках:
        image


        1. KumoKairo
          30.01.2017 22:20

          В данном случае я не имел в виду pixel perfect, я имел в виду сборку батча канвасом при изменении любых параметров (в данном случае положения) элементов внутри скролвьюшки. Для тех случаев, когда все pixel perfect и прочие вещи уже давно выключены, а производительность оставляет желать лучшего


          1. TheShock
            30.01.2017 23:17
            +2

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

            прочие вещи уже давно выключены

            которые важны, но столь очевидны что о них уже нигде не пишут.


    1. Plarium
      01.02.2017 11:14

      В статье речь про оптимизацию производительности, а не о решении проблем с какой-то конкретной системой. Поэтому в примере — несколько обобщённых примеров, которые можно обыграть в любом фреймворке.


  1. patch1
    30.01.2017 20:25
    +1

    Большое спасибо за статью, но хотелось бы отметить, то что не упомянуто в статье (во всяком случае я не заметил)

    1) Правильно ли я понял про списки? — данный подход можно использовать, только если на Canvas (на котором находится список) стоит Render mode — World Space:

    1.1) Если будет стоять Overlay — то там камера вообще ненужна, т.к рендер UI происходит и без нее
    1.2) Если Camera — то все UI элементы будут двигаться в 3D пространстве вместе с камерой (оставляя в кадре тот элемент который и был ранее) — получается прокрутку не осуществить двигая камеру…

    2) Для небольших списков есть лайфхак, во Viewport списка, замест компонента Mask, установить Rect Mask 2D. Повышает производительность на слабых девайсах примерно в 1.5-2 раза.

    image


    1. WeslomPo
      30.01.2017 21:06
      +2

      В статье речь об NGUI, в uGUI всё гораздо печальнее с производительностью.


      1. patch1
        30.01.2017 21:30

        Тогда в этом есть смысл) почему то перемкнуло что в пункте 2 про NGUI, в пункте 3 про UGUI (наверно сыграла ассоциация UI и Unity часто в контексте) — обычно данное словосочетание ассоциируется с UGUI (наверно у тех кто мало работал с NGUI и много с UGUI) — спасибо


    1. Plarium
      01.02.2017 14:04

      1) Да, только с таким Render mode канвас будет неподвижен относительно камеры.


  1. TheShock
    30.01.2017 22:06
    +3

    данных. Чтобы отображать их, мы реализовали виртуальные списки. В них есть определенное количество элементов, которые отображают контент. Это количество обусловлено размером видимой области списка и несколькими экстраэлементами для перестраховки. Когда элемент выходит за границы видимой области, ему определяются новые данные, и он переставляется в списке таким образом, чтобы пользователь, проскроллив дальше, увидел его следующим. Этот подход позволяет не создавать много объектов и отображать сколько угодно данных.

    Выложили б ответственный код. Вполне себе библиотечная функция, на которую интересно посмотреть.


    1. Plarium
      01.02.2017 11:15

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


  1. chistobaevAndrey
    31.01.2017 11:49

    И всё равно найдусь я, у которого будет лагать. От этого никуда не денешься.


  1. frangovalex
    31.01.2017 11:50

    Можете подробнее разобрать как работает GIF с отрисовкой карты?
    Это inactive геймобъекты или же динамическая подрузка с последующей выгрузкой?


    1. Plarium
      01.02.2017 11:16

      И то, и другое. Если вкратце, то там динамически подгружаемые из ресурсов при первом использовании объекты, которые по мере того, как выходят из области видимости, складываются в пулы, в inactive состоянии.
      Тут важна сама идея, как и со списками, как и глобальным сulling'ом, не рендерить то, чего игрок не видит, а так же максимально-возможное переиспользование всего: один из первых заветов любого гейм-девелопера.


  1. ajaxtelamonid
    31.01.2017 17:39
    +1

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

    Суть проблемы. По любому событию (например, приход войск разведки обратно на базу, или завершение строительства) список, который находится на экране, даже не связанный с этим событием (например, список заметок на карте), пересоздается и изменением точки отрисовки, говоря повседневным языком — мгновенно скроллируется наверх. Ладно в заметках, поматерился, отмотал на 5 экранов вниз и по новой, а если событие настигает в момент выбора количества юнитов на списке войск? Список под движущимся пальцем перерисовывается и вот ты уже выбираешь других юнитов. Люди так много теряли по невнимательности.

    Багу этому 2 года, он кочует из игры в игру, многократно репортился, но максимум, чего игроки добились — введение настройки отключения всплывающих уведомлений у событий. На предложение исправить баг целиком комьюнити-менеджеры отвечали, что сие никак не представляется возможным. Но это же какой-то позор? Фиксируйте положение списка до перерисовки и после перерисовки рисуйте его так, чтобы для пользователя он визуально не менялся, сделайте очередь на изменение списка войск, которая фризится в момент выбора войск, добавляйте вернувшиеся войска в список в конец, чтобы при перерисовке у пользователя оставалось все на прежних местах, сделайте запрет перерисовки списка заметок в принципе, не знаю, вариантов огромное количество.

    Очень ждем.


    1. Plarium
      01.02.2017 14:32

      К сожалению, случаются ситуации, когда команда разработки сосредоточена на решении более глобальных задач, а вот такие неполадки остаются долгое время в To do-листе.

      Все подобные места чинятся отдельно, и то, что вы описали в своём сообщении, мы обязательно исправим в ближайших релизах.


  1. maxru
    01.02.2017 18:36

    Горизонтальную ориентацию экрана что мешает сделать для планшетов, кстати?
    Платят мало? И не будут платить, играть неудобно.