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

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

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

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

 

Invalid AABB inAABB

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

Аналогичная ошибка падала при попытке отрисовать UI в текстуру во время анимации открытия окна.

Хорошо, что эта ошибка падала сразу при старте и с завидным постоянством повторялась. А ведь аналогичная проблема могла возникнуть в другом месте, и тогда эту ошибку бы пропустили, а исправление заняло бы еще больше времени. Столкнувшись впервые с подобной ошибкой, я обратился к всезнающему гуглу, для которого эта ситуация уже была известна. В основном были ответы с ошибками в расчетах, приводящих к невалидным значениям, наподобие  NaN; в некоторых случаях помогало отключение сортировки дочерних канвасов; в других случаях проблемным местом была система частиц. Ну, и другие подобные проблемы. Особняком стоял баг-репорт с отметкой Won’t Fix, что не могло не «радовать» (.

Перепробовав все из найденного, мы поняли, что у нас проблема в чем-то другом. Пытаясь найти игровой объект с компонентами с невалидными размерами или скейлом, мы пришли к мысли, что, возможно, проблема в некорректных размерах одного из спрайтов в спрайт-листе. Само собой, найти вручную спрайт нелегко. Благо, редатор Unity легко расширяем. Была добавлена временная команда, и злополучный спрайт с нулевым размером был найден. Для генерации спрайт-листов мы используем небезызвестный TexturePacker, который умеет автоматически генерировать спрайт-листы под разные разрешения. Один из спрайтов имел ширину в 1 пиксель; при его масштабировании в сторону уменьшения получился спрайт с шириной в 0 пикселей. Таким образом, TexturePacker сгенерировал невалидный спрайт-лист и не только не выдал ошибку, но даже не показал предупреждения. Аналогичным образом поступил и плагин TexturePacker-а для Unity — ни ошибок, ни предупреждений, все отлично. Unity не отстает, при импорте получившегося спрайт-листа все прошло гладко. И только в run-time при попытке отрисовать невалидный спрайт Unity чертыхнулась, да и то, вместо того, чтобы выдать вразумительную ошибку, выдала нечто, что выкидывается, как оказалось, в куче других случаев. Само собой, чтобы подобной ошибки не произошло в будущем, был написан тест, наподобие этого:

 

Missing References

Думаю, сложно найти разработчика на Unity, который бы никогда не сталкивался с проблемой испорченных ссылок в префабах, игровых сценах или в ScriptableObject-ах. Эта проблема стара, как и сама Unity. Ссылки на объекты могут теряться совершенно по разным причинам: случайное удаление (или изменение) .meta файлов; мерджи (как с конфликтами, так и без) сцен, префабов и т.п.; проблемы и конфликты с Cache Server-ом; затыки при переключении сильно несовместимых веток и т.д. и т.п. Не так важно, из-за чего возникает эта проблема, как то, что нет каких-то стандартных средств для борьбы с ошибками такого рода. Поэтому каждый разработчик борется с этой проблемой самостоятельно. И, если обратиться к поисковику, легко можно найти заветные строчки кода, позволяющие обнаружить missing references в сценах, префабах и т.п. Коварство этой проблемы состоит в том, что даже при билде приложения Unity не заикнется о проблеме, вследствие чего очень легко выкатить в релиз приложение, которое будет глючить. И, если посмотреть на код, то, чаще всего, это какие-то команды редактора, которые необходимо запускать вручную (видимо, перед билдом?), что странно и чревато ошибками из-за человеческого фактора. Гораздо логичнее написать тест, который будет запускаться (вместе с остальными тестами) перед сборкой приложения и фейлить билд в случае обнаружения проблемы. Для написания теста достаточно простого метода, определяющего наличие некорректных ссылок. Что-то наподобие такого:

Ну и пример теста префабов:

 

Шейдеры

В счастливые времена Flash-а, чтобы показать простую анимацию открытия/закрытия окна через альфу и скейл, требовалась всего пара строчек. И, хотя тогда тоже существовала проблема с наложением нескольких прозрачных спрайтов при наложении альфы, решалась она одной строчкой:  window.blendMode = BlendMode.LAYER;. В Unity, к сожалению, такого простого решения нет, а проблема есть. И для решения этой проблемы можно попробовать отрисовать окно в текстуру и анимировать не само окно, а эту текстуру. Само собой, у такого решения есть куча плюсов и минусов. И кому-то оно, возможно, не подойдет вовсе. Но даже в нем есть свои проблемы. Если у вас есть полностью непрозрачное изображение (например, фон окна) и поверх него прозрачное изображение (иконка или панелька), то при отрисовке такого окна на пересечении полностью непрозрачного и прозрачного получится (готовы?) полупрозрачное изображение. Т.е. сквозь ваше непрозрачное окно можно увидеть то, что находится под ним. Происходит это из-за того, что стандартный UI шейдер производит отрисовку с наложением

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

Благо, канвас позволяет изменить материал через  Canvas.GetDefaultCanvasMaterial(). И, в принципе, это работает в большинстве случаев. Но в некоторых случаях различия все равно видны, например, если весь фон окна полупрозрачный, а поверх него распологаются прозрачные иконки.

Анимация на основе отрисовки окна была реализована и работала в редакторе, но при тестировании на устройстве мы увидели фиолетовые прямоугольники вместо окон. Опытные 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 библиотеки, то при попытке упаковки приложения будет выдаваться ошибка.

Unity для новичков. Простая камера от третьего лица. Часть 1

Доброго времени суток!
Сегодня мы рассмотрим пример создания простой игровой камеры от третьего лица в Unity.

Читать →

GDG DevFest Voronezh 2016 — ключевые тренды

3 декабря 2016 года в Воронеже проходила 5-я ежегодная конференция Google developer group, на которой нам впервые удалось побывать. Сразу скажу, что конференция прошла отлично и мы остались очень довольны :)  Поэтому хотелось бы поделиться своими впечатлениями и подробнее рассказать о самой конференции, а также о докладах, которые были на ней представлены.

img_20161203_140430

Эта конференция, в отличие от многих остальных, является абсолютно бесплатной. Для участия достаточно было просто зарегистрироваться на сайте компании-организатора. В Воронеже организаторами были ребята из DataArt. 
Читать →

Поездка на GDG DevFest Voronezh 2016

3 декабря удалось побывать на GDG DevFest Voronezh 2016. GDG DevFest это ежегодная конференция, проводимая сообществами Google Developers Group (GDG) при поддержке компании Google в разных городах мира. В Воронеже она проводится пятый раз.

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

maxresdefault_live

GDG проходил в актовом зале ВГУ. Сразу на входе нас встречали волонтеры, проводившие нас на регистрацию. Регистрация прошла моментально, а до начала открытия оставалось чуть менее часа. Благодаря тестовым заданиям от компании DataArt удалось скоротать время и получить наклейку с андроидом, которая теперь украшает мой ноутбук.

В первом выступлении Денис Неклюдов (Google Developer Expert и ведущий подкастов androiddev) и Звиад Кардава (также Google Developer Expert) рассказывали о Project Tango. Project Tango — это платформа, которая использует компьютерное зрение, чтобы научить мобильные устройства воспринимать окружающее пространство и движение.
Читать →