21 февр. 2011 г.

Оптимизация игр на Unity (Pool, Coroutines)

Как оказалось, функции Instantiate и Destroy довольно затратные, поэтому, если в вашей игре порождается и уничтожается множество объектов, то используйте pool объектов.

Необходимое количество объектов генерируется при запуске игры и в нужный момент, отображается на сцене. Вместо того чтобы уничтожать объект, отключаете его поведение, либо просто меняете значение свойства active на false. Данный подход значительно ускоряет игру.

Также для повышения производительности рекомендуется использовать вместо Update так называемые Coroutines. Просто создаете функцию возвращающую IEnumerator (в C#), в которой располагается цикл, каждая итерация которого выполняется через определенные промежутки времени, которые можно задать написав yield return new WaitForSeconds(1.0f). Если пауза достаточно велика, то действия описанные в цикле будут выполняться реже, тем самым сберегая процессорное время.

17 февр. 2011 г.

Оптимизация игр на Unity для iPhone (продолжение)

Предрелизное состояние. Уже всё сделано, всё красиво. Запускаем на iPhone - появляются небольшие, но мешающие игровому процессу тормоза.
Стал читать статьи по оптимизации, нашел как использовать Profiler в Unity - очень полезный инструмент при оптимизации и iPhone internal profiler.

И смотря лог в Profiler'е обнаружил, что довольно часто вызывается сборщик мусора и на него уходит несколько миллисекунд процессорного времени. При этом никакие игровые объекты не уничтожались. Я стал читать про сборщик мусора (GC) в Unity и нашел ветку форума, где говорилось, что такое поведение встречается при использовании OnGUI. У меня как раз стояла единственная кнопочка, которую я отрисовывал в нем. После замены её на Plane, столь частый вызов GC прекратился и игровой процесс ускорился.

Но оптимизация на этом ещё не закончена. Вскоре напишу про что-нибудь ещё.

12 февр. 2011 г.

2D анимация. Sprite Manager.

Моей ошибкой при создании 2D объектов на Unity было представление, что ничего плохого не происходит если в коде поменять текстуру материала или поменять смещение и другие её параметры. Как оказалось при осуществлении этих действий Unity создает отдельный материал, что конечно же добавляет Draw Call.

Почитав немного о материалах я сначала хотел использовать sharedMaterial, но поняв, что изменив смещение текстуры на нем поменяются текстуры на всех объектах с таким же материалом я отказался от этой идеи.

Продолжив поиски я нашел прекрасный класс, который был написан пользователями Unity сообщества - Sprite Manager. Он очень удобен для создания большого количества объектов. И количество Draw Calls при его применении равно количеству разных материалов. Поэтому, с его помощью, мне удалось значительно ускорить игру.

Использовать его довольно просто:
1. Создаем пустой объект и назначаем ему скрипт SpriteManager или LinkedSpriteManager (если объекты постоянно движутся). Заполняем параметр AllockBlockSize - примерное количество объектов данного вида (т.е. создается статический массив заданной длины) и задаем материал.
2. Затем создаем объект (например, Plane), отключаем Renderer. Навешиваем на него скрипт, который содержит ссылку на объект со  SpriteManager из пункта 1.
3. Вызываем соответствующие процедуры инициализации. Я использовал функцию AddSprite.

Всем кто занимается 2D анимацией очень рекомендую использовать данный класс. Экономит много времени и ускоряет вашу игру.

9 февр. 2011 г.

2D анимация

Про то как сделать 2D анимацию в Unity есть полезная статья.
Подход заключается в том чтобы нарисовать спрайт с различными положениями объекта.
Натянуть его на плейн и использовать скрипт, который будет пробегать по кадрам и соответствующим образом менять текстуру.
Скрипт дан в статье.

8 февр. 2011 г.

Лучи света


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

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

Оптимизация игр на Unity для iPhone


Некоторый список рекомендаций по оптимизации игр на Unity, на которые приходилось обращать внимание в процессе разработки:

  • объединяйте мэши и текстуры;
  • используйте как можно меньше различных материалов;
  • используйте как можно меньше источников света;
  • максимальное количество draw calls для iPhone составляет около 25-30 (посмотреть их количество можно во вкладке Game, нажав кнопку Stats);
  • на каждый элемент GUI уходит draw call;
  • прозрачные материалы нагружают процессор, рекомендуется их использовать как можно реже;
  • отключайте поведение и отображение объектов, находящихся за пределами камеры;
  • старайтесь сжимать текстуры в PVRTC - это ускоряет рендеринг на iPhone;
  • желательно чтобы все аудио треки были моно;

  • если вы пишите на JavaScript используйте директиву #pragma strict;
  • используйте больше статических полей;
  • при элементов GUI, вместо создания объекта Rect для их позиционирования в функции OnGUI, лучше данный объект сделать свойством класса и проинициализировать его в функции Awake, иначе он будет создаваться при каждом вызове OnGUI;
  • избегайте постоянного вызова в функции Update, FixedUpdate или OnGUI таких функций как GetComponent, либо transform, желательно такие объекты делать свойствами класса, инициализировать их в Awake и затем использовать.

7 февр. 2011 г.

iPhoneKeyboard


Почему-то на форумах, когда речь заходит о iPhoneKeyboard ответ получить довольно сложно. Либо я не умею их задавать, либо мало кто понимает как она работает.

В игре нужно было сделать запись в таблицу рекордов. При завершении игры, если пользователь попал в 10-ку лучших по количеству очков, ему отображается таблица рекордов и предлагается вписать свое имя. Для удобства я хотел автоматически поместить поле для ввода имени в фокус и открыть клавиатуру iPhone.

Почитав, что GUI.TextField рассчитано на работу с iPhone я, конечно же, первым делом написал скрипт с его использованием. На PC всё было здорово, но при загрузке на iPhone поле не становилось в фокусе и чтобы ввести имя нужно было сначала на него нажать и затем появлялась клавиатура. Такая перспектива совсем не радовала.

Почитав про различные подходы к использованию и документацию по iPhoneKeyboard остановился на следующем решении:


private iPhoneKeyboard keyboard;
private string userName = "";
void  Start ()
{
    keyboard = iPhoneKeyboard.Open(userName);
}
void  OnGUI ()
{
    userName = keyboard.text;
    GUI.Label(userNameTextRect, userName);
}
Т.е. клавиатура в нужный момент автоматически открывается, а набранный пользователем текст отображается в GUI.Label. Если нужна возможность последующего редактирования информации, то можно таким же образом использовать GUI.TextField.

Зацикленная фоновая музыка


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

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

Разместил на форуме вопрос по этому поводу. Мне дали два совета:
1. Используя AudioClip.length убрать несколько миллисекунд с конца файла.
2. Использовать "несжатый" аудио-формат.

На второй вопрос я сначала просто закрыл глаза, так как, во-первых, свято верил документации и, во-вторых, размер файла получался не такой маленький и, в итоге, простая игра со всеми текстурами и звуками занимала около 28Мб, что не так уж и мало для iPhone.

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

После долгих поисков решения на форумах и прочих ресурсах я наткнулся на статью о том как сделать непрерывный циклический MP3 трек. Прочитав её я узнал, что в зависимости от программы, конвертирующей файл в MP3 она, для целого количества сэмплов, добавляет один либо несколько пустых сэмплов в начало или конец композиции. Также там дана программа, которая, вроде как, ресэмплит композицию и делает цикл непрерывным, но у меня так и не получилось добиться каких либо результатов. После помещения такого трека в Unity мелодия урезалась секунд на 10, хотя Windows Media Player играл её довольно хорошо. Попробовали сохранить трек в OGG, но небольшая пауза так и осталась.

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

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

Теперь с зацикленной фоновой музыкой нет никаких проблем.

Вступление


Уже 3 месяца занимаюсь созданием игр на Unity для iPhone. И хотя ни одной игры с моим участием пока не выпущено, пройден некоторый путь изучения Unity, осознаны некоторые тонкости при создании и оптимизации игр под iPhone со всеми его ограничениями.
В какой-то момент я подумал, что хорошо бы было где-то записывать эти вещи, чтобы самому не забыть, а в лучшем случае помочь людям не наступать на те же грабли.
Возможно, какие-то решения будут не самыми оптимальными, либо эффективными. Прошу не бояться поправлять и высказывать свое мнение.
Да прибудет с вами сила! :)