Snake Game Using Cocos2d-x HTML5 - PART 3

Datetime:2016-08-23 04:30:17          Topic: HTML5  Cocos2d-X           Share

Lets see the finished source code first and then i will explain the logic of each section in the code

//global varibales with default values 
var SnakeArray = null; 
var SnakeFood = null; 
var spriteBackGround = null; 
var BackGroundWidth = 0;;
var BackGroundHeight = 0;
var BackGroundPos;
var Direction ="";
var score = 0;  
var cellWidth = 30;
var Enum = {snakecell:0, snakefood:1 , background:2};
var snakeJSGameLayer = null;
var snakeLength = 5; //Length of the snake
var ScoreLabel = null;
var GameOverLabel = null;
var bCollisionDetected = false;

//The Background sprite class holding the game nodes
var SpriteBackGround = cc.Sprite.extend({
 ctor: function(texture, rect) {           
  this._super(texture, rect);
 }
});
//Snake cell class 
var SpriteSnakeCell = cc.Sprite.extend({
 ctor: function(texture) {           
  this._super(texture);
 }
});
//Snake food class 
var SpriteSnakeFood = cc.Sprite.extend({
 ctor: function(texture) {           
  this._super(texture);
 }
});
//Game Main container class, holding all game logic functions
var SnakeJSGameLayer = cc.Layer.extend({
    sprite:null,
    ctor:function () {
      
       this._super();
       var winSize = cc.winSize;
       //Create the background sprite 
       spriteBackGround = new SpriteBackGround(res.blank_png,
                                cc.rect(0,0,winSize.width-50,winSize.height-90));
       spriteBackGround.setAnchorPoint(0,0);
       spriteBackGround.setTag(Enum.background);
       BackGroundWidth = spriteBackGround.getBoundingBox().width;
       BackGroundHeight = spriteBackGround.getBoundingBox().height;
       //Calculate the background sprite positon by subtracting SpriteBackGround position from Main layer position 
       spriteBackGround.x = (winSize.width - BackGroundWidth)/2;
       spriteBackGround.y = (winSize.height - BackGroundHeight)/2;       
       this.addChild(spriteBackGround,1);
       BackGroundPos = {x:spriteBackGround.x, y:spriteBackGround.y};      
       //Add the score lable
       ScoreLabel = new cc.LabelTTF(setLabelString(score), "Arial", 38); 
       ScoreLabel.x = winSize.width / 2;
       ScoreLabel.y = winSize.height / 2 + 200;       
       this.addChild(ScoreLabel, 5); 
       //Add the game over lable as none visible 
       var redColor = cc.color(0, 0, 0);
       GameOverLabel = new cc.LabelTTF("Game Over press SPACE to restart!", "Arial", 38); 
       GameOverLabel.x = winSize.width / 2;
       GameOverLabel.y = winSize.height / 2;  
       GameOverLabel.fillStyle = redColor
       this.addChild(GameOverLabel, 5); 
       GameOverLabel.visible = false;
       //create the snake and the food 
       this.CreateSnake();
       this.CreateFood();
       
       if ('keyboard' in cc.sys.capabilities) {
            cc.eventManager.addListener({
                event: cc.EventListener.KEYBOARD,
                onKeyPressed: function (key, event) {
                   var target = event.getCurrentTarget();
                   //Only if space key is pressed and Collision Detected the game restart
                   if(bCollisionDetected && key == "32")
                   {
                        bCollisionDetected = false;
                        GameOverLabel.visible = false;
                        cc.director.resume();
                        target.CreateSnake();
                        target.CreateFood();
                        return;      
                   }
                   if(key == "37" && direction != "right") direction = "left";
                   else if(key == "38" && direction != "down") direction = "up";
                   else if(key == "39" && direction != "left") direction = "right";
                   else if(key == "40" && direction != "up") direction = "down";
                 } 
            }, this);
        } else {
            cc.log("KEYBOARD Not supported");
        }        
         
        //This is the main game loop
        this.schedule(this.GameLoop,0.2);
        
        return true;
    },
    CreateSnake:function() {
        score = 0;
        ScoreLabel.setString(setLabelString(score));
        direction = "right";
        if(( typeof SnakeArray != 'undefined' && SnakeArray instanceof Array ) && SnakeArray.length > 0 )
        {                
            for(var i = 0; i< SnakeArray.length; i++)             
            {
                    this.removeChild(SnakeArray[i],true);                   
            }
        } 
        SnakeArray = [];   
        var elmsToRemove = SnakeArray.length - length;
        if(elmsToRemove>1)
        {
            SnakeArray.splice(snakeLength-1,elmsToRemove);
        }
        for(var i = snakeLength-1; i>=0; i--)
        {
           var spriteSnakeCell = new SpriteSnakeCell(res.snakecell_png);
           spriteSnakeCell.setAnchorPoint(0,0);
           spriteSnakeCell.setTag(Enum.snakecell);          
           var xMov = (i*cellWidth)+BackGroundPos.x;
           var yMov = (spriteBackGround.y+BackGroundHeight)-cellWidth;
           spriteSnakeCell.x = xMov;
           spriteSnakeCell.y = yMov;
           this.addChild(spriteSnakeCell,2);           
           SnakeArray.push(spriteSnakeCell); 
        }
    },            
    CreateFood:function() {   
        //Check if food Exist , remove it from the game sprite
        if(this.getChildByTag(Enum.snakefood)!=null)
        {
            this.removeChildByTag(Enum.snakefood,true); 
        }       
        var spriteSnakeFood = new SpriteSnakeFood(res.snakefood_png);
        spriteSnakeFood.setAnchorPoint(0,0);
        spriteSnakeFood.setTag(Enum.snakefood);     
        this.addChild(spriteSnakeFood,2); 
        var rndValX = 0;
        var rndValY = 0;
        var min = 0; 
        var maxWidth = BackGroundWidth;
        var maxHeight = BackGroundHeight;
        var multiple = cellWidth;
        //Place it in some random position 
        rndValX = generate(min,maxWidth,multiple);
        rndValY = generate(min,maxHeight,multiple);
        var irndX = rndValX+BackGroundPos.x;
        var irndY = rndValY+BackGroundPos.y;
        SnakeFood = {
            x: irndX , 
            y: irndY  
        };
     
       spriteSnakeFood.x = SnakeFood.x; 
       spriteSnakeFood.y = SnakeFood.y;  
    },
    //The function will be called every 0.2 milii secound
    GameLoop:function (dt) {
        //get the snake head cell 
        var SnakeHeadX = SnakeArray[0].x;
        var SnakeHeadY = SnakeArray[0].y;
        //check which direction it is heading  
        switch(direction)
        {
            case "right":
                 SnakeHeadX+=cellWidth;
            break;
            case "left":
                 SnakeHeadX-=cellWidth;
            break;
            case "up":
                 SnakeHeadY+=cellWidth;
            break;
            case "down":
                  SnakeHeadY-=cellWidth;
            break;
            default:
                cc.log("direction not defind");
        }
        //Check if the snake head Collided with the walls or with it self 
        if(CollisionDetector(SnakeHeadX, SnakeHeadY, SnakeArray))
        {                 
                bCollisionDetected = true; 
                GameOverLabel.visible = true;
                cc.director.pause();
                return;
        }
        //Check if the snake head Collided with the food
        if(SnakeHeadX == SnakeFood.x && SnakeHeadY == SnakeFood.y)
        {
                //Add snake cell after the head position
                var spriteSnaketail = new SpriteSnakeCell(res.snakecell_png);
                spriteSnaketail.setAnchorPoint(0,0);
                spriteSnaketail.setTag(Enum.snakecell);
                this.addChild(spriteSnaketail,2);
                spriteSnaketail.x = SnakeHeadX;
                spriteSnaketail.y = SnakeHeadY;
                SnakeArray.unshift(spriteSnaketail);  
                //Add point to the points display
                ScoreLabel.setString(setLabelString(score++));
                //Create new food in new position
                this.CreateFood();         
        }
        else
        {       
                var spriteSnakeCellLast = SnakeArray.pop(); //pops out the last cell
                spriteSnakeCellLast.x = SnakeHeadX; 
                spriteSnakeCellLast.y = SnakeHeadY;
                SnakeArray.unshift(spriteSnakeCellLast);
        }
 
    }    
});
//The snake Collision Obstacles are side walls  and itself
function CollisionDetector(snakeHeadX,snakeHeadY,snakeArray)
{
        if(snakeHeadX < spriteBackGround.x || 
                snakeHeadX > BackGroundWidth || 
                snakeHeadY < spriteBackGround.y || 
                snakeHeadY > ((spriteBackGround.y+BackGroundHeight)-cellWidth))
        {
            return true;
        }
        for(var i = 0; i < snakeArray.length; i++)
        {
                if(snakeArray[i].x == snakeHeadX && snakeArray[i].y == snakeHeadY)
                {
                 return true;
                }
        }
        return false;
}
//Rendom number generator 
function generate(min, max, multiple) 
{
    var res = Math.floor(Math.random() * ((max - min) / multiple)) * multiple + min;
    return res;
}
//Convert number to string
function setLabelString(str)
{
    var stringScore = parseInt(score).toString();
    return stringScore;   
}
//Main Game container 
var SnakeJSScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        snakeJSGameLayer = new SnakeJSGameLayer();
        this.addChild(snakeJSGameLayer);
    }
});
  1. Lines 1 - 16 global variables , the scope of those will be accessible to all functions and objects.
  2. Lines 19 - 35 the game class's ,
    SpriteBackGround : contains all the games players snake and food.
    SpriteSnakeCell : the snake is build from these objetcs.
    SpriteSnakeFood : the red square which the snake need to eat.
  3. Lines 37 - 217 is the main layer that hold all the games element , all of them .
  4. Lines 39 the Layer constructor , it will initialize only once when the game start
    so all the initials setup of the game for example creating the snake (line 69) and the food (line 70)  and the main game sprite (line 44) will happen here.
  5. Lines 72 - 95 capture the keyboard events from the user.
    lines 78 - 86 when collision detected and the space key is pressed , restart the game .
    lines  87 - 90 when keyboard left / right /top /down keys pressed detected.
  6. Line 98 this is one of the most important functions in Cocos2d-x or any game in general.
    AKA : The Game Main Loop scheduler which fire the " Main Game loop function "
    in our case it is : GameLoop function (line 162).
  7. Lines 102 - 131  CreateSnake function creates the snake from  SpriteSnakeCell objects .
    lines 106 -102 checks to see if there is already snake array defined and visible in the game before removing it , this function creates new snake ,only 1 snake allowed in the game.
    lines 119 - 130 the snake creation loop.
  8. Lines 132 -156 CreateFood function , create 1 food square each time called on random location on the game layer . only 1 food object allowed
  9. Lines 162 - 216 GameLoop function , in my view the most important function in the game logic it is function that invoked each N second in our case every 0.2 millisecond from the schedule function Line 98 . this is not always accurate it depends on the game structure and CPU.

    lines 167 - 183 detect which direction is pressed by the user so the program could direct it in the right direction 

  10. Lines 185 - 191 call the  CollisionDetector if 

    collision detected do as follow :

    1 . set the global variable bCollisionDetected to true.

    2.  set GameOverLabel to be visible so there is indication the game is over.

    3.  pause all cocos2d-x engine work as result the game stop,

  11. Lines 193 - 217 if the snake "head" cell that is the element in place 0 in the SnakeArray array

    remember we are working only with the snake head see lines 164 - 165.

    if the location of the head going to as the food location the program need to remove the food object and increase the snake size lines 196 - 206 .

    if not just continue the snake move lines 210 - 213.

  12. lines 219 -236 CollisionDetector function checks if the snake head collide the game spriteBackGround boundaries .

    checks also if the snake head doesn't collide it self lines 228 - 234 .

  13. Lines 238 -242 generate function simple random number generator. used to place the food object.
  14.  Lines 244 - 248 setLabelString function convert int value type to string type 
  15. Lines 250 - 255 create the game Scene , this is the root node of all our game stage nodes.

We are ready to publish our "SnakeJS" game in the next tutorial :

Snake Game Using Cocos2d-x HTML5  - PART 4

Play the final SnakeJS game:

http://meiry.github.io/SnakeJS-html5/publish/html5/

The SnakeJS source code:

https://github.com/meiry/SnakeJS-html5





About List