Привет!

Недавно мы с друзьями опубликовали игру Scratch Mania для платформы Windows Phone, на подходе Android. Здесь я расскажу о том, где же пропадал с момента последних публикаций о Kinect и о том, каково enterprise разработчику писать игры.




Об игре
В игре Scratch Mania игроку предлагается отгадать картинку, скрытую защитным слоем. Защитный слой стирается простыми касаниями экрана. Причем игрок может стереть только часть защитного слоя, это придает интерес и, одновременно, дополнительную сложность. Картинки объединены в альбомы по темам: спорт, автомобили, актеры, города и т.д. За каждую отгаданную картинку игрок получает внутриигровые монетки, которые может обменять на новые альбомы.

Мы задумали эту игру как тему хакатона на выходные: собраться у кого-нибудь, написать основу – работающий прототип, а потом за месяц, другой довести до состояния пригодного к публикации. А фактически потратили большое года. Больше года, Карл! Справедливости ради отмечу, что работа делалась как правило вечерами, в свободное время. А теперь время взглянуть на ключевые технические решения.

Семь раз отмерь


Если составить рейтинг самых популярных вопросов от людей, которым я рассказываю об игре, – этот вопрос на первом месте: “Гы, Windows Phone? Серьезно?..” Даже табличка «Сарказм» не нужна. И тем не менее мы выбрали эту платформу потому, что знаем C#. А еще Java тоже C-производный язык, так что решили и для Android версию запилить, только она в полу-готовом состоянии пока. В итоге выбор свелся к решению использовать ли «стильный, модный, молодежный» Xamarin или писать нативные приложения. Увы, но Xamarin проиграл. В основном из-за неоднозначных отзывов коллег и интернет-сообщества. Очень не хотелось тратить много времени на борьбу с фреймворком. А кроме того цена оказалась кусающейся — порядка $20 в месяц для индивидуальных разработчиков.

Java и .NET были не единственными претендентами, рассматривались так же и экзотические варианты как, например, javascript. Но слишком уж он медлительный. Кстати, прототипы мы потом использовали для сайта игры, не пропадать же добру.

Что касается версий Windows Phone и Android, для простоты решили остановится на WP 8+ и Android API 11+ соответственно. И здесь я в основном буду говорить о Windows Phone, т.к. об Android пока рано говорить.

Лиха беда начало


На прототипы мы потратили примерно две недели, вместо двух выходных, как планировали изначально. Начали с javascript. Идея простая: добавляем canvas на страницу, в нем рисуем картинку. Создаем еще один canvas, без добавления в DOM. В нём рисуем другую картику, при этом размеры canvas’ов одинаковые. В обработчике события OnMouseMove копируем область N?N пикселей из одного canvas’а, в другой. Получается, что-то вроде:


Но таким никого не удивишь, поэтому следующий прототип мы написали на Windows Forms, a сам алгоритм стирания был сделан по аналогии с инструментом airbrush из графических редакторов. В этом прототипе две картинки накладывались друг на друга и у верхней, в зоне касания, изменялось значение альфа-канала. Уверен, гуру javascript смог бы написать что-то подобное, но среди нас такого не нашлось.

Код из прототипа практически без изменений был перенесен на смартфоны. Оказалось, что компоненты Windows Phone и Android по-разному работают с альфа-каналом. В Windows Phone канал премультиплецирован (premultiplied), тогда как в Android нет. Это значит, что в Android значение альфа-канала не влияет на цветовые компоненты (RGB) пиксела, тогда как в Windows Phone они подвергаются изменению согласно формулам:
A = alpha
R = red ? alpha / 255
G = green ? alpha / 255
B = blue  ? alpha / 255

Изображения, представленные в таком формате, проще накладывать друг на друга, так как этом случае операция вычисления значения пикселя фактически сводится к сложению компонентов пикселей, например, Rres ~ R1 + R2. Подробности можно найти в книге Programming Windows (6th edition) by Charles Petzold (amazon).


Но беда не приходит одна. Cобытие Touch.FrameReported (onTouchEvent в Android) очень медленное на обеих платформах. Скажем, если мы подписываемся на это событие, чтобы в его обработчике вычислить новое значение для альфа-канала в точке касания, то получаем FPS около 20. Это очень мало, т.к. задержки становятся видны невооружённым глазом. В Android пока отложили и решили поправить ближе к релизу. В Windows Phone проблему решили с помощью фонового потока, который примерно раз в 25мс опрашивает экран, получает точки (их обычно несколько) касания (если есть) и вычисляет значение альфа-канала. Это позволяет держать FPS уровне 30-40.

Кроме того, нужно помнить, что в Windows Phone нет инвалидации части изображения — после любых изменений надо обновлять картинку целиком. Это также накладывает дополнительные ограничения на обработку касаний: чем больше точек за раз будут обработаны, тем лучше. С другой стороны — нельзя обрабатывать слишком много точек, т.к. будут заметны рывки.

Нет предела совершенству


Красивое стирание – первая «киллер фича», вторая – альбомы картинок. Понимая, что такой важный код требует соответствующего к себе отношения, к проектированию подошли серьезно. Расслоили систему – от низкоуровнего парсера отдельных альбомов, до высокоуровнего менеджера установленных альбомов и альбомов доступных для загрузки.
Об альбомах
В игре изначально предустановлено 3 альбома с картинками, остальные альбомы хранятся на нашем сервере. Это позволяет игроку самому решать какие альбомы ему интересны и загружать их, а нам позволяет упростить процесс распространения новых альбомов — игрок будет видеть их с пометкой «новый» в списке альбомов.

Вот тут мы дали маху, что можно охарактеризовать как “Энтерпрайз головного мозга”. Достаточно взглянуть на диаграмму классов.

На первый взгляд не все так страшно, но на диаграмме многое упрощено: не указаны некоторые типы, code contracts для всех интерфейсов, классы-помощники и т.п. Это семейство несколько раз подвергалось рефакторингу, переписывалось и в конце концов практически все интерфейсы и фабрики были удалены.

На ошибках учатся


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

Это практически все, что мы успели сделать за 4 месяца. Каркас был готов: можно было переключаться между предустановленными альбомами, отгадывать картинки в двух режимах (по буквам и выбирая из предложенных вариантов), просматривать уже отгаданные картинки. Но предстояло еще: нарисовать UI, реализовать подсчет очков, загрузку альбомов, интеграцию с социальными сетями – “рассказать друзьям”, добавить google analytics и внутриигровые покупки.

Все в «ажуре»


Серверную часть игры мы развернули в Microsoft Azure. Исходные картинки и готовые альбомы хранятся в Blob Storage. Для хранения метаданных альбомов, таких как идентификатор, физическое местоположение (в Blob’е), доступность в игре и т.п. используется Azure SQL. Работу с этими компонентами скрывает RESTful сервис. Сейчас это main stream, так что мы решили не отставать.

По сути наш сервис — это набор 3 методов: получит список альбомов, получить подробную информацию об альбоме, скачать альбом.



Для скачивания альбома сервис генерирует ссылку с ограниченным временем жизни. Azure предоставляет готовый механизм — Shared Access Signatures. Суть процедуры такая: в URL зашивается период действия, после этого URL подписывается приватным ключом. Таким образом исключается любое изменение URL. Дату можно и не указывать — достаточно создать политику доступа для контейнера, а в URL сослаться на эту политику. Однако этот вариант менее удобен в случае короткоживущих ссылок (на каждый новый период надо создавать новую политику).

Встречают по одёжке


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

На рисование эскизов у нас ушел примерно месяц. В качестве редактора был выбран Expression Design, просто потому, что: а) он у нас был, б) им удобно пользоваться и в) его файлы легко экспортируются в XAML или PNG. Поэтому позднее почти все стили и иконки получилось перенести без изменений в игру.
Design to PNG
Когда приходится экспортировать большое количество файлов Expression Design в PNG или JPEG, без автоматизации процесс может быть утомительным. Я написал небольшой конвертер использующий Expression Design API (это не документированный API, полученный путем изучения сборок одним популярным «рефлектором»).

Нам потребуются две сборки:
%ProgramFiles%\Microsoft Expression\Design 4\GraphicsCore.dll
%ProgramFiles%\Microsoft Expression\Design 4\Microsoft.Expression.Design.dll
И такой код:
const string from = @"..."; // путь к *.design файлу
const string to = @"..."; // путь к выходному *.png файлу
 
Module.Instance.Initialize();
var doc = Module.Instance.OpenDocument(from, false, null);
 
ExportImageInfoHandler exportHandler = doc.ExportImage(to, (Enum)ImageFormat.Png);
exportHandler.Info.Resolution = 96.0;
exportHandler.Info.DocumentUnits = UnitType.Pixel;
exportHandler.Info.ConstrainProperties = false;
exportHandler.Execute();


Другой, не менее важный элемент игры, влияющий на общее восприятие – это картинки, которые игрок будет отгадывать. Мы старались подбирать интересные и качественные, а также со свободной лицензией. На каждый альбом приходится порядка 20-30 картинок. Чтобы упростить себе жизнь, решили, что картинки должны быть одного размера. Побочным эффектом такого допущения стали дополнительные усилия при создании альбома, каждую картинку нужно подогнать по размеру. А что если разрешение, которое мы решили использовать сегодня, завтра изменится?

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


Что посеешь, то и пожнешь


На рекламу игры мы потратили около 3500 р. Сюда входит реклама в ВКонтакте, Bing и Windows Store. Как показала практика реклама в Reddit, все равно что покупка снега зимой: $20 – 0 установок.

Зато работает публикация новостей в группах посвященным мобильным играм в социальных сетях. Самый простой способ — находить группы посвященные играм, windows phone и т.п. и связываться с администратором с просьбой опубликовать рекламу. Сейчас мы продолжаем работать в этом направлении, в основном ищем контакты людей готовых опубликовать обзор игры. Пока на встречу пошли только ребята из 1800 Pocket/PC и опубликовали обзор.

Версия для Android из-за релиза пока не очень активно разрабатывается. Это цель на ближайшее время. Будет ли версия для iOS? Пока не ясно и во многом зависит от успешности игры для Windows Phone и Android.

P.S. Так каково же enterprise разработчику писать игры? Поучительно. Не важно проектируешь ли ты сложные высоконагруженные системы, автоматизируешь бизнес-процессы или пишешь свой интернет-магазин – keep it simple. Всё. Банально… :)

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