The first development and publication of the game in social networks

Game idea

I had a long-standing desire to develop a full-fledged game for social networks, but there were no interesting ideas. Once I saw in the appstore a word game that was only in English. I wanted to do the same with the Russian dictionary.

The meaning of the game is to make words from touching hexagons. The game has 3 modes:
  • game against time (when searching for words, 1 second is added for each letter);
  • find a way out in the allotted time (find words and thereby pave the way to exit the center);
  • free mode (word search and completion at any time).

Appearance







Monetization

In the original game, monetization was that the user has access to 1 type of game out of three. The rest are unlocked after 50 and 100 games played.

I decided to do a similar monetization. But later added more functional word suggestions. Tips can be bought.

Additional advertising:
  • VK - dating service and preloader
  • OK - offer traffic management system


Time spent

Development was carried out from scratch (including the study of the framework) in his spare time from the main work, in the first month is very active.

Schedule of net development costs (does not include time for documentation and forums):

image

Theory

First of all, before developing the game, I had to study the features of mathematics with hexagons. Great article that covers everything I needed to know www.redblobgames.com/grids/hexagons

Choosing a development environment

The development was divided into 2 parts - into 2 autonomous projects:
  1. JS code of the game itself;
  2. PHP code for saving / issuing player statistics and additional files for interacting with social networks.

Initially, the idea was to use a cross-platform framework to develop a game for the browser and then port it to ios and android. Therefore, the choice fell on the cocos2d framework version html5.

The main drawback of this framework is poor documentation, some things had to be found in the source. But bribed by the use of this framework, some of its popularity, as well as the participation of zynga in its development. In general, I was not disappointed in him.

A feature of this framework is that everything on the screen is divided into layers and sprite objects. Which are by default rendered at 60 frames / sec. You can also force the automatic or automatic selection of the webgl mode, which uses hardware acceleration for graphics.

Graphics

I ordered all the graphics on a freelance website after the prototype was ready. Separately, I had to order an icon, which became more winning.

Some technical points and development features


Problem 1 - sprite caching

If you look at the main screen of the game, you can see many objects - these are hexagons and letters. In two games, their total number is about 5 thousand. This caused the first problem - the framework redraws them all 60 times per second and terribly slows down.

To solve this problem, one solution was found:
  1. Using letters as pictures;
  2. Combining all pictures in one file. Using the Texture Packer program, all the pictures were packed into 2 png files. One file was used for images in the main menu, the second - in the game itself. It saves one general picture with sprites and additionally applies an xml file with a description of the boundaries of each sprite. This is all imported by the framework and stored in memory;
  3. Using a special SpriteBatchNode container. According to the documentation, all sprites included in the SpriteBatchNode will be drawn in a single call to WebGL, and all sprites not included in this component will be drawn individually. Be sure all files added to the container must be in the same file.

    Sample code that uses a SpriteBatchNode container
    var size = cc.director.getWinSize();
    var x0 = size.width / 2 - (3 / 4 * this.game.tileWidth * this.game.size) / 2;
    var y0 = size.height / 2 + this.game.tileHeight * this.game.size / 2;
    var hex = cc.textureCache.addImage(res.Game_png);
    this._hexBatch = new cc.SpriteBatchNode(hex, 50);
    this._tableLayer.addChild(this._hexBatch);
    for (var i = 0; i < this.game.size; i++) {
        for (var j = 0; j < this.game.size; j++) {
            var layer = this.game.table[i][j];
            if (!layer || layer == '*') {
                continue;
            }
            var height = Math.sqrt(3) / 2 * layer.width - 2;
            var width = layer.width - 2;
            var x = x0 + i * 3 / 4 * width;
            var y = y0 - j * height / 2 - (j + 1) * height / 2 - (i % 2) * height / 2;
            var border = new cc.Sprite('#' + layer.hexagon_border);
            border.x = x;
            border.y = y;
            this._hexBatch.addChild(border);
            layer.attr({
                x: x,
                y: y
            });
            this._hexBatch.addChild(layer);
        }
    }
    this.addChild(this._tableLayer);
    this.wordPreviewLayer = WordPreviewLayer.create();
    this.addChild(this.wordPreviewLayer);
    


    The only problem is that it only makes sense when the user has active WebGL, without hardware acceleration, it will not work in the same way.


Problem 2 - Events

The whole project is divided into separate components, each of which lies in a separate class and file. They do not contact each other directly. The interaction is implemented using global events. Cocos2d contains the EventManager class for working with events.

A number of own events were created.

Event Initialization Example
var Timer = cc.LabelBMFont.extend({
    started: false,
    paused: false,
    lastTime: null,
    timerLength: 0,
    alert: 20,
    listeners: [],
    init: function () {
        this.listeners.push(cc.eventManager.addCustomListener(EVENT_TIMER_START, function (data) {
            this.start(data.getUserData());
        }.bind(this)));
        this.listeners.push(cc.eventManager.addCustomListener(EVENT_TIMER_STOP, function () {
            this.stop();
        }.bind(this)));
        this.listeners.push(cc.eventManager.addCustomListener(EVENT_TIMER_PAUSE, function () {
            this.pause();
        }.bind(this)));
        this.listeners.push(cc.eventManager.addCustomListener(EVENT_TIMER_CONTINUE, function () {
            this.resume();
        }.bind(this)));
        this.listeners.push(cc.eventManager.addCustomListener(EVENT_TIMER_ADD, function (data) {
            var seconds = data.getUserData();
            this.add(seconds);
            cc.eventManager.dispatchCustomEvent(EVENT_TIMER, this.timerLength);
        }.bind(this)));
        this.listeners.push(cc.eventManager.addCustomListener(EVENT_TIMER_END, function (data) {
            this.timerLength = 0;
            this.stop();
        }.bind(this)));
        return true;
    },
    cleanup: function() {
        this._super();
        for (var i in this.listeners) {
            cc.eventManager.removeListener(this.listeners[i]);
        }
    },


An example of an event call:

cc.eventManager.dispatchCustomEvent(EVENT_TIMER_START, this.timer_start);

When working with custom events, there are also some features: after the object is destroyed, eventManager will still consider it to be signed. To solve this problem, a local array of listeners is populated for each object, and when the object is destroyed, the cleanup destructor method is always called, in which I delete the added custom methods.

Problem 3 - Mobile Version

The mobile version of the game did not work out.
Firstly, the game runs in a mobile environment in the SpiderMonkey emulator, in which for some reason caching did not work. Because of what, the game was terribly slow on the iPad.

Secondly, some of the constructions I wrote caused an error; the code needs to be modified in places for compatibility.

The idea of ​​porting for ios and android is currently postponed.

Achivki

Later in the game 50 achievements were added. Work with them was not so difficult. To implement achievements, create an array that contains parameters and the init method to initialize the handler, which will record the fulfillment of the achievement condition:

Example
var achievements = [
    {
        id: 'the_graduate',
        name: t('The Graduate'),
        description: t('You completed Tutorial'),
        game: 'Tutorial',
        icon: 'graduate.png',
        pts: 1,
        init: function () {
            var event = cc.eventManager.addCustomListener(EVENT_TUTORIAL_END, function () {
                sendAchieve(this, event);
            }.bind(this));
        }
    }, {
…
}];


And we add the initialization of all achievements that the user has not yet earned.

The code
cc.eventManager.addCustomListener(EVENT_GAME_LOADED, function () {
    for (var i in achievements) {
        if (!cc.UserData.achievements || cc.UserData.achievements.indexOf(achievements[i].id) == -1) {
            achievements[i].init();
        }
    }
    cc.eventManager.addCustomListener(EVENT_SEND_ACHIEVEMENT, function(data) {
        var achieveData = data.getUserData();
        if (!cc.UserData.achievements) {
            cc.UserData.achievements = [];
        }
        cc.UserData.achievements.push(achieveData.id);
    })
});


In this example, at the end of the training level, the user is presented with a pop-up message about the earned achievement, a request is sent to the server, and the achievement is added to the user information array.

Word search

When a game is downloaded from the server, a dictionary of words is loaded, by which a search is performed when each letter is selected. Initially, a simple array with words was used, which affected the overall performance, because the dictionary contains about 150 thousand words. Having searched for a solution to this problem, I came across the concept of Trie or Prefix tree. As a result, the original array was converted to a file in trie data format. This solution allowed to significantly reduce the brakes when highlighting each letter of the word.

Project assembly

Before transferring js sources to the second project, it is necessary to assemble and minify all files into one js file. To do this, cocos2d already has a ready-made build.xml file that has been expanded. And as a result, the assembly is performed by one command in the console - ant. The finished file is simply copied to the second project.

Collection of statistics and integration with social networks

As a backend, the choice fell on symfony 2 and the mongo database only because for me it is a quick and easy way to implement the backend part.

Here you can highlight only one feature - for each social network, a separate integration file was created and build.xml created 3 minified files for each social network.

Web server optimization

The whole project is hosted on one vps server from digitalocean with a standard price of $ 10: 1 core cpu, 1Gb ram, 30Gb ssd. Debian OS, nginx + php-fpm web server. Dong mongodb.

To optimize traffic, gzip optimization has been enabled. But this increased the load on the processor at a time when there was a lot of traffic in classmates. As a result, the following solution was implemented:

gzip_static  on; 

For each js file, the build.xml script created a compressed version with the .gz extension. As a result, nginx simply took these files and gave them to the client without any CPU load.

Statistics


The game was first launched in VK. The day after approval, the game was added to the new section.
There was a separate surge after 2 weeks, when I ordered a new icon, which looked more logical.

Schedule
image

With Facebook, the situation is not very clear. Nevertheless, it seems to me that it’s pointless to go there without advertising - there are a lot of mobile games in the catalog.

The statistics are as follows. In early January, a clear surge is visible, which sharply went to zero.
To transfer earned funds, you must provide documents. Here, too, I did not understand - you can register as a private individual, but they ask for another PSRN.

Schedule
image

To start the game in OK, you must have an IP or legal entity. But you can find people who already have their games in OK and publish through them, agreeing on an amount from 10% transferred to the account (OK takes a little more than 50%).

In OK, the statistics look more interesting:

Schedule
image

General Google statistics since launch:

Schedule
image

Google also has the ability to save events. For example, there are statistics on events in the game:

Schedule
image

CPU load statistics:

Schedule
image


Total

Expenses

  • Development took about 200 hours of free time;
  • Design - 5500 rub.


"Income"

VK:
  • Users - 31,000 people;
  • Payments for the entire period - 84 votes;
  • Advertising revenue - 1,500 rubles.

OK:
  • Users - 171,500 people;
  • Payments for January - 27660 OK;
  • Advertising income (for 2 weeks) - 2600 rubles.

FB:
  • Users - 12,900 people;
  • Payments - $ 4.70.


Summary


We can conclude that for some money you need a game with a mandatory donation. Also, all types of monetization and advertising must be connected immediately. Since I was not familiar with the popular advertising sites and the actual amount of traffic, I did not connect them later, when the first wave of traffic passed.

It is also necessary to carry out load testing, since it is not known in advance how the game will go.

Also popular now: