Win an Ipad
  Developer   Demos & Samples   Puzzle

Puzzle

Screenshot
Puzzle is a simple game suitable for all ages. The goal of the game is to put the right pieces into correct shapes. Everyone has had a chance to play a puzzle game in real life but there are not that many puzzle games for mobiles. Nevertheless, this game is suitable as stand-alone game for children but it can be also used as a great marketing tool.

This article is also available on codeproject.com side. This is only a little changed version, focused mostly on how to create game not on benefits of Moscrif SDK. 

About the game

Our game is made for Tablets and other devices, which run iOS and Android, with screen resolution larger than 600x900px. I have tested it on Amazon Kindle Fire, Galaxy Tab, Nook Color, iPad 2 and iPhone 4.

In this case, we designed crocodile to be the puzzle objective so ever piece has to be put in correct shape in order for the picture of the crocodile to appear. This puzzle game can be implemented in many ways. For marketing purposes, you can use your company logo, product photo etc.

User interface & user experience

Recently, the mobile industry has been putting a lot of emphasis on user experience because programmers are finding out that only applications with user-friendly interface can succeed. It means that applications have to do what user’s intuition expects to do, while being responsive and smooth. To achieve this experience, parts can be moved smoothly according to finger movement and when a particular part is close to the position where it belongs, the part automatically snaps to it.

When making an app, our goal is  to make a simple user interface, which gives the user best experience possible.

Image 1: User interface

Development process

Using Moscrif SDK saves a lot of time, because we do not need to create three separately codes for every platform. We will create only one code and publish it for all target platforms.

There are three tasks that need to be addressed in order to create this game. The first one is part distribution algorithm which needs to take place right at the beginning of the game. During the game there are two other tasks that need to be accomplished to make this a good puzzle game: part dragging and snapping.

Parts distribution  

Restart function

When user taps onto the screen or every time when user hits the refresh button, all puzzle parts are placed randomly on the playing field. Position for every part is found separately. 

Code Example 1:  Restart function - calls _findPosition for every puzzle part

/**
Function sets objects to the init state.
*/
function restart()
{
    // go throw all objects in this._parts array
    for (var part in this._parts) {
        part.isOnPlace = false;
        // find new position for this part
        this._findPosition(part);
    }
    this._onPlace = 0;
    this._pulseButtons();
}

    Function _findPosition

Function _findPosition is even more interesting because it runs in cycle until the right position for that particular part is found. Every run of this cycle generates new random position and then checks if it doesn’t overlays another, already placed, part. However, in some cases, cycle as we described can run forever. To prevent this critical mistake, we added counter property and max number of cycle repetitions.

Image 2: flow chart - _findPosition

Example code 2: Function _findPosition: finds a suitable position for puzzle part

/**
Part is set on new position.
@param part Part
*/
function _findPosition(part)
{
    //Helper function
    function random(from, to) { return from + rand(to-from+1); }

    // number of while runnings.
    var counter = 0;
    while (counter < 100/*Max repeats*/ ) {
        counter ++;
        //Set random position on axis y.
        part.y = random(part.height/2,System.height-part.height/2);
        //If y coordinate is over crocodile
        if (part.y+part.height/2 < this._finalTop)
            part.x = random(part.width/2, System.width - part.width/2);
        else {
            //Choose one from two areas (left or right from crocodile).
            var area = rand(2);
            if (area == 0)
                part.x = random(part.width/2, this._finalLeft-part.width/2);
            else
                part.x = random(this._finalLeft + this._final.width,
                                        System.width-part.width/2);
        }
        //Check if parts overlay.
        var collision = false;
        for (var p in this._parts) {
            if (part.intersectsBounds(p)) {
                collision = true;
                break;
            }
        }
        //If they do not overlay, it jumps from while loop.
        if (!collision)
            break;
    }
}

Parts dragging

Smooth part dragging is important to provide enjoyable user experience. When user taps on a puzzle part, finger movement will change position of the part. 

Example code 3: Function _getPart - finds puzzle part under the tap position

/**
Return part by coordinates.
@param x Integer Position on x axis.
@param y Integer Position on y axis.
@return Part
*/
function _getPart(x, y)
{
    //Auxiliary variable remembers picture located on position x y.
    //init state is nothing
    var searchPart = #nothing;

    //search image located on position x y
    for (var part in this._parts) {
        if (part.intersectsPoint(x, y))
            searchPart = part;
    }

    //If image is found on that position.
    if (searchPart != #nothing) {
        //set relative position
        searchPart.relX = searchPart.x - x;
        searchPart.relY = searchPart.y - y;

        //Move up
        this._moveUp(searchPart);
    }

    return searchPart;
}

Example code 4: Function pointerDragged - change part position in pointer-dragged event

/**
Event fired at pointer-dragged.
@param x Integer Position on x axis
@param y Integer Position on y axis
*/
function pointerDragged(x, y)
{
    super.pointerDragged(x, y);
    if (this._catchImage != #nothing) {
        this._catchImage.x = x;
        this._catchImage.y = y;
        this._catchImage.push(this);
    }
}

Parts snapping 

Snapping is another feature that provides good user experience. While user is dragging a part, application calculates its distance from the position where it belongs. If the distance is less than System.width / 38 it automatically snaps to its place. 

Example code 5: Function push - calculates distance from the position where it belongs

/**
Check distance from location where part should be. If it is too close move it to the right place.
@param sender Object
*/
function push(sender)
{
    //Calculate the distance from the position
    //where the image should be placed
    // |draw, loc|
    var sizeX = Math.pow((this.locX + this.width/2) - this.x,2);
    var sizeY = Math.pow((this.locY + this.height/2) - this.y,2);
    var size  = Math.sqrt(sizeX + sizeY);

    //If the distance is smaller than constant
    if (size < System.width / 38) {
        //Set picture location on the right position.
        this.x = this.locX + this.width/2 - this.relX;
        this.y = this.locY + this.height/2 - this.relY;
        //If picture wasnt on the right position.
        if (!this.isOnPlace) {
            //Increment variable, which remember count of picture, which are on right position.
            sender._onPlace++;
            this.isOnPlace = true;
        }
    //Picture was on right position, but he is not anymore.
    } else if (this.isOnPlace) {
        sender._onPlace--;
        this.isOnPlace = false;
    }
}

Image 3: Snapping area

Summary 

Now you know how to create your own puzzle game and what features are required to make the game enjoyable while providing more user-friendly experience. Yes, yes we know the game is very basic but it was made for learning purposes and it should serve as base ground for your next awesome app. As we mentioned before, implementing such a game into your already existing business app will increase brand awareness, and customer engagement.

 AirHockey   Breakout 
Write a Comment (0)
Subject
Please complete this mandatory field.
HTML Tags Not Allowed!
Comment
Please complete this mandatory field.