Как стать автором
Поиск
Написать публикацию
Обновить

История создания мобильного приложения для детей позволяющего зарабатывать деньги своим умом. Продолжение

Время на прочтение11 мин
Количество просмотров18K

Продолжение истории разработки мобильного приложения iOS/Android для мотивации детей заниматься математикой


Это вторая часть истории (вперемешку с рассказом о моих ошибках и их решениях) о том, как я (где-то два года в свободное время) разрабатывал мобильное приложение (под iOS/Android), которое бы мотивировало мою дочь решать примеры по математике. В итоге, получилось приложение, позволяющее ребёнку зарабатывать деньги своим умом.


Первую часть читайте тут.


План второй части


  • О написании кода
  • О контроле версий
  • Об озвучке
  • Об иконке
  • О сборке под Android и о размере
  • О сборке под iOS и о размере
  • О названии и продвижении
  • Статистика
  • О чём жалею
  • Что понял
  • Ссылки

Мелочи в программировании, которые упрощают мне жизнь


  • Ещё с Mono продолжаю использовать //TODO (в комментариях) для отметок мест, которые нужно доработать. Потом, все эти места можно удобно мониторить на закладке Task List (вызывается Ctrl+\, T):

Использования //todo для создания списка задач


  • Недавно начал пользоваться такой фичей как #region, чтобы, к примеру, прятать области перечисления переменных:

использование regions в коде


  • удобно обёртывать часть кода какими-то конструкциями с помощью последовательности горячих клавиш Ctrl+K, Ctrl+S. К примеру, выделяю часть кода, который нужно поместить внутрь if и жму Ctrl+K, а потом сразу же Ctrl+S, ввожу название нужно оператора (if) и жму Enter.
  • очень упрощает работу с кодом, если придерживаться единого стиля, к примеру, как здесь — C# Style Guide with Unity in mind.
  • я очень рад, что подружился с корутинами (Coroutine) — это, если грубо, такие функции, которые растягивают своё выполнение на несколько фреймов. Т.е. они не пытаются любую задачу выполнить за один кадр (просчитывая его полсекунды, понижая тем самым быстродействие до 2 FPS), а делают немножко в первом кадре, немножко во втором и так далее, чтобы игра не тормозила. Другое их назначение — выполнять что-то такое, что не нужно делать прям в каждом кадре (к примеру, проверять что-то 3 раза в секунду).

Пример ниже показывает корутину светофора, которая бесконечно (каждые десять секунд) зажигает по очереди жёлтый, красный, зелёный свет:


IEnumerator TrafficLights()
{                                 
    while (true)
        {
            yield return new WaitForSeconds(10f);      
            yield StartCoroutine(this.GetComponent<Lamp>().BlinkYellow());
            yield StartCoroutine(this.GetComponent<Lamp>().BlinkRed());
            yield StartCoroutine(this.GetComponent<Lamp>().BlinkGreen());
        }
}

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


  1. сначала идёт ожидание 10 секунд (первый yield),
  2. потом запускается корутина BlinkYellow (второй yield) и пока она не завершиться, корутина BlinkRed не запуститься,
  3. по завершению BlinkYellow запускается BlinkRed,
  4. по завершению BlinkRed запускается BlinkGreen,
  5. дальше цикл повторяется с начала (смотреть пункт 1).

Контроль версий, ну просто очень надо!


Видео, которое мне помогло начать использовать GitHub в связке с Unity — How to use GitHub with Unity.


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


  • Я перестал боятся делать какие-то кардинальные изменения в коде, что могут запороть проект.
  • Очень легко вернуться к прежнему рабочему состоянию.
  • Удобно сравнивать файлы с кодом.
  • Можно наглядно видеть, что поменялось в коде.
  • Неважно что случается с моим компом — у меня всегда есть запасная версия в облаке.

Вот только при работе с контролем версий нужно приучить себя комитить (постить обновления) очень маленькими порциями. Чтобы не было как у меня поначалу — изменил 20 файлов, добавил тонну нового функционала и закомитил всё это за один раз под названием «Фух, теперь оно наконец-то работает».


Хорошо, что я когда-то разобрался, что лучше не добавлять в репозиторий файлы кеша (папка Library), временные файлы (папка tmp, obj), и всё что Unity генерит на лету. По сути, в репозиторий нужно было добавлять только папки Assets и ProjectSettings.


Не обязательно использовать GitHub, ведь его минус в том, что в бесплатном аккаунте нельзя сделать код приватным (видимым только для себя). Но зато есть GitLab или Bitbucket, где это возможно.


Как я тпрпрррукал в микрофон


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


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


Чаще всего логика создания звукового эффекта в Audacity была следующей:


  1. Обрезал всё лишнее.
  2. Использовал эффект Noise Reduction (чтобы убрать шум с записи).
  3. Добавил звуку мультяшности эффектом Wahwah.
  4. Эффектами Bass and Treble игрался с частотами, чтобы звук звучал более смачно на динамиках смартфонов.
  5. Если я что-то говорил, то эффектами Change Pitch, Speed и Tempo изменял звучание своего голоса на бурундучковый.

Поначалу, у меня сильно заметно отставал звук от события (не в редакторе, а на смартфоне). Помогла опция DSP Buffer Size выставленная в режим Best latency (Edit -> Project Settings -> Audio) и вот эти советы (как в вопросе, так и в ответе).


Как дочь генерировала идеи иконок


Пока я разрабатывал приложение, то пробовал несколько раз создавать иконки самостоятельно, но ни одна из них не прижилась (да и по правде говоря, выходила какая-то муть). Поэтому, когда дело дошло до открытой публикации, я поручил разработку иконку дочери. Объяснил ей, что иконка должна быть привлекательной и объяснять смысл приложения. Она подошла с большим энтузиазмом и моментально нарисовала совершенно непригодную картинку. Я её забраковал и объяснил, что именно нужно улучшить или не использовать вовсе. Дальше всё пошло, как и ожидалось — она обиделась и перестала рисовать иконки.


Спустя полмесяца, обида прошла и она подошла к заданию более основательно: нарезала из тетради в клеточку квадратиков (по размеру как иконка на экране её iPod-а) и начала рисовать варианты один за другим:


Варианты иконок, нарисованные дочерью


Приносила мне. Я выбирал понравившиеся мне идеи. Говорил, в каком направлении двигаться дальше. Она уходила рисовать следующие варианты. Спустя два дня и три десятка вариантов мы остановились на правом нижнем (фото выше).


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


Прогресс создания иконки


Сборка под Android


С созданием моего первого APK-файла мне помогло разобраться вот это видео Unity Android Build — How to, где рассказывается как всё настроить, не устанавливая тяжеленную Android Studio.


Повторюсь, что у меня нет устройств с Android-ом (я просто прошу друзей потестить после новой сборки) и поэтому ошибки с устройствами, что показаны на видео — у меня отсутствовали.


Но без задоринки всё равно не обошлось. Пришлось долго повозиться с обновлением Android SDK, поскольку Unity не хотела работать с самыми последними обновлениями. В итоге, оказалось, что нужно было дать возможность Unity самому выбирать и устанавливать нужную версию обновления. Т.е. я просто нажал кнопку Update Android SDK в самом Unity, а не пытался обновить всё самостоятельно, как это показано в видео.


Проблема при обновлении Android SDK вручную

Пока я не догадался, что нужно обновляться из-под Unity, я долго страдал, обновляясь вручную с помощью скрипта sdkmanager.bat (нужно использовать именно этот скрипт с параметром --update, а не android.bat, который указан в видео). Он во время обновления жаловался, что не может перезаписать папку tools (из которой же сам и запускался). Пробовал кучу советов и уже даже не помню, что помогло, кажись какое-то решение с symboliс links.


Ещё у меня выскакивали непонятные ошибки, связанные с java. Оказалось, что проблема с версией Java Development Kit — его также не нужно было устанавливать самую последнюю версию. Я попробовал несколько старых и на одной из них ошибки пропали и всё заработало.


С подписью apk-ашки для публикации в Google Play мне помогло видео How to sign a Unity app for the android market. Я хорошенько сохранил и забекапил эти два пароля, поскольку их нужно вводить каждый раз при создании нового билда. А если их потерять, то нельзя будет обновить приложение (только выпустить новое с новыми ключами).


Иконку, в отличии от iOS версии, нужно изначально делать со скруглёнными углами, иначе на некоторых устройствах, она так и останется идеально квадратной.


Размер сборки под Андроид


Сейчас размер моего apk-файла 22МБ.


Я хорошо уменьшил размер сборки с помощью формата сжатия текстур/спрайтов с потерями — RGBA Crunched ETC2 (для спрайтов с альфой) и RGB Crunched ETC (для текстур без альфа-канала). Главное, нужно было выставить Compressor Quality = 100 (этот параметр не влияет на быстродействие текстур на конечном устройстве, а только на качество текстуры и на скорость сжатия в редакторе).


Любые другие танцы с бубнами не позволили мне добиться какого-либо существенного уменьшения размера apk-файла (кроме некоторых нюансов, описанных ниже).


Сейчас я использую следующие..


.. настройки из Player Settings..


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


  • Unity 2018.1 (чем новее версия, тем лучше она пакует и тем больше всяких вкусностей).
  • Auto Graphics API (Unity сама выбирает OpenGLES2 и 3).
  • Scripting Backend — Mono (он может и не быстрее, чем IL2CPP, но зато сборка получается однозначно меньшего размера).
  • Target Architectures — ARMv7 и x86. Убрав поддержку Intel x86, я уменьшил размер apk ещё на 10МБ (стало бы 12МБ). Но я не знаю, как много устройств я отсекаю снятием этой галочки. То что я знаю — под эту архитектуру телефоны уже года два как не выпускаются. А те, что были, можно сосчитать на пальцах одной руки (последние, что я знаю это Asus Zenfone, Lenovo K 900, Xolo x1000 и ещё может быть какие-то). Поправьте меня, пожалуйста, в комментариях, если я чушь несу.
  • Stirpping Level — Strip Assemblies — безопасно обрезает ненужный код. Другие опции может и делают какой-то выигрыш для размера (у себя я не заметил), но не такие безопасные.
  • Build System — Gradle (позволяет активировать Minify опцию).
  • Minify — Proguard — укорачивает названия всех классов и переменных, чтобы сэкономить на размере жалкие байты, но, как бонус, делает код менее читабельным для препятствия обратной инженерии.

Сборка под iOS


Собираю я приложение на макбуке жены (помню, брали самый дешёвый MacBook Air 2015-го года).


Чтобы собирать под iOS 7 даже в XCode версии 9.3 оказалось достаточно вручную вбить значение 7.0 в поле Deployment Target (на закладке General).


Чтобы добавить языки (на которые я перевёл приложение с помощью Lean Localization) в описание приложения в AppStore, нужно было добавить следующие строчки в файл info.plist (в любое место, к примеру, сразу после <dict>):


<key>CFBundleLocalizations</key>
<array>
<string>English</string>
<string>Russian</string>
</array>

Сейчас я использую Unity 2017.4 для сборки под iOS (как я писал выше, мне нужна поддержка iOS 7, иначе я бы использовал 2018.1).


Размер сборки под iOS


Сейчас размер закачиваемого файла под мой айфон — 44 МБ.


К сожалению, под iOS я не могу использовать Crunched спрайты, поскольку они не поддерживаются на девайсах старее iPhone 5s (без поддержки Metal и OpenGL ES 3.0).


Когда я пробовал уменьшить размер билда любой ценой, то самый маленький размер (34 МБ) получался если:


  • использовать Unity 2018 (т.е. минимальная поддерживаемая версия — iOS 8),
  • Graphics API выбрать только Metal,
  • оставить Target Architectures только x64,
  • использовать Crunched textures.

Но я ещё совсем не разобрался, как работать с app thinning — это такая штука, которая позволяет апстору автоматически собирать моё приложение под конкретный iPhone/iPad/iPod конечного пользователя (что приводит к уменьшению размера скачиваемого файла). Я только успел статей в закладки себе набросать:



Мои Player Settings, которыми хочу поделиться:


  • Удобно один раз ввести Automatic Signing Team ID (его можно взять на сайте Apple Developer здесь Account > Membership) и поставить галочку Automatically Sign, чтобы не делать потом это каждый раз в XCode.
  • Accelerometer Frequency я поставил в Disabled (отключил акселерометр), поскольку я его не использую. Может сберегу людям немного батарею.
  • Behavior in BackgroundSuspend — так приложение не закрывается при нажатии кнопки Home, а продолжает работать в фоне (отловить это событие можно с помощью OnApplicationPause, чтобы сохранить игру, к примеру).

Как я выбирал название и что использовал для продвижения


Сколько пользовался Booking-ом и TripAdvisor-ом, но никогда не замечал, что их название в AppStore не просто Booking, а «Booking.com Travel Deals» и «TripAdvisor Hotels Restaurants» (название разбавлено ключевыми словами, чтобы по этим ключевым словам приложение находили в поиске апстора). Поэтому я решил не использовать старое название Math4Ami, а долго игрался с разной формулировкой, чтобы включить туда нужные мне ключевые слова. В итоге, сначала я пришёл к «Pocket Money: Math», а позже к «Learn Math & Earn Pocket Money». На русском эти варианты звучали как «Карманные деньги за математику» и «Карманные деньги и математика: учись и зарабатывай».


Продвигал я своё приложение рассылая/публикуя ссылки на него друзьям и знакомым, написал две статьи у себя на блоге и там же повесил баннер.


Кстати, если поместить в <head> web-страницы вот такую строчку:


<meta name="apple-itunes-app" content="app-id=1372434662" />

.. то, открывая эту страницу на iPhone, все будут видеть вверху баннер вашего приложения.


Статистика скачиваний


Сперва я выпустил iOS версию.


Статистика скачиваний приложения на iOS


Очень многие говорили о нелюбви к яблочным девайсам и убеждали, что Андроид куда круче. Я решил проверить и, спустя 20 дней разбирательств со сборкой apk-файла, сделал релиз в Google Play.


Статистика установок приложения на Android


Объективные выводы тут не сделаешь (или вообще никакие), но чисто субъективно — я ожидал большего от Google платформы, но она, на данный момент, оказалась в половину хуже Apple.


Я жалею о том, что:


  • не делал бекапы при переходе на новую версию Unity,
  • не использовал систему контроля версий (вроде git),
  • пытался работать с Perforce (просто ужасная система — она нереально тормозит разработку),
  • не использовал ScriptableObject (SO) раньше — это настолько круто,
  • писал код в одну длинную простыню (аж самому страшно вспоминать, как я эту простыню потом рефакторил),
  • не знал что такое запахи кода, а о них ой как нужно было знать,
  • старался везде использовать глобальные переменные, а не локальные (параноидально думая, что если я буду часто запрашивать функцию, то локальная переменная внутри функции будет создаваться снова и снова, а глобальная бы была только одна. В итоге, после сотни вызовов функции с локальными переменными, у меня была бы куча истраченной памяти и потеря производительности из-за Garbage Collector),
  • не всегда придерживался единого стиля в коде и в названии всех ассетов. Вот хороший пример для подражания для UE4 (там много полезного об именовании ассетов и я применяю это также и в Unity),
  • забывал, что я пишу приложение в первую очередь для дочери, а не ради красивого кода или в угоду другим пользователям.

Я понял что:


  • дети очень легко подсаживаются на моё приложение (сразу после установки приложения, они могут часами в нём зависать, пока батарея у телефона не сядет),
  • детей затягивает делать математику в моём приложении, даже если им не говорить о деньгах. Если детей в семье двое, то доходило даже до драк за телефон, чтобы порешать примеры,
  • детям очень быстро наскучивает моё приложение. На следующий день, редко кто игрался в приложение, даже если ребёнок понимал, что получит за это деньги,
  • плохое удержание пользователя/ребёнка — это не совсем проблема моего приложения конкретно, а специфика обучения детей (это мне школьные учителя рассказали). Приложение должно быть ну очень разнообразным, чтобы всё время поддерживать интерес у детей,
  • довольно часто родители и дети неверно истолковывают принцип работы приложения и думают, что решая примеры они получат деньги от меня (от разработчика). Мне приходили разные письма с вопросами типа: как выводить деньги, можно ли вывести на webmoney или paypal, где указывать адрес для отсылки денег чеком и т.д.,
  • если в ответах к плохим отзывам извиняться за свои ошибки и недосмотры (а также объяснять, как оно должно работать на самом деле), то люди склонны прощать ошибки и убирают плохие оценки,
  • на бесплатном продвижении своими силами далеко не уедешь,
  • дети растут очень быстро и пока я писал app — моя дочь его уже переросла. Но, у меня подрастает новый «тестер»!

Ссылки


Список ссылок из статьи в очерёдности их упоминания:


+ Как держать единый стиль кода C# Style Guide with Unity in mind.
+ GitHub и видео о том, как прикрутить git к Unity.
+ Бесплатный приватный репозиторий есть на GitLab или Bitbucket.
+ Бесплатная Audacity для записи и редактирования аудио.
+ Как убрать задержку звука.
+ Видеоурок Unity Android Build — How to.
+ Видео How to sign a Unity app for the android market.
+ Устройства Apple, поддерживающие Metal/OpenGL ES 3.
+ App thinning (Apple documentation).
+ App thinning (Unity help).
+ AssetBundle Workflow (Unity documentation).
+ Где брать Team ID.
+ Что такое запахи кода.
+ Принцип именования ассетов/ресурсов в игровых проектах на UE4.


Спасибо, что дочитали!

Теги:
Хабы:
Всего голосов 39: ↑36 и ↓3+33
Комментарии14

Публикации

Ближайшие события