Pull to refresh
0
Rating

Создание и продвижение Symbian-хита. Часть 1-я. Разработка

Microsoft Lumia corporate blog
В этом посте публикуется история о том, как создавалось приложение Magic Brush, и о трудностях, возникших в процессе его разработки. На данный момент Magic Brush — одно из наиболее популярных приложений в Магазине Ovi, что обеспечивает стабильный доход. В хорошие времена количество продаж приложения только в Магазине Ovi достигает 4K копий в месяц. А Lite-версия была загружена в нем около 1,7M раз. Но забавно то, что задумывалось вначале совсем другое приложение. Обо всем по порядку под катом.


Работа программы Magic Brush:


Страничка программы: http://www.tarasov-mobile.com/magicbrush.html

Что задумывалось, или движок для игры


Все началось в феврале 2010 года. Тогда я купил себе свой первый смартфон — Nokia 5800 Express Music. Поигравшись примерно месяц с новым устройством, я вспомнил свою детскую мечту и решил написать игру для touch-телефона. И все, чего я тогда хотел, — это:
  • приобрести новый опыт;
  • получить фан от разработки;
  • возможно, заработать немного денег. :)

Я выбрал жанр физической 2D-головоломки, но мне хотелось, чтобы моя игрушка чем-то выгодно отличалась на фоне уже имевшихся Symbian-игр. После изучения контента из Магазина Ovi я решил сделать игру с векторной графикой, сглаживанием и субпиксельным позиционированием объектов в игре. И как только решение было принято, я взялся за реализацию. Первым делом нужно было создать движок.

Знакомство с Symbian SDK сперва повергло меня в шок. Во-первых, Symbian C++ показался мне крайне странным и непривычным. Во-вторых, смартфоны на Symbian^1 (5800, N97 и пр.) были построены на довольно слабой аппаратной платформе без графического ускорителя на борту. В SDK были библиотеки OpenGL, но они были реализованы программно. В-третьих, несмотря на поддержку CPU смартфона операций с числами с плавающей точкой, компилятор SDK не умел генерировать код, выполняющий операции с числами с плавающей точкой аппаратно.

Первая проблема была решена довольно быстро. К счастью, писать код можно не только на Symbian C++, но и на стандартном C++. И так как моя игра с Symbian API практически не работала, то на Symbian С++ была написана только инициализация, взаимодействие с сенсорами и таймером. Все остальное было успешно написано в классическом C++ с использованием STL.

Разобравшись немного с прелестями Symbian C++, я начал заниматься вопросами вывода графики на экран. У меня не было опыта работы с OpenGL, к тому же на Symbian^1 OpenGL не дало бы прироста производительности (т. к. нет ускорителя). Зато в Symbian обнаружилась очень приятная возможность — Direct Screen Access (DSA). С помощью DSA я смог получить прямой доступ к видеопамяти устройства.

Отлично, доступ к видеобуферу получен, теперь можно на максимально возможной скорости программно рендерить в него все, что душе угодно. Довольно быстро были написаны методы вывода в буфер спрайтов, а для сглаженных объектов векторной графики была подключена библиотека Anti Grain Geometry (AGG). Вернее, ее очень облегченная версия: AGG Lite. С использованием AGG были быстро написаны методы для вывода на экран простых графических примитивов (линия, круг и т. п.). Все работало достаточно шустро, и картинка радовала глаз.

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

И тут начались проблемы. Картинка на экране выглядела отлично, все линии были сглаженными, а за счет субпиксельной точности вывода все движения объектов казались очень плавными… Пока движущихся объектов на экране было мало. Если же на экран добавлялось 10–12 игровых векторных объектов, то fps катастрофически падал. Нужно было что-то делать, чтобы спасти положение.

Первым делом вся арифметика с плавающей точкой была переписана на арифметику с фиксированной точкой. В этом помог класс Fixed из Box2D, затем были добавлены предварительно рассчитанные таблицы для вычисления тригонометрических функций, оптимизированные методы для вычисления квадратного корня и случайных чисел. В проекте не осталось ни одного использования чисел с плавающей точкой. Это дало хороший прирост производительности, но все же недостаточный. Удалось найти несколько мест в AGG, поддающихся оптимизации. Например, отключение смешивания цветов и включение простого копирования для вывода непрозрачных участков объектов. Это добавило еще пару fps, но поднять fps выше 15 все же не удалось. Играть на 15 fps было возможно, но… Пришлось думать над другим геймплеем, в котором было бы меньше динамики и больше статических объектов.

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

Что получилось, или Magic Brush


И тут мне на глаза попалась эта статья на Хабре: Дмитрий Рыжков и его My Little Artist завоевали солидный приз в конкурсе на лучшее приложение для Intel Atom. Посмотрев на My Little Artist в работе и взглянув на harmony by mr.doob, я понял, что могу очень быстро реализовать нечто подобное на уже имевшемся движке. Всего за 15 минут была добавлена и опробована первая кисточка, и она работала великолепно! Я проанализировал и переписал код кистей harmony на C++ с некоторыми оптимизациями. Также были добавлены девять новых кистей, написан UI приложения, а у каждой кисти появились дополнительные настройки (цвет, толщина линии, прозрачность и др.).

Работу различных кистей можно увидеть на этом видео:


Ну а в качестве примера я расскажу, как реализовывалась кисть Smoke:



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

virtual void StrokeStart(Fixed x, Fixed y) = 0;
virtual void Stroke(Fixed x, Fixed y) = 0;
virtual void StrokeEnd(Fixed x, Fixed y) = 0;


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

agg::rgba8 color;
Fixed opacity;
Fixed width;


и указатель на текущий буфер для вывода:

BufferGraphics* graphics;


Для реализации кисти Smoke понадобится из тонких линий — «струй» дыма — скомпоновать более толстую линию. Для этого нужно знать текущее положение каждой «струи» относительно текущего положения пальца пользователя. С этой целью в SmokeBrush используется вектор точек points:

std::vector<Point> points;


Также необходимо запоминать предыдущее положение пальца, чтобы провести линии из него в текущее положение:

Point previousFingerPosition;


Логика работы кисти описана в методах StrokeStart и Stroke:

void SmokeBrush::StrokeStart(Fixed x, Fixed y)
{
    points.clear();
    for (int i = 0; i < (int)(width * Fixed(3)) + 16; ++i)
    {
        Fixed r = (fixrand() * Fixed(0.4f) + Fixed(0.1f)) * width;
        Fixed a = fixrand() * Fixed::PI2;
        points.push_back(Point(r * cos(a), r * sin(a)));
    }
    previousFingerPosition = Point(x, y);
}


При касании экрана необходимо очистить вектор points от значений, которые остались в нем после предыдущего использования кисти. Затем в этот вектор записывается новое облако точек. Причем чем больше толщина кисти, тем больше «струй» понадобится для отображения кисти на экране. Каждая отдельная «струя» располагается на случайном расстоянии от точки касания экрана, но не более чем половина толщины кисти. Также необходимо запомнить текущую точку касания для последующего использования.

Ранее я писал, что в проекте не осталось ни одного использования чисел с плавающей точкой, но в тексте можно увидеть float. К счастью, компилятор достаточно умен, чтобы правильно обработать inline-конструктор класса Fixed. В результате на этапе выполнения работа действительно происходит только с целочисленными значениями.

В методе Stroke производится вывод на экран:

void SmokeBrush::Stroke(Fixed x, Fixed y)
{
    for (vector<Point>::iterator i = points.begin(); i != points.end(); ++i)
    {
        Fixed newX = i->x + (fixrand() - Fixed(0.5f)) * width * Fixed(0.25f);
        Fixed newY = i->y + (fixrand() - Fixed(0.5f)) * width * Fixed(0.25f);
        Fixed d = newX * newX + newY * newY;
        if (d > width * width * Fixed(0.25f))
        {
            newX = i->first;
            newY = i->second;
        }
        graphics->DrawLine(previousFingerPosition.x + i->x,
        previousFingerPosition.y + i->y,
        x + newX, y + newY,
        width * Fixed(0.0625f), color);
        i->x = newX;
        i->y = newY;
    }
    previousFingerPosition = Point(x, y);
}


В цикле осуществляется обход всех точек в облаке. Для каждой точки вычисляется небольшое случайное смещение относительно ее текущего положения. Если точка сместилась слишком далеко от точки касания экрана (больше чем на половину толщины кисти), то она остается на своем старом месте. Затем осуществляется рисование отдельной «струи» на экране, причем толщина отдельной «струи» зависит от толщины кисти в целом. В конце цикла смещенная точка записывается обратно в points. Это позволяет получить волнистые тонкие линии в составе более толстой кисти.

Таким нехитрым способом можно сделать кисть, которая в умелых руках может стать полезным инструментом для рисования:



Запуск продукта


Итак, примерно через месяц после прочтения статьи на Хабре, Magic Brush был полностью готов. Еще в процессе работы над программой я задумался над тем, каким образом я буду ее продавать, и понял, что маркетинг отнимает колоссальное количество времени. Параллельно с разработкой я изучал различные материалы о продвижении приложений в Store’ах и однажды в процессе гугления наткнулся на статью Дмитрия Тарасова «Мобильное ПО как бизнес». Это стало началом нашего сотрудничества и нового, не менее интересного, этапа развития Magic Brush — маркетинговой магии. Но об этом в следующей части нашего рассказа.

А напоследок — линк на рисунки, выполненные в MagicBrush: http://vk.com/album-19391365_127709331.
Tags:
Hubs:
Total votes 40: ↑32 and ↓8 +24
Views 13K
Comments 16
Comments Comments 16

Information

Founded
Location
Финляндия
Website
www.microsoft.com
Employees
1,001–5,000 employees
Registered