Пользователь
0,0
рейтинг
14 февраля 2013 в 18:56

Разработка → Оптимизация 2d-приложений для мобильных устройств в Unity3d tutorial

Недавно наша студия завершила разработку большого обновления — Captain Antarctica: Endless Run — для устройств на iOs. Кропотливая работа над обновлением затронула производительность, которая оказалась очень низкой на слабых устройствах. Я боролся с этим целую неделю и добился как минимум 30 FPS, а также значительного сокращения размера приложения. Хочу рассказать, как я это сделал, ну и как делать не стоит.
Статья пригодится любым разработчикам на Unity (причем не только менеджерам проектов и техническим специалистам, но и просто программистам, художникам и дизайнерам), потому что она затрагивает как оптимизацию на Unity в целом, так и конкретно оптимизацию 2d-приложений для мобильных устройств.


Возможно все!


Начну с того, что каждый раз, когда я приступаю к оптимизации, я поначалу не верю, что можно что-то еще оптимизировать, особенно если проект уже прошел несколько циклов оптимизации до этого. Но просмотр официальной документации Unity, тем на форумах, статей в Интернете наводит меня на мысль о новых возможных улучшениях. Таким образом я веду специальный список, в котором записаны основные идеи по тому, что можно оптимизировать в проекте на Unity, постоянно обновляю его и первым делом обращаюсь к нему, когда речь заходит об оптимизации. Этими идеями я хочу с Вами поделиться. Надеюсь, статья поможет Вам сделать Ваш проект намного более шустрым.
Сразу обозначу, что разработка велась на Unity 3.5.6, целевая платформа — устройства Apple от iPhone 3GS и новее.

Базовые правила


Для начала приведу несколько правил, которыми я пользуюсь при разработке и оптимизации.
1. Не оптимизируйте заранее.
Это золотое правило должно быть знакомо всем, кто когда-либо занимался оптимизацией. Вспомним правило 80/20: 80% пользы получается от 20% работы.


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

Однако, есть пара исключений из этого правила, которые особенно важны при разработке для мобильных устройств, потому что это правило больше подходит для PC-проектов. А PC намного производительнее мобильных платформ и менее ограничены в ресурсах. Так вот, исключения:

  • Допустим, есть код, или конструкция, которую вы уже сейчас знаете, как написать лучше, с меньшей затратой по памяти/процессора и тд. Вы об этом знаете по своему опыту, потому что не раз оптимизировали компоненты подобного рода, и знаете, что это приводит к увеличению производительности. Так почему бы уже сейчас не написать ее правильно? Обычно такие вещи складываются в свод правил типа «как правильно писать код, и как писать не надо», и грамотный программист постоянно им пользуется во избежание ошибок в будущем. Нечто подобное имеет место для художников, дизайнеров и тд.
  • Если есть действие, которое будет повторяться много раз, и с самого начала можно его оптимизировать, почему бы это не сделать сразу. Дальше все будет идти автоматом, и не нужно будет исправлять одну и ту же вещь несколько раз. Сюда относятся, например, вспомогательные скрипты, помогающие дизайнерам ускорить однообразную работу с группой объектов.

2. Найдите то, что нужно оптимизировать.
Несколько лет назад я имел такую ситуацию: проходишься по коду, сценам и тд и оптимизируешь все, что только можно, а потом смотришь на производительность — это ошибка начинающего оптимизатора.
Для начала нужно найти то, что тормозит систему, на чем бывают скачки производительности. В этом очень сильно помогает профайлер. Конечно, он довольно условен и сам немного нагружает систему, но польза от него неоспорима! В Unity Pro есть встроенный пройфалер, довольно удобный. Но если у Вас обычный Unity, можно использовать профайлер xCode, или любой другой подходящий. Профайлер помогает находить наиболее нагружающий код, показывает используемую память, насколько звуки грузят систему, кол-во DrawCall на конечном устройстве и тд. Таким образом, прежде чем оптимизировать, прогоните приложение через профайлер. Думаю, Вы много чего нового узнаете о своем проекте)


Что мне помогло решить проблему с производительностью?


Еще до прогона через профайлер было очевидно, что слабое место — в кол-ве Draw Calls. В среднем сцена выдавала порядка 70 DrawCalls, что для устройств уровня iPad1 и ниже является фатальным. Нормально для них — 30-40 Draw Calls. Посмотреть кол-во Draw Calls можно прямо в редакторе в окне Game->Stats:.


Кол-во Draw Calls, показываемых в редакторе, совпадает с таковым на конечных устройствах. Профайлер это подтвердил. Вообще, очень полезно смотреть эту статистику, и не только программистам, но и дизайнерам, для нахождения «тугих» мест в игре.
В наших сценах плохо работало группирование нескольких Draw Calls для одного и того же материала в один Draw Call. Это называется Dynamic Batching. Я начал рыть на тему «как понизить кол-во Draw Calls и улучшить их группирование». Ниже перечислены основные правила, придерживаясь которых, можно получить приемлемое количество Draw Calls. Вот те, которые мне очень сильно помогли:

1. Использовать атласы для комбинирования нескольких текстур в одну большую.
На самом деле важнее даже, чтобы спрайты/модели использовали не то что бы одну общую текстуру, а скорее один общий материал. Именно в кол-ве различных материалов измеряется кол-во Draw Calls (в идеальном случае). Поэтому у нас в проекте используемые изображения всегда объединены в атласы, разбитые на категории: объекты, используемые на всех сценах, объекты GUI, задний фон и тд. Вот пример такого атласа:

Такое разбиение так же будет полезно в будущем для применения к текстурам различных настроек. Но об этом позже.

2. Не стоит изменять Transform->Scale.
Объекты с измененным Scale попадают в отдельную категорию, увеличивающую кол-во Draw Calls. Я заметил это, когда еще раз проходился по документу Draw Call Batching. Вот что значит перечитывать;) Пройдясь по сцене, я обнаружил огромное количество таких объектов:


  • Оказалось, что дизайнеры уже давно увеличили некоторые часто используемые объекты в 1.2 раза через Scale прямо в прифабе объекта. В итоге, мы пришли к решению увеличить их размер прямо в текстуре. Это к тому же соблюдало условие пиксель-в-пиксель, что очень важно для 2d-игры.
  • Были объекты, которые имели одно и то же изображение, но разный Scale. Для таких объектов был написан специальный скрипт, который переводил нужный Scale с Transform прямо на меш, используемый для спрайта, т.е. менял размер меша и оставлял Scale = (1, 1, 1).
  • Также Scale часто использовался у нас для отражения объекта, например, Scale.x = -1 отражает объект слева направо. Все такие скейлы были заменены на соответствующие им повороты.
  • Еще у некоторых объектов Scale был изменен в анимации, пару раз неоправданно. Не забывайте проверять анимации, часто изменения в них — неявные, и могут быть обнаружены только после запуска.

В результате устранения практически всех изменений Scale удалось снизить кол-во Draw Call практически вдвое! Правда, эти улучшения заняли у нас порядочное время, поэтому стоит помнить о Scale уже на начальном этапе дизайна уровней. И теперь в Памятке дизайнерам у нас жирным шрифтом красным цветом написано: Старайтесь избегать изменения Scale.

Еще несколько подсказок (взятых в том числе из документа Unity), как сократить количество Draw Calls:

  • Статические объекты могут быть помечены как Static. Тогда будет использоваться Static Batching (только в Pro-версии), который тоже поможет сократить кол-во Draw Calls.
  • Старайтесь использовать объекты с одним и тем же материалом на одном и том же расстоянии от камеры. Пример: у нас различия по расстоянию в 10 юнитов уже давали 1-2 дополнительных Draw Call. При этом какая-то особая закономерность выявлена не была, но я подозреваю, что есть связь между размерами камеры, размерами объектов, их расстоянием до камеры и количеством Draw Calls. Экспериментируйте!
  • Старайтесь, чтобы объекты с разными материалами не перекрывали друг друга. Это тоже увеличивает кол-во Draw Call, особенно для полупрозрачных объектов.
  • Многопроходные (multi-pass) шейдеры увеличивают количество Draw Call. У нас таких не было, но полезно будет учесть это в будущем.
  • Каждая система частиц дает 1 Draw Call. (Имеется ввиду старая система частиц Unity 3.5.6, используемая нами по сей день. Как обстоят дела в Unity 4, я не знаю). Поэтому если на экране одновременно N систем частиц — это автоматически как минимум N Draw Calls. Обычно, одного и того же эффекта можно достигнуть разными способами, в том числе меньшим числом как систем частиц, так и частиц в системе. Часто видел, как начинающие дизайнеры эффектов используют огромное кол-во частиц (и огромный размер самих частиц), чтобы создать вау-эффект. При этом они не думают о производительности (особенно учитывая, что все это делается в редакторе на PC) и обычно достаточно меньшего количества частиц, чтобы достичь того же эффекта.

Скачки производительности




Второй фактор, влияющий на производительность — так называемые скачки производительности. Порою в игре были «зависания» на 0,5-1 секунду, что конечно же было неприемлемо и напрямую влияло на геймплей. Причем такое наблюдалось даже на самых последних устройствах.
И в этом случае помог профайлер! Вот список правил для уменьшения скачков производительности:

1. Старайтесь не использовать Instantiate(), особенно для сложных объектов.
Скачки производительности приходились в основном на вызовы Instantiate(), которые создавали новые объекты из прифабов, или клонировали существующие. Причем некоторые объекты были очень громоздкими, что и повлияло на время их создания. Вместо этого пришлось переписать систему так, чтобы объекты использовались заново. Т.е. состояние объекта после окончания использования (или перед использованием) приводилось к начальному. Это также помогло сократить объем используемой памяти (так как на новые объекты больше не нужно было новой памяти) и количество вызовов Destroy().

2. Минимизируйте количество вызовов Destroy().
Destroy (особенно для больших объектов) почти всегда приводит к манипуляциям с памятью. А это обычно плачевно сказывается на производительности. Это правило напрямую связано с правилом выше, ибо вызовы Instantiate()/Destroy() обычно связаны. Таким образом, использование объектов заново лишило необходимости уничтожать их.

3. Минимизируйте вызовы gameObject.SetActiveRecursively().
Для сложных объектов вызов может быть очень долгим, потому что он предполагает не просто активацию объектов и их компонентов, но в некоторых случаях и загрузку необходимых ресурсов.

4. Минимизируйте вызовы Object.Find().
Думаю, не стоит объяснять, что время этой операции зависит от кол-ва объектов на сцене. Сюда же относятся функции типа GetComponent().

5. Минимизируйте вызовы Resources.UnloadUnusedAssets() и GC.Collect().
Unity иногда сама прибегает к ним, если недостаточно памяти для загрузки нового ресурса или пришел запрос от ОС освободить неиспользуемую память. Таким образом, первые 2 правила автоматически сокращают кол-во таких вызовов. Лучшее место для вызова Resources.UnloadUnusedAssets вручную — перед загрузкой сцены или непосредственно сразу после ее запуска. Это также поможет освободить дополнительную память для сцены, что иногда бывает критично. Соответствующий скачок производительности можно скрыть, например, экраном загрузки;)

Использование правил выше привело к устранению скачков производительности и намного более плавным геймплею и картинке.

Другие оптимизации


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

Скрипты

  • Не используйте GetComponent<>() в Update, FixedUpdate и других подобных функциях. Вместо этого лучше кэшировать компонент в Awake() или Start(). Если объектов, использующих скрипт, очень много, такое кэширование может значительно сократить время работы скрипта.
  • Кэшируйте встроенные компоненты типа transform, renderer и тд. Особенно если они используются в функциях типа Update(). Ибо каждый такой вызов делается через GetComponent(). См. правило выше. Это можно сделать, например, так:

    /// <summary>
    /// Cached transform
    /// </summary>
    protected new Transform transform
    {
      get
      {
        if(cachedTransform == null)
          cachedTransform = base.transform;
        return cachedTransform;
      }
    }
    private Transform cachedTransform = null;

  • Используйте Vector2(3,4).sqrMagnitude вместо magnitude.
  • Используйте Color32 и Mesh.colors32 вместо Color и Mesh.colors при доступе к цветам меша.
  • Можно использовать OnBecameVisible/OnBecameInvisible для скриптов, которые могут быть отключены, когда камера больше не видит объект.
  • Используйте встроенные массивы. Имеются ввиду массивы типа T[]. Они намного быстрее всех остальных коллекций.
  • Отключите логи при работе приложения на устройстве. Обычно печать лога требует доступа к файловой системе, а если логов много, это может привести к печальным последствиям для производительности.
  • Отключите исключения. Как следствие — старайтесь не использовать их в коде. Отключение исключений поможет сохранить до 30% производительности. Но не забывайте, что если ошибка произойдет, и она не сможет быть обработана исключением — это падение приложения на устройстве. Поэтому приходится выбирать — либо хорошее тестирование и прирост производительности, либо надежность, но производительность чуть хуже. В случае, когда производительность удовлетворяет, преимущество лучше отдать второму варианту.

Физика

  • Чем меньше одновременно активных Rigidbody, тем лучше. Деактивируйте неиспользуемые.
  • Сокращайте количество FixedUpdate в единицу времени. Fixed Time = 0.03333 гарантирует физику со скоростью 30 вычислений в секунду, что обычно очень неплохо. Даже 20 может быть приемлемым, что соответствует Fixed Time = 0.05.
  • Минимизируйте использование Continuous или Dynamic collision detection. Они очень ресурсозатратны. Для 2d-игр они обычно не нужны.

Анимации

  • Следите за количеством анимированных объектов на экране. Чем меньше — тем лучше.
  • Вместо нескольких простых анимаций на сложном объекте, можно сделать одну сложную, делающую то же самое.
  • Можно попытаться уменьшить Sample Rate у клипа. Особенно если анимаций очень много.
  • Используйте Culling Type = Based On Renderers или Based On User Bounds. Тогда анимация будет проигрываться только тогда, когда объект виден на экране.

    Если это, конечно, устраивает.

Система частиц

  • Используйте Vertical Billboard вместо Billboardв Particle Renderer.
  • Используйте на мобильных устройства шейдеры из Mobile->Particles. Это касается не только системы частиц.
  • Используйте как можно меньше систем частиц и самих частиц в системе для достижения нужного эффекта.
  • Для слабых систем можно отключить некоторые малозаметные или несущественные системы частиц. Что позволит сохранить несколько Draw Calls и поднять на них производительность в ущерб эффектности. Уверяю, зачастую приятнее получать удовольствие от процесса игры, нежели от мегакрутых эффектов.

GUI

  • Не используйте OnGUI(). Каждый такой вызов — несколько дополнительных Draw Calls. Тоже самое относится к GUILayout. И вообще, поддержка GUI в Unity сделана очень плохо. У нас, например, своя система GUI, основанная на спрайтах. В Asset Store есть несколько других очень полезных плагинов для GUI.
  • Размер текстуры, генерируемой для шрифта, можно уменьшить, добавив только используемые символы. В Font Settings установить Character = Custom Set, а в Custom Chars включить используемые символы:
  • Можно попробовать использовать отдельные камеры для объектов сцены и GUI. Это позволит сделать GUI zoom-независимым, что освобождает от использования на нем Scale при увеличении/уменьшении. Иногда это может увеличить производительность.

Другое

  • Отключить акселерометр, если он не используется. Можно также понизить частоту измерений в Player Settings.
  • Можно ограничить максимальный FPS на старых устройствах. Например, установив его в: Application.targetFrameRate = 30. Обычно это приводит к более гладкой картинке. Также, это уменьшает просадку аккумулятора, т.к. за то же время требуется меньше процессорной мощности. Вообще, на устройствах Apple FPS > 60 не имеет смысла, т.к. частота обновления экрана у них — 60.
  • Иногда периодическая частая сборка мусора сглаживает производительность. Потому что сама система делает это редко и когда уже все совсем плохо, и может накопиться большое кол-во объектов для уничтожения, что приводит к скачку производительности. Если делать это чаще, объектов для уничтожения будет меньше, и освобождение памяти будет более гладким. С другой стороны, за все время работы сцены сборка мусора может и не понадобиться.

Уменьшение размера приложения


Для чего это может понадобиться? Раньше это делалось потому, что приложения размером <20Mb можно было загружать на iOS через 3g-сеть. Что в принципе должно увеличить кол-во закачек, хотя конкретной статистики я не видел. В связи с выпуском iPad3 приложения стали «жирнее», и порог был поднят до 50Mb. Не стоит также забывать, что после заливки приложения в AppStore оно будет увеличено в размере в среднем на 4Mb. Для проверки, сколько приложение будет весить после заливки в AppStore в xCode в Organizer->Archives даже появилась специальная кнопочка Estimate Size:


Все необходимое по уменьшению размера билда описано в документах Unity:

Я же опишу здесь то, что использовал сам. Начнем с того, что влияет на производительность:

1. Используйте правильный формат текстуры.
Это также позволит уменьшить объем используемой текстурной памяти, а соответственно и повысить производительность. Иногда достаточно использовать формат текстуры 16 bits, особенно если вся графика нарисована всего в нескольких цветах. Сравните:

Для монотонных текстур можно использовать только ее серую и альфа-компоненту и лепить из них готовый объект, используя специально написанный для этого шейдер и умножение на цвет:

Для задников и нечетких объектов можно использовать компрессию. Сейчас PVRTC-компрессия на iOS довольно продвинутая. Стоит помнить, что чем больше текстура — тем лучше ее качество после компрессии. На маленьких текстурах использование компрессии может быть неприемлемым.
Чтобы это все имело смысл, нужно разделять объекты на группы типа «задний фон», GUI, игровые объекты, о чем я уже писал. Тогда на каждый тип можно завести свой атлас и использовать различные настройки формата текстуры.

2. Используйте правильный формат звуков.
Раньше я не задумывался об этом. Использование компрессии на звуках позволило мне не только сократить размер приложения, но и объем используемой им памяти. Сам я пользуюсь следующими правилами:
  • Используйте Audio Format = Native для очень коротких и маленьких по размеру звуков (< 100 Kb).
  • Для остальных используйте компрессию. Compression = 96 Kbps — это уже очень приемлемо. Ниже — заметны искажения.
  • Используйте Load Type = Compressed in Memory для большинства ужатых звуков. Это позволит уменьшить объем используемой памяти, но может влиять на производительность, так как требует распаковки во время воспроизведения.
  • Для фоновой музыки используйте Load Type = Stream from disk, особенно на системах с быстрым HDD. Это позволит сохранить очень много памяти.
  • Используйте Decompress on Load во всех остальных случаях. Звук будет занимать больше памяти, но практически не будет «сажать» CPU, что поможет иногда избавиться от скачков производительности.
  • Используйте Hardware Decoding для фоновой музыки. Встроенный декодер в устройствах iOS позволяет сократить использование CPU на проигрывании фоновой музыки, но это может быть сделано только для одного трека одновременно.
  • Используйте Force to Mono, если стерео не нужно. Или если в файле оно присутствует, но не различимо. Это в 2 раза уменьшит объем используемого места (как в памяти, так и на диске).

Далее следуют другие шаги по уменьшению размера билда. Большинство настроек делается в Player Settings:

  • Установите в настройках проекта Stripping Level = Use micro mscorlib. Это только для владельцев Pro. Не используйте без надобности единицы из System.dll и System.Xml.dll. Они не совместимы с Use micro mscolib.
  • Установите API Compatibility Level в .Net 2.0 subset. Но иногда после этого код может не работать, если соответствующие классы/функции и тд не входят в .Net 2.0 subset. Что однажды случилось в моем случае.
  • Также следует избавиться от зависимостей от ненужных библиотек.
  • Установите Script Call Optimization Level в Fast but no exceptions, чтобы отключить исключения. Это также уменьшит размер билда.
  • Установите Target Platform в armv6 (OpenGL ES1.1). Если вам не нужен armv7. Или наоборот, в armv7, но не оба одновременно. Учитывая, что Apple все меньше поддерживает устройства с armv6, имеет смысл оставить лишь armv7.
  • Не используйте массивы JS. Лучше не использовать JS вообще, используйте C#. Обычно, после переписывания кода скриптов с JS на C# приложение весит меньше.

Использованные источники вдохновения


В первую очередь использовались документы Unity — самые полезные ресурсы от самих разработчиков Unity. Их читать нужно в первую очередь, желательно по несколько раз, а через некоторое время еще раз, потому что они постоянно обновляются.

Отдельно выношу уже указанные документы по сокращению размера приложения:

Другие источники:

Заключение


Проведенная мною оптимизация позволила существенно повысить производительность игры и играбельность в целом. В качестве дополнительного бонуса был уменьшен размер приложения;) Надеюсь, моя статья поможет Вам сделать ваше приложение на Unity еще лучше.
Возможно, кому-то будет интересно почитать мои предыдущие статьи:

Скорее всего, следующей моей статьей будет статья о том, как упростить работу в Unity и минимизировать кол-во ошибок.
Если у кого-то есть вопросы, предложения, поправки — я всегда готов выслушать и обсудить.
Антон Григорьев @fischer
карма
66,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

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

  • +1
    Не понял, почему Scale разбивает batch, но об этом, наверное, надо спрашивать у разработчиков Unity.

    Насколько оправдано использование Unity для разработки 2d игр (небольших и «среднего размера»)? Или лучше поискать другой движок?
    • +1
      Это описано где-то в доках Unity — такое вот странное поведение.

      По второму пункту — Unity кроссплатформенный, С# — очень комфортный язык. Плюс, в Unity есть редактор.

      Без базовых знаний 3D графики не обойтись в любом случае. Если сомневаетесь, какой движок выбрать, выберите любой и что-нибудь на нем сделайте — после этого будет проще движки выбирать :)
    • 0
      Насколько мне известно, только non-uniform scale разбивает dynamic batching.

      Насчёт 2D игр — тут можно посмотреть игры с комментариями разработчиков, там есть 2D игры.
      • 0
        Разбивает любой скейл, причем GO с одинаковым скейлом начинают батчится между собой снова. Еще есть одно ограничение — в меше должно быть не более 300 вертексов (не более 900 вертексных атрибутов, если быть более точным). Если меши подпадают под это ограничени — батчинг сработает, иначе будет дополнительный дк.
        В своих спрайтах просто сделал галочки — «Bake scale on start» — при старте скейл запекается в меше и сбрасывается в единицу. Все работает прекрасно за исключением скейла иерархии — вся цепочка от корня должна иметь единичный скейл. Так же иногда нужен динамический скейл (например, при нажатии на кнопку она должна ужаться и вернуть свои размеры через определенное время) — на такими неприятные штуки пришлось закрыть глаза. Использую единый подход для спрайтов + гуи. В случае сложного гуи с необходимостью скролинга разных списков и т.п. расширенных контролов — хорошо помогает NGUI (а вот с анимацией у него не очень хорошо, как и с внутренней логикой, работающей практически везде через GetComponent()).
    • –5
      Родные frameworks от Apple сделаны идеально для разработки 2D игр.

      Не нужны никакие движки, разве только у Вас сложная физика и столкновения.
  • –2
    Нормально для них — 30-40 Draw Calls.

    Всегда стараюсь всю сцену впихнуть в 1-2 вызова. С системами частиц, любыми трансформациями, рисованием примитивов, кривых итд. Но, конечно, работаю не с Юнити.
    • +1
      Что-то мне сдается, что запекание всего перечисленного добра в 1-2 вызова обходится дороже, чем соббсна 30-40 вызовов…
      • 0
        Для мобилок — неверное утверждение.
        • 0
          для любой системы неверное утверждение :)
          • 0
            Это не так — обновление данных в буфере вершин в односторонннем порядке (WriteOnly) для мобилок, где число draw calls необходимо держать в пределах 10-20 — просто спасение, ибо тут упирается все именно в gpu, а не в cpu.
          • 0
            Извиняюсь, не так прочитал :) Просто для десктопных цифромолотилок сейчас и по 500 дк — это нормально, а вот для мобилок уже 20 дк будет не подъемно, если ориентироваться на бюджетки.
            • 0
              Что значит неподъемно? Оверхед на каждый вызов никоим образом не относится к видеокарте, это прежде всего временные затраты на обмен данными между cpu и gpu. И как правило, этот оверхед очень мал по сравнению с тем, что видеокарте приходится рендерить. Вот у меня сейчас в игре, скажем, 30 draw calls, и при этом рендеринг упирается тупо в филлрейт, с которым на бюджетных девайсах куда больше проблем, чем с количеством вызовов. Т.е. я могу хоть в сотню вызовов все отрисовывать — быстрее не станет.
              Минимизация количества вызовов — не панацея. В документации Unity вообще приведены такие ориентировочные числа: не больше 200 вызовов для мобильных девайсов, не больше 2000 для ПК
              • 0
                Простой тест — отрисуйте 200 мешей, из 4 вертексов и 2 трисов с шареным материалом с текстурой каждый:
                1. с отключенным динамическим батчингом.
                2. с включеннм динамическим батчингом.

                Запускайте тесты на реальной железке.

                >> В документации Unity вообще приведены такие ориентировочные числа: не больше 200 вызовов для мобильных девайсов, не больше 2000 для ПК
                Надо меньше слушать и больше проверять. Возможно, для последнего ябблопада это и так, но попробуйте повторить то же самое на гигагерцовом куалкоме с adreno200.
                • 0
                  Я и не спорю, что 200 — это в общем случае перебор. Просто 20 — это же совсем мелочь.

                  Простой тест — отрисуйте 200 мешей, из 4 вертексов и 2 трисов с шареным материалом с текстурой каждый:

                  Проверил.

                  С батчингом (1 вызов на все меши + 1 на индикатор фпс):

                  400 мешей — 60 фпс
                  200 мешей — 60 фпс

                  Без батчинга (по вызову на каждый меш)

                  400 мешей — 42 фпс
                  200 мешей — 60 фпс

                  Девайс — Motorola Defy, с цпу OMAP3630 (1GHz) и гпу PowerVR SGX530 (который примерно раза в 1.5 медленнее, чем стоящий в древнем iPhone 3GS SGX535). Достаточно лоу-энд для вас?
                  Так что документация Unity вроде как даже не врет.
                  • 0
                    >> С батчингом
                    >> 400 мешей — 60 фпс
                    >> Без батчинга
                    >> 400 мешей — 42 фпс

                    Что и требовалось доказать — даже на такой простой геометрии есть разница и это без всей остальной логики, только рендер.

                    >> Достаточно лоу-энд для вас?
                    Разговора про ябблофоны не было в принципе — там графический чип гораздо мощнее того зоопарка, который ставится в ондроеды. До сих под можно найти найти смарты на ARMv7 и с adreno200/205, а PowerVR-ы ставятся в относительно производительные решения.
                    • 0
                      >> Что и требовалось доказать — даже на такой простой геометрии есть разница и это без всей остальной логики, только рендер.
                      Добавлю — все это сильно зависит от gpu, можно почитать труд MadFinger-ов о сравнении производительности и фишках PowerVR и что им пришлось сделать, чтобы оно относительно вменяемо заработало на тегре2.
                      • 0
                        Согласен. Как я писал в комменте ниже, например, на PowerVR SGX530 вывод одного полупрозрачного квада во весь экран может легко съесть 15 фпс.
                        А Motorola Defy — уже очень старый аппарат (2010 год), и уже тогда SGX530 уже тогда считался видеочипом не ахти. Тот же Adreno 205 гораздо быстрее. Вот уже SGX540 — совсем другого класса производительности.
                        • 0
                          Ну мой эталон для тестов на данный момент — это acer liquid с adreno 200 (800x480) и на нем 60фпс бывает практически только при пустой сцене и очистке камеры сплошным цветом. Если на нем пойдет с приличной производительностью, то пойдет и на всем остальном. Если не пойдет — надо крутить, пока не пойдет :) Потом для проформы делается проверка на более мощных смартах и планшетах с разными чипами.
                          • 0
                            Если на нем пойдет с приличной производительностью, то пойдет и на всем остальном.

                            Согласен :) Хотя меня из-за этого постоянно терзают сомнения рода «Хм, а может добавить вот этот крутой эффект и забить на потерю fps на очень слабых девайсах?..»
                            • 0
                              В связи с переходом на 4-ку пришлось забить на ARMv6, а это минимум четверть рынка текущего железа потребителей (из-за китайцев, которые продавались за копейки и устраивают потребителей в серфинге по сети, например). Так же из-за того, что ARMv7 скорее всего нет без gles2, придется отказаться еще и от gles1.1 и все перевести на короткие шейдеры :(
                              Но тенденция радует — все меньше остается старого железа под 2.х и еще меньше под ондроед 3.х, а 4.х-ветка неуклонно растет, что уже означает неплохое железо у потенциальных потребителей.
                              • 0
                                А что такого нового в Unity 4 вы используете, что были вынуждены перейти? Просто интересно. Большая часть нововведений там или не особо критична, или же к мобильным играм относится слабо…
                                По поводу GLES 2 точно так же не пойму — если работает на GLES 1.1, то зачем переводить? Да и переводить шейдеры, в общем-то, необязательно — Unity переводит всё сама с вполне адекватным качеством. Вот буквально вчера решил «оптимизировать» и перевел самые тяжелые шейдеры с ShaderLab на CG. Прироста — от силы 1-2 фпс.
                                • 0
                                  >> А что такого нового в Unity 4 вы используете, что были вынуждены перейти?
                                  Была скидка при апгрейде с 3.х :) Ну и полноценные динамические шрифты на всех платформах. Правда пока до этого не дошло, т.к. NGUI.

                                  >> По поводу GLES 2 точно так же не пойму — если работает на GLES 1.1, то зачем переводить?
                                  С fixed-а неизвестно что генерит юнити — проще самому указать что именно хочу в vert и frag.
                                  • 0
                                    Дык если в качестве целевого API выбрать GLES 1.1, то fixed function и будет использоваться. Если вам не нужны возможности GLES 2.0, то, ИМХО, лучше на 1.1 и остаться — например, у меня GLES 2.0 всегда работает примерно на 15-20% медленнее.
                                    Хотя возможно, на современных видеочипах ситуация будет другая.
                                    • 0
                                      Вот именно. рубить концы так все :)
                    • 0
                      Требовалось доказать то, что разница между 1-2 вызовами и эдак 50 практически нулевая и абсолютно не соответствует вашей фразе
                      а вот для мобилок уже 20 дк будет не подъемно

                      Так что в пределах эдак 50 draw call оверхед на вызовы практически никак на производительность влиять не будет. Оптимизировать нужно в первую очередь сам рендеринг.
                      • 0
                        Дополнил про разные gpu и что тестирование на одном устройстве мало что дает. Возможно, для одной платформы, где других ускорителей нет, это и сойдет, но не для ондроеда и прочих nook-ов.
            • 0
              У меня вопрос. А расскажите что внутри у себя Unity3D подразумевает под 20 — Draw Call — это просто 20 вызовов glDrawArrays за апдейт? Тогда какие-то подозрительно маленькие цифры — вот у нас 2д игра, там за апдейт over 1к таких вызовов (800 текстур, 5к нодов). Даже после того как собрали весь GUI в 3-4 атласа — ну сотни (отдельные окна внутри батчаться только пока). Ну потом еще добавим логические слои может будет десятки — но блин, это же мелочь. Оно то и на over 1к то если логику убрать на втором айпаде занимало не больше 15-20ms, переход на атласы ну дал пару-тройку допольнительный ms, ну еще потом выйграем. Но это мелочь.

              Или DrawCall это нечто большее?
              • 0
                Уточните, пожалуйста, на чем разработка, как смотрите Draw Calls и на каком целевом устройстве?
                • 0
                  С++, iPad2-3-4 (ну и андроид планшетки), смотрим сколько раз за update вызвался glBindTexture/glDrawArrays.
                  • 0
                    По идее оно и есть. Вы под андроидом на реальном железе тестили? Потому что запуск под профайлером в редакторе позволяет только выявить узкие места, но не показывает реальную производительность.
                    З.Ы, Ужасающие цифры, если честно. Вы гугломапсы со всеми слоями что ли рисуете? :)
                    • 0
                      Да, тестили на реальном. Правда последний раз под андроидом я смотрел давно давно — релиз под апады на носу (android только через месяц), у нас тогда была поменьше нодов — но там все было ок. Будет плохо — есть куда еще оптимизировать.

                      Причем тут редактор не очень понял :)

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

                      Берем просто GUI, просто окно магазина… Вернее так, у нас в базе есть GUIFrame и есть GUITileset — GUITileset это nine patch tile — тайлсет из 9 частей (углы, стороны, цетр) — который можно добавлять в GUIFrame и в простейшем случае он его заполняет. Т.е. отрисовка одного тайл сета это 9 вызовов (количество мешей от 2-х для угла в лучшем случае, до сотни — например центр это кусок 32x32 текстура с noise, а им нужно залить окно 800x500). Т.е. 9 вызовов. Ну и таилсет может быть 3 patch — если ему по высоте/ширине менятся не нужно — например кнопка.

                      Дальше что такое магазин. Это окно, плюс панель для заголовка (в нем фильты — пяток кнопок), плюс два вертикальный фрейма, в одном категории (штук 20) — вертикальный скрол, в другом предметы в каждой категории (горизонтальный скролл). И так левый фрейм — там 10 видны за раз (20 внутри), кажда кнопка это таилсет (два по факту — нажатый или нет, но одновременно один отрисовывает) + иконка внутри + текст (считаем что текст за один вызво отрисовывается). Т.е. на кнопку это 110*(9+1+1) — вызово.

                      Далее, справа блоки с предметами — каждый блок таил сет, внутри иконка (внутри иконки циферки), блок с характеристиками (рамка вокруг таилсет), текст описания, три картник монеток. Ну штук 25 вызовов. А блоков 6 на 3 :)

                      Дальше продолжать? Есть более сложные окна.

                      Да тупо — вот иконка предмета в рюкзаке — плашка внизу, иконка, поверх рамка (подсвечивать не по уровню предметы), поверх по низу плашка (там цифры еще доп.), в уголке цифра количество. Т.е. хоть убей меньше чем за 1+1+1+1+1+1 вызово не нарисовать (см. выше про без атласов и батчинга). А иконок в рюкзаке — на закладке 8x10 минимум.
                      • 0
                        Глупый вопрос — чем не устроил в таком случае стандартный гуи в юнити? Он делает ровно столько же и с тем же количеством draw calls. Собственно, поэтому его разработчики под мобилки и тихо ненавидят, ибо при том количестве дк, что и у вас, любая железка должна вставать торчком, если это не десктоп хотя бы 6 годичной давности.
                        • 0
                          У нас не Unity, у нас сильно переписанный cocos2d-x. Юнити нам не очень подходит — у нас там много не стандартных нюансов, типа загрузки картинок в паралельно потоке (в том числе и по сети), загрузки анимации из бинарных данных которые уже в томформате в котором должы быть в памяти, мешанина форматов (jpeg/png/pvr565,pvr8888,pvr444,pvrtc4, png+прозрачнотьс в jpeg), отрисовка swf-вок свои рендерером и прочая магия, которая не очень совмстима с Unity :)

                          А что касается железки — мы бы и на первой айпаде бы жили (правда с 20-23 fps) если бы не память. И это нам еще есть куда оптимизировать.
              • +1
                В общем случае — это количество переключений состояния, но в целом да, это вызовы операций немедленной отрисовки (glDrawArrays/glDrawElements).
                А вот у вас что-то подозрительно огромные цифры. Возможно, второй айпад менее чувствителен к количеству вызовов, но, как я тестировал в комментариях выше
                С батчингом
                400 мешей — 60 фпс
                Без батчинга
                400 мешей — 42 фпс

                Насчет GUI — ну например, все сторонние GUI-системы для Unity стараются минимизировать количество вызовов буквально до 1.
                • 0
                  Я совсем low end не смотрел давно, у нас немного другой фокус в текущем проекте. Но блин 400 мешей это как жить то? Весь GUI целиковыми картинками рисовать или на каждый чих запекать — так отзывчиваости не будет или памяти не хватит.

                  Вот я тупо пример с рюкзаком пресонажа в ММОRPG приводил — каждая иконка предмета это 64 на 64 пускай, иконки все разные берутся с сервера, количество 2к+, набор в рюкзаке произвольный. Т.е. хоть убей каждую иконку отдельный draw call. Пускай рюкзак в рюкзаке на одной странице 8x10 предметов. Это тупо на иконки 80 отрисовок по 2 треугольника для каждого своя текстура. Т.е. 160 мешей на одну страницу в рюкзаке. А там еще пресонаж с вещами рядом одетыми + боевка внизу + задник боевки + чат.

                  Хотя конечно о лоу-енде типа Motorola Defy мы даже и не думаем :)
                  • 0
                    Весь контент не должен ехать сервера по запросу, а должен быть уже на клиенте, либо должен приезжать в виде клиентских апдейтов при логине. Не суть. Давайте посчитаем. Допустим, вы не берете дохлое железо, значит потяните текстуры 2к х 2к, а это уже 32х32 = 1к иконок. 2 такие текстуры и вы получаете всего 2 дк для отрисовки любого количества из этих 2к иконок. Т.к. вы все-равно держите все картинки в оперативке, то еще и тут будет прирост по скорости загрузки и по размеру (если использовать сжатие).
                    • 0
                      Ооо… тут много чего.

                      Ну на счет клиенских апдейтов — за билд больше 50 мегабайт отдел маркетинга вам такое спасибо скажет, весь день будите отбиватся. Это раз.

                      Во вторых — даже если и не так, это чего — когда геймдизу захочется новый предмет ввести это ему не 15 минут в админке, а апдейт выкатывать? Или художника позыв иконку улучшить? Нафиг, нафиг.

                      Далее. Вот изменил художник иконку — что теперь игроку атлас посылать целиком в 2 (6 в ретине) мега (вместо 16 кб) — вот нам спасибо игроки скажут (а текстуры мы и 4кx4к потянем если нужно).

                      Ну и еще — не держим мы иконки в памяти, только те которые по факту у конкретного игрока — да еще и только на текущей и вокруг страницах (остальные стримингом будут подгружаться в процессе скролла — с диска или сети если конент не выкачен) — иначе памяти не напасещься… Да и нету у игрока еще всех иконок — если он выбрал не выкачать контент, а докачивает его по мери потребности.

                      Ну фиг с ними с иконками — возьмем доспехи на персонаже — для примера доспех 12 предметов, 60 типов доспеха, x2 мужской/женский — каждый предмет атлас 20-30 кадров (в различных фазах анимации). Масс бой — ты видишь до 6 персонажей, каждый может быть в произвольном наборе пердметов. Персонажы постоянно меняют, прибегают к тебе, убигают, бои 500x500 — можно увидить практически любой набор. Что будем делать? :)
                      • 0
                        >> Ну на счет клиенских апдейтов — за билд больше 50 мегабайт отдел маркетинга вам такое спасибо скажет, весь день будите отбиватся. Это раз.
                        >> Во вторых — даже если и не так, это чего — когда геймдизу захочется новый предмет ввести это ему не 15 минут в админке, а апдейт выкатывать? Или художника позыв иконку улучшить
                        и далее по тексту — надо просто всех, любящих делать частые неконтролируемые апдейты, отправлять сразу к маркетологам :)
                        Все апдейты должны быть оттестированы и выкатываться запланированно большими бандлами — юзверы скажут спасибо за сэкономленный трафик и скорость работы + саппорт ваших серверов выкатит ящик коньяка. Всю статику (а весь контент это именно статика) можно выкатывать в облако и забыть про него до следующего апдейта.

                        >> Персонажы постоянно меняют, прибегают к тебе, убигают, бои 500x500 — можно увидить практически любой набор. Что будем делать? :)
                        Все точно так же — контент статичен, бегают только идентификаторы — все лежит в атласах и рисуется максимально быстро и без подзагрузки. Если будут проблемы с постоянно увеличивающимся количеством объектов — это уже проблемы геймдиза, генерирующего тонны бесполезного лута.
                        Вообще, по-хорошему это надо все разделять по «экранам» и грузить только то, что нужно, остальное выгружать.
                        • 0
                          Так, ну про апдейты это разговор не предметный — наш опыт показывает, что апдейты должны быть как можно чаще и в идеале не требовать изменения клиента. И плюс чем меньше качать тем лучше.

                          Ну а статика и так будет в облаке благодаря амазоновскому CDN.

                          Эээ… Все доспехи в атлас? У нас даже браузерка себе такого позволить не может, хотя 600 метров на ура выжирает. А нам бы в 80 уложится. Все доспехи загрузить это мегов так 256 за раз минимум, это если похитрить.

                          И что значит по экранам — вот бой, PVP бой, против тебя оппонент, на заднем фоне пара — вот они меняются, к тебе прибегает и убегает.
                          • 0
                            >> И что значит по экранам
                            иконки доспехов и всего прочено не нужны на экране боя, как и начинка инвентаря.
                            По поводу «все доспехи в атлас» — неужели все 2к предметов визуально уникальны? Я понимаю там диабла или вов, да и то там комбинации, визуально разных вряд ли будет даже 2к. Т.е. можно комбинировать иконки из запчастей + оптимизировать малоотличающиеся, разобрав их по запчастям. Понятно, что дизайнерам придется нарисовать инструментарий для сборки предметов из запчастей, но профит очевиден.
                            • 0
                              2к я говорил про иконки предметов вообще, не про доспехи. Вообще уникальных картинок в контенте у нас 5к+.

                              Доспехов я зовучил сколько. А про вов не нужно — там только базовых тиеров 12 штук + еще штук 50 остальных сетов доспехов. Если брать комбинации вещей там не удивлюсь если будет больше 1кк комбнаций потенциальных.

                              У нас вот только что глянул 456 уникальный мужских частей доспехов — и да они большей часть сильно разные. Рисовать по частям — возвращяемся выше к количество call draw :)
                              • 0
                                Рисовать по частям — возвращяемся выше к количество call draw :)

                                Так как раз нет — каждая запчасть будет меньше целой картинки и в один атлас их влезет больше. А дальше — батчить по атласам. Но раз вам хватает производительности, то и так хорошо. :)
      • 0
        уточните, что имеете ввиду под «запеканием всего перечисленного добра»
        • 0
          Объединение всех объектов и сущностей в один массив для вывода через glDrawArrays/glDrawElements как бы не мгновенное и подразумевает процессорный оверхед (как и обновление сего массива). Поправьте, если я не прав.
          • 0
            Уточню — для динамических объектов. Статику можно объединить вполне очевидно (если она рисуется одинаково)
            • 0
              1. Буфер можно держать в VRAM (где она есть в виде отдельной памяти — будет хороший буст).
              2. Обновлять буфер не для всего, а только для изменяющихся объектов.
              3. Для того, чтобы получать пенальти по скорости, нужно ворочать по несколько тысяч вершин в каждом кадре.
          • +1
            Посмотрите в сторону XNA или Sony PSM SDK. Как они работают с 2д. Объединение всего что возможно и минимизация вызовов к видеокарте — это лучшая практика.

            Количество вершин у вас ведь все равно одинаковое, что разбивать вызовы что не разбивать. Наполнить 30 массивов по 40 вершин или 1 в 1200 — какая разница?
            При том количестве вершин что в 2д (врядли вы вылезете за 2000) и при том как 2д обрабатывается (вы же в 2д объекты двигаете, а не камеру) вообще нет смысла выделять «статику» — быстрее будет банально сформировать 1 буфер и 1 раз его весь обновить.
            • 0
              В Unity точно так же можно при желании рендерить всю сцену в 1-2 вызова. Вот только, по-моему, оверхед на каждый вызов не настолько велик, чтобы был вообще смысл говорить о разнице в производительности для 2д между, скажем, 1 и 30 вызовами.
              А про статику — имелось в виду, что её можно загрузить в VBO и в дальнейшем забыть о ее обновлении вообще.
              • 0
                а вы проверьте. у меня на живом проекте после запихивания всего в 2 вызова фпс поднялся с 45 до 60. До этого отдельно рендерил кривые и на каждую кривую был отдельный вызов (всего до 20).
                Не могу сказать, что это заслуга только уменьшения количества вызовов, так как переписывание сильно затронуло разные части рендера.
  • +3
    Пункт про предварительную оптимизацию очень неочевидный и больше зависит от опыта и квалификации.

    Например, мы когда делали Shadow Snake, затачивали его строго под iPad и натянули игру на другие разрешения перед релизом за 2 дня. Вин!

    Когда делали анимации — заводили под каждый анимированый спрайт отдельную текстуру (один или несколько стрипов анимации) — так было проще. В результате получили ощутимое количество нерационально использованного места в текстурах. По Draw Calls мы вписались в 20-30, но вот по размеру дистрибутива вышли в районе 70 Мб. Сейчас возникли проблемы с публикованием в Google Play, т.к. там свыше 50 Мб приходится гемороиться. Чудом вписались в 48 Мб путем сжатия всего чего только можно было сжать и ухудшения качества некоторых текстур, т.к. переделопачивать всю графику — слишком много работы. Теперь уже знаю, что это оптимизировать надо с самого начала. Фэйл!
  • 0
    Слабые устройства — это какие?
    • 0
      Имелись ввиду iPhone3gs, iPhone/iPod 4 и iPad1. То, что ниже, я вообще не рассматриваю, учитывая статистику их использования и то, что в новом xCode под armv6 уже не скомпилишь (но если захотеть, то можно). Можно глянуть сюда для сравнения производительноси.
      Некоторые крутые (обычно 3d) игры забивают на них и в описании пишут: Поддержка iPhone4s и выше.
  • 0
    Спасибо, хорошая статья.

    Сейчас кажется уже пофиксили, но был в Unity (версии ~3.4) забавный баг. Если в коде встречалось Input.acceleration, то при сборке проекта подключалась библиотека акселерометра и молча вставала в общий update. Отъедая ~5-7 ms, что для намеченных 30fps было критично.
    Причём этот Input.acceleration мог быть в никем не используемом классе и даже не попадать в сборку.
  • 0
    спасибо за советы, тоже столкнулся с проблемой производительности, на айпаде все летает, на айфоне вобще не запускалось, в итоге было пагубное влияние размеров текстур
  • 0
    Про влияние скалинга на batch не знал, спасибо.
  • +1
    Ещё один совет по оптимизации — помогает указание очерёдности отрисовки объектов. К примеру, землю лучше всего рисовать последней, т.к. она часто закрыта объектами. Если на земле сложный шейдер, то выгода может быть существенной.

    Порядок отрисовки можно менять в шейдере или в материале.
  • 0
    Уже было написал гневный коммент, но полез в родную доку — оказывается там-то и написано «Don't use scale». Правда, следующие же предложения все раскрывают — не батчатся группы между собой, а не вообще, объекты (1,1,1), (x,x,x) и (x,y,z). Таким образом, если вся сцена в объектах произвольного масштаба (что как бы в реальном проекте ожидаемо), то на батчинг никакого влияния масштаба не должно быть. Дока правда молчит насчет влияния на скорость рендера ненормализованных по масштабу мешей. В частности, NGUI в принципе построен на масштабировании всех элементов для pixel perfect, и прекрасно в 1 вызов рендерит все.
    • 0
      NGUI работает по совершенно иному принципу — он генерирует и динамически обновляет по одному мешу на каждую панель, т.е. все виджеты не имеют своего меша, а их трансформы учитываются при апдейте общего меша.
    • 0
      По поводу группировки объектов со Scale (1, 1, 1), (2, 2, 1) и (2, 2, 2). Вот вам реальная статисткика: с ними — 60-70 Draw Calls, после приведения все к 1 — 20-25 Draw Calls. У меня сложилось впечатления, что не все объекты со скейлом (2, 2, 1) группируются друг с другом, даже если это абсолютно одинаковые объекты, хотя судя по документу Unity должны. Вероятно сыграло роль еще и то, что объекты перекрывали друг друга.
      • +1
        А на чем смотрели, 4.0 или 3.х как в статье? Я про 4.0
        • 0
          Ясно, у нас пока 3.x. Видно в 4.0 группировку усовершенствовали. Спасибо за комментарий.
  • 0
    Использую большую часть описанных в статье советов. Однако добавлю, что лучше использовать как можно меньше полупрозрачных объектов, занимающих большую площадь экрана — на мобильных девайсах с этим очень плохо. Так, на Motorola Defy вывод банального полупрозрачного текстурированного квада во ведь экран опустил FPS с 55~60 до 40.
    • 0
      Да да, помнится читал у девлоге у одних ребят что оказалось выгоднее делать больше полигонов, чтоб меш был приблизительно по форме объекта и оставалось меньше лишних прозрачных пикселей
  • 0
    Расскажите, использовали ли Sprite Manager (2), и как генерировали атласы. Тот что SM2, например, может делать это автоматически.
    • 0
      Нет. Спрайты у нас делаются на своей системе. А атласы делались художником вручную с самого начала. Неудобно, конечно, и ошибки бывают. Но сейчас как раз работаю над их автогенерацией с возможностью настройки.
    • +1
      Обычно всегда используется вот это — docs.unity3d.com/Documentation/ScriptReference/Texture2D.PackTextures.html
      В редакторе или рантайме. А дальше уже кто-как сохраняет полученные Rect-ы для дальнейшей выборки из атласа.
      • 0
        Да, видел. Спасибо!
        Я уже занимался лет 5 назад упаковкой текстур. Сейчас появились новые идеи типа использования прозрачных частей спрайтов как дополнительное пространство. Обычно это круглые объекты, соответственно, их прозрачные части — углы. Вот такие углы разных спрайтов на текстуре можно совмещать друг с другом для более экономичного использования пространства текстуры. Поэкспериментирую, насколько это эффективно, и напишу результаты;)
        • 0
          Не получится — это не пустое пространство, а блоки, которые не должны ничего содержать при отрисовке на экране. Можно отрезать прозрачные области по краю, но это работа художника + контент может быть выровнен так, чтобы правильно анимироваться.
          • 0
            Возможно, Вы не поняли. Они и не будут ничего содержать, так как они прозрачные) Но вот такие прозрачные области двух спрайтов можно совместить, в результате целостность спрайта не потеряется, т.к. в это области все равно останется прозрачно, и будет получено дополнительное место в размере этой области.
            • 0
              Другое дело, будет ли это существенно добавлять дополнительного пространства на текстуре? Вот это я и хочу выяснить)
            • 0
              Попробовать можно, но куча потраченного времени вряд ли окупит результат — проще пересортировать спрайты по атласам. Например, у меня все автоматизировано для сборки по папкам-атласам. Внутри папки каждый файл имеет имя вида ИмяОбъекта_Фпс_ТипАнимации_ИмяАнимации[-N] (где N от [1 до +max] и служит для верной сортировки кадров в анимированном спрайте). Это все подхватывается из папки и собирается в редакторе юнити в оптимизированные бинарные дескрипторы применительно к каждому атласу. Полученный атлас так же складывается рядом с дескриптором + у него настраиваются свойства импорта. В скрипте, посредством которого выполняется генерация атласа, стоит проверка на переход размеров за лимит 1к х 1к — таким образом можно отслеживать слишком большие и потенциально нерабочие атласы на старых устройствах. По полученному атласу проводится визуальный анализ и невлазящие куски переносятся в другую папку-атлас.
              • 0
                Интересно) Да, я тоже думаю над вопросом траты времени. Но если удастся сделать, я обязательно отпишусь о результатах. Чтобы либо предостеречь, либо наоборот порекомендовать)
          • 0
            А про выравнивание контента для анимирования — это не должно влиять на текстуру. В текстуре должен быть спрайт, максимально урезанный без прозрачных «полос» по краям. А нулевая точка для анимации должна задаваться отдельно. Будь это задание в скрипте спрайта в случае анимированных спрайтов, или смещение относительно базового объекта, в случае организации объектов. В любом случае, не стоит это делать через добавление прозрачности на текстуре для визуального смещения объекта;)
            • 0
              Зависит от сложности анимации. Иногда проще что-то получить в виде готовой последовательности кадров, чем пытаться анимировать это руками через простые трансформации.
              • 0
                Тоже верно. Тут тогда получается вопрос выбора. И ситуация может получиться, как в комментарии выше — в одном случае получилось сразу и практически без оптимизации, в другом, из-за того, что сразу не задумались — фейл и большие трудозатраты по переделыванию. Тяжело сразу понять, как выйдет;)
                • 0
                  Тех, кто делает сразу и без оптимизации, следует пересадить на дохлые железки 600МГц / 256Мб ОЗУ, gles1.1 и лимитом памяти на процесс в 32Мб, вот тогда они научатся ценить ресурсы и сразу же будут делать прямые и быстрые решения, потому что другие попросту не запустятся :) Всегда следует ориентироваться под самое дохлое железо — более быстрое будет работать гарантированно + весьма и весьма шустро, с хорошим запасом производительности.
                  • 0
                    Ой. А зачем ориентироват на дохлое железо, если оно дает 1-2% прибыли? :)

                    Да и уже 1000Мгц/256 ОЗУ и gles 1.1 — это вполне себе второй айпад (а лимит на процесс говорит что вы вероятно на java пишете) — а он по прожнему зверь дай бог, и под категорию дохлое желехо не попадает.
                    • 0
                      Нет, на ява я не пишу слава богу, на юнити мы строгаем. :) Ресурсы развращают — надо держать себя в руках :)
        • 0
          Мои 5 копеек:

          Наверно нет смысла писать самому свою 2D систему, если только для своих проектов (если на продажу — то там разговор особый). За 65 баксов есть 2D Toolkit — там есть и упаковка в атласы, и спрайт порежет и выкинет прозрачные места, и вывод спрайтов, и анимации, и масштабирование без увеличения DrawCall (только что проверил, не увеличивается). В общем, рекомендую.

          Если же просто интерес к написанию, или есть затея продавать его в дальнейшем — то да, свой имеет смысл. Если же цель быстро написать игру — то проще, быстрее и дешевле использовать готовый инструмент, тем более не дорогой.
          • 0
            А лучше подловить акцию и купить NGUI за 50% :) В принципе, так и поступаю — для множества анимированных спрайтов (включая инстанцирование) использую свою либу, для продвинутого гуи — стороннюю либу (NGUI). Будущий встроенный гуи будет сильно на него похож, но не во всем лучше, так что вопрос о покупать или нет пока не стоит — покупать стоит, потому что оно тестится на куче версий и платформ и в коде ставятся затычки в виде условной компиляции workaround-ов. Самостоятельно такое писать — очень затратно по времени.
  • 0
    Отключение исключений поможет сохранить до 30% производительности.

    Можно узнать, откуда такие данные?
    • 0
      На личном опыте проверено. Unity не зря сделали такую возможность: Fast but no Exceptions.
      Также можно отключить их в xCode (если включены) и сравнить производительность.
    • 0
      Если вы имели ввиду цифру 30 — точнее будет так: 20-30% производительности. Это из личного опыта и профайла.
      • 0
        Просто хочется понять, откуда берется рост производительности, что во включенной поддержке исключений так тормозит? Насколько понял, речь про настройку для скрипта Unity, а не про родные исключения Obj-C/C++? Может дело в кривой трансляции C# в нативный код или что там у них проихсодит, вроде проверки какого-нибудь флага исключений после каждой операции.
        Еще вот вижу, что вместе с исключениями отваливатеся рефлекшн.
        • 0
          Ну, исключения (и связанные с ними операторы — try/catch/throw), всегда добавляли некоторую нагрузку на программу. Что .NET/Mono, что в C++/Objective-C. Только все зависит от конкретной реализации. В Unity их отключение дает существенный прирост.
          В xCode, если включить Objective-C Exceptions — в зависимости от сложности проекта они тоже будут влиять на производительность.
        • 0
          Код проверки валидности вызовов занимает процессорное время

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.