[Tutorial] HTML5 Game Development with Cocos2d-JS – Firefox OS

Welcome!

 

In this post we will create a simple game for Firefox OS, Web and mobile devices using Cocos2D-JS. I’ll show you the basic of sprites, sound and touch events. At the end of this post, you will be able to start with HTML5 game development.

HTML5 Game Development

 

I – About the game

 

We are going to create a simple game, called Slide and Survive. You can download it here for Android , download here for Firefox OS, or play in this page .In this game, you just need to move the green square, avoiding contact with the red ones. The objective is to stay as longer as possible without any contact.

II – Creating our project

If you don’t have Cocos2d-js downloaded, take a look in this documentation. In this post you will see how to download and configure Cocos, enable autocompletion, and cocos-console.

After it, create a new Cocos2d-html5 project, with the command:

cocos new SlideAndSurvive -l js

This command will create a HTML5 project, called SlideAndSurvive inside the folder SlideAndSurvive.

Go to my github page and download the game source, go to the res folder to get the assets.

Now, we are able to start the development.

 

III – Configuring the assets and the main file

 

Cocos help us to use all resource files easily. To create a map of all resource files, open the src/resource.js. You can delete the auto-generated. In this file, we will create two vars:

res: An object to store all file locations and virtual names;

g_resources: A list to store our resources.

So, first create a object called res with the path and name for each resource file. In this game, we are using four, a green square, a red square, a sound and a font. Create like this one:

var res = {
    Square_png : "res/bloco.png",
    Enemy_png : "res/inimigo.png",
    TitleFont: "res/fonts/Marker Felt.ttf",
    Music: "res/fundo.mp3"

};

Now create a list to store all resource files:

var g_resources = [
    res.Square_png,
    res.Enemy_png,
    res.TitleFont,
    res.Music
];

With this list, will be easier to find our game resources in the development.

 

Now open your main.js, and replace with the following code:

cc.game.onStart = function(){
    cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);
    cc.view.resizeWithBrowserSize(true);
    //load resources
    cc.LoaderScene.preload(g_resources, function () {
        cc.director.runScene(new GameScene());//run the GameScene
    }, this);
};
cc.game.run();

This code will create a view with the resolution 800×450, but don’t worry, Cocos is perfect, and independent of the size of your screen, it will be automatically resized, and you can code to any resolution use this values. For example, if this game run on a 320×240 screen, the Cocos will understand that this screen has 800×450, so if you set the player to the position 750×400, it will work perfectly!!

The second command allow the view to auto resize with the browser. And after load the resources, we will create a new scene, the GameScene. You will code all the GameScene in the following topic. So, let’s go!!

 

IV – Game Scene

 

This code is in the file res/app.js.

First, to make it easier, replace this file with the following code:

var Objs = { //global objects
    EnemiesDirection: [],
    Enemies: [],
    Square: null,
    Title: null,
    gameTime: null,
    gameTimeInfo: null,
    gameTimeTotal: null,
    gameBestInfo: null,
    gameBestValue: null,
    gameInfo1: null,
    gameInfo2: null,    
    soundInfo: null
}

var timePlayed = 0; //game time
var isAlive = false; //is the game is running

function moveSquare(destination){ //move the green square to destination
}
function gameStart(){//game start, hide texts
}
function gameOver(){//game over, check score
    }
}
var GameLayer = cc.Layer.extend({//main scene
    ctor:function () {
    },
    update: function(dt){//update callback, run every frame
    },
    checkCollision: function(){
    },
    onTouchBegan: function(touch, event){//touchbegan callback
    },
    onTouchMoved: function(touch, event){//touchmoved callback
    },
    addTexts: function(){//add the texts to the screen
    },
    SoundClicked: function(){
    },
    addSquares: function(){//add the squares to the scene
    },
    generateDirection: function(){//generate a random direction
    }
});

var GameScene = cc.Scene.extend({//create the scene and start the game
    onEnter:function () {
        this._super();
        var layer = new GameLayer();
        this.addChild(layer);
    }
});

This code has all functions declaration, and some public functions and public variables.

Now, let’s code the game! The first functions is ctor. It’s the constructor of our scene.

ctor code:

 // 1. super init first
        this._super();

        var eventListener = cc.EventListener.create({//event listener
            event: cc.EventListener.TOUCH_ONE_BY_ONE, //one click
            swallowTouches: true, //is onTouch return true, stop event propagation
            onTouchBegan: this.onTouchBegan, //callbacks
            onTouchMoved: this.onTouchMoved});

        this.addSquares();//add enemies square
        this.addTexts(); //add texts

        cc.eventManager.addListener(eventListener, Objs.Square);//start the event listener

        for(var i = 0; i < 4; i++)
            Objs.EnemiesDirection[i] = this.generateDirection();//generate a random movement direction

        this.scheduleUpdate();//runs update() every frame
        return true;
}

 

The function addSquares

 addSquares: function(){//add the squares to the scene
        Objs.Square = cc.Sprite.create(res.Square_png);
        Objs.Square.setPosition(cc.p(400,225));
        Objs.Square.setTag(1);

        this.addChild(Objs.Square);

        var En = Objs.Enemies;
        for(var i = 0; i < 4; i++) {
            En[i] = cc.Sprite.create(res.Enemy_png);
            this.addChild(En[i]);
        }
        En[0].setPosition(cc.p(100, 100));

        En[1].setPosition(cc.p(700, 100));
        En[1].setScaleX(1.7);
        En[1].setScaleY(0.4);

        En[2].setPosition(cc.p(700, 350));
        En[2].setScaleX(0.4);
        En[2].setScaleY(1.5);

        En[3].setPosition(cc.p(100, 350));
        En[3].setScale(0.8);

        Objs.Enemies = En;
}

 

The function addTexts():

addTexts: function(){//add the texts to the screen
        var bestTime = localStorage.getItem("bestTime");//load the best time from localStorage
        Objs.Title = cc.LabelTTF.create("Slide & Survive", res.TitleFont, 40);
        Objs.Title.setPosition(cc.p(400, 350));
        this.addChild(Objs.Title);

        Objs.gameTime = cc.LabelTTF.create("0.000", res.TitleFont, 20);
        Objs.gameTime.setPosition(cc.p(50, 10));
        this.addChild(Objs.gameTime);

        Objs.gameTimeInfo = cc.LabelTTF.create("Time: ", res.TitleFont, 26);
        Objs.gameTimeInfo.setPosition(cc.p(200, 225));
        this.addChild(Objs.gameTimeInfo);

        Objs.gameTimeTotal = cc.LabelTTF.create("0.000", res.TitleFont, 26);
        Objs.gameTimeTotal.setPosition(cc.p(300, 225));
        this.addChild(Objs.gameTimeTotal);

        Objs.gameBestInfo = cc.LabelTTF.create("Best time: ", res.TitleFont, 26);
        Objs.gameBestInfo.setPosition(cc.p(540, 225));
        this.addChild(Objs.gameBestInfo);

        //check if there is a bestTime, if not set the default as 0
        Objs.gameBestValue = cc.LabelTTF.create(bestTime ? parseFloat(bestTime).toFixed(3) : "0.000", res.TitleFont, 26);
        Objs.gameBestValue.setPosition(cc.p(650, 225));
        this.addChild(Objs.gameBestValue);

        Objs.gameInfo1 = cc.LabelTTF.create("Move the green square avoiding contact with the red ones!",
            res.TitleFont, 20);
        Objs.gameInfo1.setPosition(cc.p(400, 310));
        this.addChild(Objs.gameInfo1);

        Objs.gameInfo2 = cc.LabelTTF.create("Are you able to do it???",
            res.TitleFont, 20);
        Objs.gameInfo2.setPosition(cc.p(440, 290));
        this.addChild(Objs.gameInfo2);

        var useSound = localStorage.getItem("Sound");
        if(useSound == 1) {
            cc.audioEngine.playMusic(res.Music, true);
        } else {
            if(useSound != 0) {
                localStorage.setItem("Sound", 1);
                useSound = 1;
            }
        }
        Objs.soundInfo = cc.MenuItemFont.create(useSound == 1 ? "Disable sound" : "Enable Sound", this.SoundClicked, this);
        Objs.soundInfo.setFontSize(20);

        var Menu = cc.Menu.create(Objs.soundInfo);
        Menu.setPosition(680, 20);
        this.addChild(Menu);

    },

Now, the generateDirection() function will generate a director to each enemy block. Each block will have a random direction:


generateDirection: function(){//generate a random direction
        var i = Math.floor((Math.random() * 3));
        var v = 7;
        switch (i){
            case 0:
                return cc.p(v, v);
            case 1:
                return cc.p(-v, v);
            case 2:
                return cc.p(-v, -v);
            case 3:
                return cc.p(v, -v);
        }
        return cc.p(0, 0);
    }

The update() function will be called each frame, so we can put the game logic here:

  update: function(dt){//update callback, run every frame
        if(!isAlive)//if is not running, stop
            return;
        timePlayed += dt; //add dt to game time
        Objs.gameTime.setString(timePlayed.toFixed(3));//update game time label

        var size = cc.director.getWinSize();//get win size

        for(var i = 0; i < 4; i++){//move the enemies
            var pos = Objs.Enemies[i].getPosition();

            if((pos.x <= 0) || (pos.x >= size.width))//the enemy position will be relative with his direction rect
                Objs.EnemiesDirection[i] = cc.p(Objs.EnemiesDirection[i].x * -1, Objs.EnemiesDirection[i].y)
            if((pos.y <= 0) || (pos.y >= size.height))
                Objs.EnemiesDirection[i] = cc.p(Objs.EnemiesDirection[i].x, Objs.EnemiesDirection[i].y * -1)
            Objs.Enemies[i].setPosition(cc.pAdd(Objs.EnemiesDirection[i], pos));

        }
        this.checkCollision();//check collisionss

    },

The checkCollision() will test if the hero touched an enemy:

   checkCollision: function(){
        //create a rect to represent our green square
        var rectHero = cc.rect(Objs.Square.getPositionX() - Objs.Square.getContentSize().width/2*Objs.Square.getScaleX(),
                Objs.Square.getPositionY() - Objs.Square.getContentSize().height/2*Objs.Square.getScaleY(),
                Objs.Square.getContentSize().width*Objs.Square.getScaleX(),
                Objs.Square.getContentSize().height*Objs.Square.getScaleY());

        for(var i =0; i < 4; i++){
            //create a rect for each enemy
            var rectEnemy = cc.rect(Objs.Enemies[i].getPositionX() - Objs.Enemies[i].getContentSize().width/2*Objs.Enemies[i].getScaleX(),
                    Objs.Enemies[i].getPositionY() - Objs.Enemies[i].getContentSize().height/2*Objs.Enemies[i].getScaleY(),
                    Objs.Enemies[i].getContentSize().width*Objs.Enemies[i].getScaleX(),
                    Objs.Enemies[i].getContentSize().height*Objs.Enemies[i].getScaleY());
            if(cc.rectIntersectsRect(rectHero, rectEnemy)) {//check collision
                gameOver();//if ok, gameover
                return;
            }
        }

    },

The SoundClicked() clicked function will be the callback of the menu enable/disable sound:

SoundClicked: function(){
        var enabled = localStorage.getItem("Sound");
        if(enabled == 1){
            cc.audioEngine.stopMusic(true);
            localStorage.setItem("Sound", 0);
            Objs.soundInfo.setString("Enable sound");
        } else {
            cc.audioEngine.playMusic(res.Music, true);
            localStorage.setItem("Sound", 1);
            Objs.soundInfo.setString("Disable sound");
        }

    },

Now we have the onTouch events, the first one, onTouchBegan():

   onTouchBegan: function(touch, event){//touchbegan callback

        var target = event.getCurrentTarget();
        var PosInScreen = target.convertToNodeSpace(touch.getLocation());
        var Size = target.getContentSize();
        var rect = cc.rect(0, 0, Size.width, Size.height);

        if(cc.rectContainsPoint(rect, PosInScreen)){ //check if i'm clicking in the green square
            switch(target.getTag()){
                case 1:
                    if(!isAlive){//if the game is not running
                        gameStart();//start it
                        moveSquare(cc.p(400, 225));//make sure to start the game at the center of the screen
                    }
                    return true;
            }
        }
        return false;
    },

 

And now onTouchMoved():

    onTouchMoved: function(touch, event){//touchmoved callback
        var target = event.getCurrentTarget();
        var PosInScreen = target.convertToNodeSpace(touch.getLocation());
        var Size = target.getContentSize();
        var rect = cc.rect(0, 0, Size.width, Size.height);
        if(!isAlive)//if is not running, go away
            return;

        if(cc.rectContainsPoint(rect, PosInScreen)){//check if clicked in the green square
            switch(target.getTag()){
                case 1:
                    moveSquare(touch._point);//move the square
                    return true;
            }
        }
        return false;

    },

Before finish it, we have 3 global functions. The first of them, moveSquare():

function moveSquare(destination){ //move the green square to destination
    var size = cc.director.getWinSize();
    if((destination.x > 0 ) && (destination.x < size.width)) //check if square is inside the screen
        if((destination.y > 0) && (destination.y < size.height))
            Objs.Square.setPosition(destination); //if ok, move it
}

Now the gameStart():

function gameStart(){//game start, hide texts
    isAlive = true;
    timePlayed = 0;
    Objs.Title.setVisible(false);
    Objs.gameTimeInfo.setVisible(false);
    Objs.gameTimeTotal.setVisible(false);
    Objs.gameBestInfo.setVisible(false);
    Objs.gameBestValue.setVisible(false);
    Objs.gameInfo1.setVisible(false);
    Objs.gameInfo2.setVisible(false);
}

And to finish our game, and our tutorial, aahahah, the gameOver():

function gameOver(){//game over, check score
    isAlive = false;
    Objs.Title.setVisible(true);
    Objs.gameTimeInfo.setVisible(true);
    Objs.gameTimeTotal.setVisible(true);
    Objs.gameBestInfo.setVisible(true);
    Objs.gameBestValue.setVisible(true);
    Objs.gameInfo1.setVisible(true);
    Objs.gameInfo2.setVisible(true);

    Objs.gameTimeTotal.setString(timePlayed.toFixed(3));

    Objs.Square.setPosition(cc.p(400, 225));//move enemies to default location
    Objs.Enemies[0].setPosition(cc.p(100, 100));
    Objs.Enemies[1].setPosition(cc.p(700, 100));
    Objs.Enemies[2].setPosition(cc.p(700, 350));
    Objs.Enemies[3].setPosition(cc.p(100, 350));

    var bestTime = parseFloat(Objs.gameBestValue.getString()); //get best time
    if(timePlayed > bestTime){ //check the game time
        localStorage.setItem("bestTime", timePlayed); //if is a new best time, save it
        Objs.gameBestValue.setString(timePlayed.toFixed(3)); //and show it
    }
}

 

 

Ok, our game is complete!! If you need, get the full source here.

V – Let’s publish it!

This game will be compatible if Firefox OS. This post is too big, so I think that is better to divide the building process in another post. You can check it here.

 

So, that’s all. If you have any problem/suggestion/bla/bla/bla, just comment here.

Thx for reading!

 

Hey!! I’m available for freelances!! If you need anything, just contact me!

Contact me!!

About these ads

One thought on “[Tutorial] HTML5 Game Development with Cocos2d-JS – Firefox OS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s