Советы по оптимизации Unity проекта

Рано или поздно практически любой разработчик на Unity сталкивается с проблемами производительности в своем проекте. И не важно, что это за проект: очередной AAA проект от известной студии, или Match 3 в новом сеттинге от новичков в игровой индустрии. Оптимизировать проект придется так или иначе. Также не важно, под какую платформу (платформы) написан проект: PC, консоль или мобильный. Кажется, что уж в 2019 году-то с гигагерцами не только на компьютерах, но и на телефонах, необходимость в оптимизации для не AAA проектов должна была отпасть. Но нет, приходится экономить спички практически как на заре игростроения. Непонятно, почему так происходит; возможно, это плата за скорость разработки в Unity? Гоу в комментарии, обсудим.

 

Как нетрудно догадаться из названия статьи, речь сегодня пойдет об оптимизации Unity проектов. Если точнее, то мобильных Unity проектов. Если вам еще только предстоит оптимизировать проект, то, думаю, вы сможете почерпнуть много полезной инофрмации из данной статьи. Если же у вас уже есть опыт в оптмизации и профилировании Unity (да и не только Unity) проектов, вы можете помочь дополнить статью примерами из своего опыта. Нам и остальным читателям это будет очень интересно и полезно.

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

Читать →

Побитовые операции

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

Где применяются?

Побитовые операции широко применяются при работе с битовыми полями (флагами) и битовыми масками. Битовые поля позволяют максимально эффективно хранить некоторые состояния, имеющие только два значения: «включен» и «выключен». Access Control List (ACL) — хороший пример битового поля, использующийся для управления доступом к объекту. Например, в Linux и Mac OS каждый файл в системе имеет свой ACL, определяющий полномочия пользователя по чтению, записи или запуску файла. Битовые поля позволяют хранить несколько значений, не расходуя лишнюю память (по одному биту на каждое значение, вместо 4-х или 8-ми). А битовые маски (и побитовые операции), в свою очередь, используются для извлечения необходимых данных из битовых полей.

Довольно трудно обойтись без побитовых операций при реализации сетевой коммуникации. Побитовые операции здесь используются для проверки чек-сумм, определения битов окончания операции, для объеденения команд и данных, сериализации и т.д. и т.п.

Не получится не использовать побитовые операции и при реализации алгоритмов компресии и шифрования. Если взглянуть на любой алгоритм сжатия (например Deflate) или шифрования (например, AES), то мы увидим, что в описании довольно часто всплывают именно биты.

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

Побитовые операции являются частым гостем в случаях, когда требуется использовать разного рода оптимизации и микрооптимизации кода. Банальный пример:  2 << 4 в большинстве случаев быстрее. чем Math.Pow(2, 5).

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

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

Читать →

Поездка на Flutter Study Jam #2

17 августа 2019 года состоялся Flutter Study Jam в Воронеже. Это уже второе мероприятие подобного рода, проводимое  сообществом Google Developers Group (GDG). В этом году организаторами являлись компании Wrike и Surf.

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

Мероприятие проходило в Воронеже в Книжном Клубе “Петровский”. Уютный зал в роскошном торговом центре и радушные организаторы сразу исправили настроение от холодной и дождливой погоды на улице.

Читать →

Как наладить дружеские отношения между Unity и F#?

F# — это функциональный язык программирования (но, если точнее, на самом деле, это — мультипарадигмальный язык, т.к., кроме функционального программирования, он поддерживает как императивное программирование, так и объектно-ориентированное программирование) под платформу .NET Framework. Своего рода Scala в мире JVM, только для CLRF# предоставляет много интересных возможностей, и во многих сценариях он на две головы выше своего брата C#. Пожалуй, чтобы описать все преимущества F# над C#, потребуется целая статья (кстати, хорошая идея для следующей статьи), поэтому давайте не будем останавливаться на этом вопросе сейчас. Тем более, никто не обязывает нас писать проект полностью на F#. Если в каких-то местах есть необходимость (или более выгодно) писать на C#, нам никто не запретит это сделать. Таким образом, можно использовать сильные стороны обоих языков программирования!

 

Основным языком программирования в движке Unity, как, думаю, многим должно быть известно, является C#. И, несмотря на то, что F# движком Unity официально не поддерживается, нам попадалась информация от одного из сотрудников Unity в твиттере о том, что у них есть тесты некоторой кодовой базы на F#.

Другими словами, не стоит бояться писать на F# под Unity, только из-за того, что что-то может не заработать или не скомпилиться, т.к. F# официально не поддерживается. Все-таки, и C# и F# работают поверх CLR и компилируются в промежуточное представление (IL), которое и выполняется либо Mono, либо компилируется в нативный код посредством IL2CPP. Таким образом, для Unity, теоретически, абсолютно не важно, на каком языке вы будете писать.

Читать →

Несовершенство современного ПО на примере Unity

Разработка ПО в современном мире идет семимильными шагами. От релиза до релиза количество добавленых фич может составить несколько десятков (а порой счет идет и на сотни), а API измениться до неузнаваемости. И, вроде бы, в этом нет ничего плохо, но из-за количественных изменений довольно часто страдает качество. Баг-трекер забивается нерешенными задачами, частенько можно наблюдать регрессию по уже имеющемуся функционалу, а часть багов не правится по непонятным причинам, а порой и вовсе без таковых. Отправляемые баг-репорты отклоняются или ожидают минимального проекта для воспроизведения ошибки, а последний, зачастую, невозможно создать. А если все-таки удалось создать, то в ответ нередко получаешь отписку «У меня все работает».

И вишенка на торте — голосование за исправление багов. И если «ваша» проблема наберет большое количество лайков, то тогда, возможно, разработчики обратят внимание и поправят ошибку, хотя не факт. Бывают случаи, когда даже большое количество лайков не помогает. Увы (. И если баги (и то в основном критические, и то не всегда) худо-бедно правятся, то разного рода мелкие улучшения и полезности, которые могли бы сэкономить время, остаются за бортом, что не может не печалить. Само собой, вышеперечисленное относится не ко всему ПО вообще, а, скорее, это что-то типа средней температуры по палате.

Как вы уже могли догадаться, сегодня речь пойдет о нескольких мелких проблемах в Unity, вероятность исправления/улучшения которых стремится к нулю. Перечисленные ниже проблемы — это небольшая капля из того моря проблем, с которыми приходится сталкиваться ежедневно при работе с Unity. Думаю, если вы плотно общаетесь с Unity, то вам должно быть понятно, о чем идет речь. Вообще, стабильность Unity улучшается, и сейчас хотя бы можно стало работать (иногда недолго). Раньше это был просто кромешный ад из зависаний и крешей (сейчас же можно отработать день-другой, а можно встрять всего на полдня). И если на нашем относительно маленьком проекте столько проблем, трудно представить, сколько их на крупных проектах.
Читать →

Haxe — “Unity” среди языков программирования

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

Читать →

Неожиданное исключение на ровном месте

Намедни столкнулся с неожиданным (по крайней мере для меня) исключением на банальном куске кода в Unity. Если убрать все лишнее, то получится примерно вот такой скрипт:

Вышепреведенный код отрабатывал без ошибок, если на игровом объекте висел компонент CanvasGroup . А если этого компонента нет, то код генерирует исключение:

MissingComponentException: There is no ‘CanvasGroup’ attached to the «Button» game object, but a script is trying to access it.
You probably need to add a CanvasGroup to the game object «Button». Or your script needs to check if the component is attached before using it.
TestScript.Start () (at Assets/TestScript.cs:10)

По задумке, само собой, не ожидалось никакого исключения. Какое-то время потребовалось, чтобы понять, почему генерируется это исключение. Я, конечно, был в курсе, что Unity переопределяет оператор сравнения и что использовать операторы ?.  и  ??  с Unity.Object  и его наследников не следует. Но почему-то мне казалось, что переопределенный оператор на что-то влияет, только если мы имее дело с уничтоженным объектом. В моем же коде, по идее, этого не должно было быть. И потому это исключение оказалось неожиданным. Логично было бы предположить, что вместо  MissingComponentException будет генерироваться исключение  NullPointerException . Но, как вы уже поняли, этого не происходит. Согласно документации, метод  GetComponent :

Returns the component of Type type if the game object has one attached, null if it doesn’t.

Этот метод должен вернуть ссылку на компонент или  null , если компонент не найден. А далее приводится пример:

который «подтверждает» ранее описанное поведение. Но если бы это была бы правда, то мой код бы не генерировал исключение. А он генерирует. Дело в том, что метод  GetComponent возвращает ссылку на управляемый компонент, даже если компонент не найден. Иными словами, если запрашиваемый компонент не найден, то все равно возвращается ссылка на управляемый компонент, который не ссылается на настоящий C++ компонент и переопределенный оператор сравнения с  null  не срабатывает.  Создается ощущение, что вернулся  null . Это легко подтвердить, например, таким кодом:

В результате выполнения этого кода мы увидим:

UnityEngine.CanvasGroup
UnityEngine.Debug:Log(Object)
TestScript:Start() (at Assets/TestScript.cs:9)

Также стоит отметить, что, если сбилдить приложение, то тестовый код выполнится без ошибок, а ненайденный компонент будет добавлен. Происходит это потому, что в run-time  GetComponent будет возвращаться настоящий  null , и все выполнится, как и задумывалось. Т.е. подмена возвращаемого значения работает только в редакторе Unity. Само собой, не стоит использовать операторы  ?.  и  ??  с  Unity.Object  и его наследниками в реальном коде.

Статистика Android устройств, август 2017

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

Android приложение
Жанр: игра, три в ряд.
Установок: > 100K.
DAU: > 25K.
Аудитория: в основном женская.

ОС

Android 5 — 36,5%
Android 4 — 28,9%
Android 6 — 28,2%
Android 7 — 6,4%

Читать →

Пробуем TypeScript на сервере

Доброго времени суток.

В этой статье мы познакомимся с языком программирования TypeScript на примере создания небольшого серверного приложения (простая авторизация), используя такие популярные технологии как Node.js и Express.js. Также вкратце коснёмся рассмотрения довольно интересной embeddeb NoSQL базы данных NeDB.

Статья ориентирована прежде всего на людей уже знакомых с Node и Express и не рассматривает самые базовые вещи, относящиеся к этим технологиям. Я не буду подробно описывать и все особенности TypeScript, а код далеко не является эталонным — это всего лишь пример. Однако, если у вас есть желание попробовать TypeScript, но вы не знаете как к нему подобраться, особенно со стороны backend’a,  тогда, возможно, вы найдёте что-то полезное в этой статье. Ссылка на GitHub репозиторий в конце статьи.

TypeScript

TypeScript (далее по тексту TS) — это язык программирования, являющийся надмножеством JavaScript. Начал разрабатываться компанией Microsoft и был впервые представлен в 2012 году. Автор языка — Андреас Хейлсберг, который до TS приложил руку к разработке таких языков как C# и Delphi. В самом Microsoft заявляют о TS  следующим образом: «Язык программирования TypeScript является средством разработки веб-приложений, расширяющим возможности JavaScript».

Технология сама по себе довольно интересная и выгодно отличается от того же JavaScript. Главное, что выделяет TS — это строгая типизация, а также более привычная многим (java-like) реализация ООП. В TS вы можете использовать всё, что есть на данный момент в JavaScript, плюс объявления типов, модификаторы доступа, классы, интерфейсы и ещё много всего. Получается такой JavaScript с человеческим лицом. При этом компилируется TS в тот-же JavaScript. На выбор любой из стандартов: ES3, ES5 или ES6. Как и большинство новых технологий TS является open source проектом, доступным в репозитории на GitHub: https://github.com/Microsoft/TypeScript Ещё одним аргументом в пользу данного языка является тот факт, что на нём написан один из самых популярных frontend фреймворков — Angular2. И его можно использовать с другими библиотеками и фреймворками, например с тем-же React. Ну а мы попробуем разобраться насколько TS применим на сервере. И так, приступим.
Читать →

Поддержка Multidex для Android в Adobe AIR

Пару слов о поддержке Multidex для Android в AIR, которую Adobe ввела в 25-й версии. С помощью мультидекса можно упаковывать приложения, которые превышают лимит в 64K методов. Это может произойти, если вы пишете своё или используете стороннее ANE, содержащее большое количество библиотек и, соответственно, большое количество методов. В таком случае при сборке вы получите ошибку:

Too many method references max is 65536

AIR 25 наконец-то позволяет включить multidex, чтобы преодолеть эту проблему.

При разработке собственного ANE выставьте флаг поддержки multidex в gradle скрипте проекта

Далее, при упаковке ANE исключите библиотеку com.android.support:multidex. Её можно прописать в зависимостях проекта для компиляции Android библиотеки, но обязательно надо игнорировать при упаковке самого расширения, потому что иначе, при попытке сборки приложения, будет выводиться ошибка:

UNEXPECTED TOP-LEVEL EXCEPTION: java.lang.IllegalArgumentException: already added: Landroid/support/multidex/BuildConfig;

Видимо потому, что библиотека добавляется AIR’ом и повторное включение её в ANE создаёт конфликт.

Для приложения не забудьте прописать поддержку multidex в секции Android манифеста файла-дескриптора (android > manifestAdditions > manifest):

Обратите внимание, что если ANE содержит pre-dex библиотеки, то при попытке упаковки приложения будет выдаваться ошибка.