Creation and promotion of a Symbian hit. Part 1 Development

    In this post, a story is published about how the Magic Brush application was created, and about the difficulties that arose during its development. At the moment, Magic Brush is one of the most popular applications in the Ovi Store, which provides a stable income. In good times, the number of app sales in the Ovi Store alone reaches 4K copies per month. And the Lite version was downloaded about 1.7M times. But what’s funny at the beginning was a completely different application. Everything in order under the cut.


    The work of the Magic Brush program:


    Program Page: http://www.tarasov-mobile.com/magicbrush.html

    What was conceived, or the engine for the game


    It all started in February 2010. Then I bought my first smartphone - Nokia 5800 Express Music. After playing with the new device for about a month, I remembered my childhood dream and decided to write a game for the touch-phone. And all I wanted then was:
    • gain new experience;
    • get fan from development;
    • maybe make some money. :)

    I chose the genre of physical 2D puzzle, but I wanted my toy to compare favorably with the background of the existing Symbian games. After studying the content from the Ovi Store, I decided to make a game with vector graphics, anti-aliasing and sub-pixel positioning of objects in the game. And as soon as the decision was made, I took up the implementation. The first step was to create an engine.

    Acquaintance with the Symbian SDK at first shocked me. Firstly, Symbian C ++ seemed to me extremely strange and unusual. Secondly, smartphones on Symbian ^ 1 (5800, N97, etc.) were built on a rather weak hardware platform without a graphics accelerator on board. There were OpenGL libraries in the SDK, but they were implemented programmatically. Thirdly, despite the support of the smartphone’s CPU for floating point operations, the SDK compiler was not able to generate code that performs operations with floating point numbers in hardware.

    The first problem was resolved fairly quickly. Fortunately, you can write code not only in Symbian C ++, but also in standard C ++. And since my game with the Symbian API practically did not work, only the initialization, interaction with sensors and timer was written on Symbian C ++. Everything else was successfully written in classic C ++ using STL.

    Having sorted out a bit about the charms of Symbian C ++, I began to deal with the issues of displaying graphics on the screen. I had no experience with OpenGL, besides, on Symbian ^ 1 OpenGL would not give a performance gain (since there is no accelerator). But in Symbian a very nice feature was discovered - Direct Screen Access (DSA). Using DSA, I was able to directly access the video memory of the device.

    Well, access to the video buffer has been obtained, now you can programmatically render everything that your heart desires into it at the highest possible speed. Quite quickly, sprite buffer methods were written, and the Anti Grain Geometry (AGG) library was connected for smoothed vector graphics objects . Rather, its very lite version: AGG Lite. Using AGG, methods were quickly written for displaying simple graphic primitives (line, circle, etc.). Everything worked smartly enough, and the picture was pleasing to the eye.

    Container classes were used to store information about complex vector objects consisting of many colorful shapes. In them I also implemented methods for displaying these objects on the screen in certain coordinates, at a certain angle and with a certain scale. Then Box2D was connected to the project , and the picture on the screen began to obey the laws of physics.

    And then the problems began. The picture on the screen looked great, all the lines were smooth, and due to the subpixel accuracy of the output, all the movements of the objects seemed very smooth ... While there were few moving objects on the screen. If 10-12 game vector objects were added to the screen, then fps fell catastrophically. Something had to be done to save the situation.

    First, all floating-point arithmetic was rewritten to fixed-point arithmetic. The Fixed class from Box2D helped with this, then pre-calculated tables were added for calculating trigonometric functions, optimized methods for calculating the square root and random numbers. There are no uses for floating point numbers left in the project. This gave a good performance boost, but still not enough. We managed to find several places in AGG that could be optimized. For example, disabling color mixing and enabling simple copying to display opaque portions of objects. This added a couple more fps, but still failed to raise fps above 15. It was possible to play at 15 fps, but ... I had to think about another gameplay in which there would be less dynamics and more static objects.

    In parallel with thinking about the new gameplay, I continued to work on the engine: I added support for bitmap fonts from the AngelCode converter , homemade localization support and a window system, as well as a small set of widgets.

    What happened, or Magic Brush


    And here this article on Habré caught my eye: Dmitry Ryzhkov and his My Little Artist won a solid prize in the competition for the best application for Intel Atom . Having looked at My Little Artist at work and looked at harmony by mr.doob , I realized that I could very quickly implement something similar on an existing engine. In just 15 minutes, the first brush was added and tested, and it worked great! I analyzed and rewrote the harmony brush code in C ++ with some optimizations. Nine new brushes were also added, the UI of the application was written, and each brush had additional settings (color, line thickness, transparency, etc.).

    The work of various brushes can be seen in this video:


    Well, as an example, I will tell you how the Smoke brush was implemented:



    In my program, all brush classes, including SmokeBrush, implement 3 purely virtual methods of the base class that display on the screen:

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


    They are called up when you touch the screen, move your finger and its separation from the surface of the screen, respectively. In addition, the base class contains attributes that are responsible for color, transparency and line thickness:

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


    and a pointer to the current buffer for output:

    BufferGraphics* graphics;


    To implement the Smoke brush, you will need to compose a thicker line from thin lines - "jets" of smoke - to compose a thicker line. To do this, you need to know the current position of each "jet" relative to the current position of the user's finger. For this purpose, SmokeBrush uses the points vector points:

    std::vector points;


    It is also necessary to remember the previous position of the finger in order to draw lines from it to the current position:

    Point previousFingerPosition;


    The logic of the brush is described in the StrokeStart and Stroke methods:

    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);
    }


    When touching the screen, it is necessary to clear the points vector from the values ​​that remained in it after the previous use of the brush. Then a new cloud of points is written to this vector. Moreover, the greater the thickness of the brush, the more "jets" will be needed to display the brush on the screen. Each individual "jet" is located at a random distance from the touch point of the screen, but not more than half the thickness of the brush. It is also necessary to remember the current point of contact for later use.

    Earlier, I wrote that in the project there was not a single use of floating-point numbers, but float can be seen in the text. Fortunately, the compiler is smart enough to properly handle the inline constructor of the Fixed class. As a result, at the execution stage, work really only happens with integer values.

    The Stroke method displays the following:

    void SmokeBrush::Stroke(Fixed x, Fixed y)
    {
        for (vector::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);
    }


    The loop traverses all points in the cloud. For each point, a small random offset is calculated relative to its current position. If the point has shifted too far from the touch point of the screen (more than half the thickness of the brush), then it remains in its old place. Then a separate “stream” is drawn on the screen, and the thickness of a separate “stream” depends on the thickness of the brush as a whole. At the end of the loop, the offset point is written back to points. This allows you to get wavy thin lines in the composition of a thicker brush.

    In such a simple way, you can make a brush, which in skillful hands can become a useful tool for drawing:



    Product launch


    So, about a month after reading the article on Habré, Magic Brush was completely ready. Even in the process of working on the program, I thought about how I would sell it, and realized that marketing takes a tremendous amount of time. In parallel with the development, I studied various materials on promoting applications in Store'ahs and once in the process of googling I came across an article by Dmitry Tarasov “Mobile software as a business” . This was the beginning of our cooperation and a new, no less interesting, stage in the development of Magic Brush - marketing magic. But more about that in the next part of our story.

    And finally - a link to the drawings made in MagicBrush: http://vk.com/album-19391365_127709331 .

    Also popular now: