Saturday, October 31, 2015

How Disable Cocos2d-x Texture anti-aliasing especially for pixel art game

When working with games which are using pixel art , usually the game needs to keep the look of the pixel images sharp / none blurred .. now when we working with pixel art the images are small and they are scaled to be bigger , and while they become bigger OpenGL Smears the look on the image .
it can be fixed with small OpenGL command .

1. Here is example of small pixel art animation i made BEFORE the fix see how it looks:
i have 2 framed animation converted to *.plist and then to sprite frames in the code .

The 2 frame animation png file :
The Compiled Scene:



Not much to say it looks like Sh**t. see how blurry it is .
To fix it

2. The source code which contains the Fix  Lines: 5-6:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
 auto cache = SpriteFrameCache::getInstance();
    cache->addSpriteFramesWithFile("sprites.plist", "sprites.png");
    auto sprite = Sprite::createWithSpriteFrameName("sprite_1.png");
    //Disable Texture anti-aliasing 
    Texture2D::TexParams texParams = { GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
    sprite->getTexture()->setTexParameters(texParams);
    sprite->setScale(6.0f);
    auto spriteBatch = SpriteBatchNode::create("sprites.png");
    spriteBatch->addChild(sprite);
    this->addChild(spriteBatch);    
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    Vector<SpriteFrame*> animFrames(2);
    char str[100] = { 0 };
    for (int i = 1; i <= 2; i++)
    {
        sprintf(str, "sprite_%d.png", i);
        auto frame = cache->getSpriteFrameByName(str);
        animFrames.pushBack(frame);
    }
    auto animation = Animation::createWithSpriteFrames(animFrames, 0.3f);
    // 14 frames * 1sec = 14 seconds
    sprite->runAction(RepeatForever::create(Animate::create(animation)));


As we can see the function setTexParameters let us set custom parameters to our Sprite Texture which contains the animation frames  and the way it should render 
This is how it looks like after the FIX :


Much much better .

Thursday, October 15, 2015

How to compile Cocos2d-x with Box2d support Instead of default Chipmunk

Cocos2d-x engine in some stage i think somewhere in version 2 + Changed its default physique engine from Box2d to Chipmunk .
As new developer that just checking this game engine you might think that Cocos2d-x doesn't support Box2d.
Well it does but the configuration is buried deep in the compiler configuration . why .. i have no idea.
But once you understand where to change you will have the support to Box2d right away .
AFTER compiling the project again .

  1. The example will be with Visual Studio 2013 , but its the same in Xcode And Eclipse
    That is in the Preprocessor Definitions .
  2. Once you load your new project in Visual Studio right click on you project name
    then properties.
  3. In the Popup window you see that is opened go to :
    On the left :
    Configuration Properties -> C/C++ -> Preprocessor  (A)
    Then on the right :
    Preprocessor Definition -> click on the small arrow in the drop list and select "<Edit..>" (B)
  4. Once you click on "<Edit..>"  small popup window will be open and called:
     "Preprocessor Definition"
    On the upper Multiline Edit box
    You need to set :
    CC_ENABLE_CHIPMUNK_INTEGRATION=0
    and now you need to add new Preprocessor Definition:
    CC_ENABLE_BOX2D_INTEGRATION=1
    it needs to be like in this image :

  5. Now yo must compile the project again , so the new definition you just set will take effect.




Monday, October 12, 2015

How to prevent Cocos2d-x console sending user data while creating project

Hey!
Im working on  "Beginner to Game Dev Master course " should be ready soon , please register to my email list to be updated when it is ready

Thanks !


First of all i want to state that while the Headline sound somehow suspicious.
I have nothing Against the cocos2d-x team , they are doing it to understand their user base.
And allot of frameworks/apps doing it. just open Wireshark and see ...

  1. In your Cocos2d-x framework project root directory go to :
    ..\cocos2d-x-3.8.1\tools\cocos2d-console\bin
  2. Open the file : cocos2d.ini
  3. Search for the string : enable_state=true
    1
    2
    3
    4
     Enable/Disable the data statistics
    # If the value is 'false' or 'no', statistics is disabled.
    # Otherwise, it's enabled.
    enable_stat=true 
  4.  Change it to : enable_state=false
     
    So now it should look like this:
    1
    2
    3
    4
     Enable/Disable the data statistics
    # If the value is 'false' or 'no', statistics is disabled.
    # Otherwise, it's enabled.
    enable_stat=false
    



Monday, October 5, 2015

Snake Game Using Cocos2d-x Javascript iOS- PART 7

First small teaser , this is the complete game played by my kids



As you may notice in the previous tutorial we had no touch events to control the snake , as you Remember in the SnakeJS Snake Game Using Cocos2d-x HTML5 - PART 3   
In the code Lines 72 - 95 there is only keyboard handling . lts change that .
The controllers i chose for this tutorial will be buttons to mimic the keyboard's arrow keys.
I aware there are a lot of debate on the topic. which is better swipe gestures or virtual keyboards.
But for the simplicity i will do it with buttons .
For the buttons i used the open source Vector Editor called 
Inkscape .
Couple of squares and i have 4 buttons to use , very simple very basic .






Or this color .. with round angles , Inkscape is really great vector editor .
For quick Inkscape tutorials  I strongly recommend you to visit this site: http://www.2dgameartguru.com/ 



So after we done with the controllers lts dive into our new game code .
The Code :




  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
//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,
   touchup:3,
   touchdown:4,
   touchright:5,
   touchleft:6
   };
var snakeJSGameLayer = null;
var snakeLength = 5; //Length of the snake
var ScoreLabel = null;
var GameOverLabel = null;
var bCollisionDetected = false;
var spriteTouchUP = null;
var spriteTouchDown = null;
var spriteTouchLeft = null;
var spriteTouchRight = null;
var origin = null;
var winSize = null;
var SnakeJSGameLayer = null;

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

var SpriteTouch = cc.Sprite.extend({
 _listener:null,
    _fixedPriority:0,
    _removeListenerOnTouchEnded: false,

    ctor: function(priority,texture){
        this._super(texture);
        this._fixedPriority = priority || 0;
    },

    setPriority:function(fixedPriority){
        this._fixedPriority = fixedPriority;
    },

    onEnter:function(){
        this._super();

        var selfPointer = this;
        var listener = cc.EventListener.create({
            event: cc.EventListener.TOUCH_ONE_BY_ONE,
            swallowTouches: true,
            onTouchBegan: function (touch, event) {
                var locationInNode = selfPointer.convertToNodeSpace(touch.getLocation());
                var s = selfPointer.getContentSize();
                var rect = cc.rect(0, 0, s.width, s.height);
    var target = event.getCurrentTarget();
                if (cc.rectContainsPoint(rect, locationInNode)) {
                    selfPointer.setColor(cc.color.RED);            
                    if(bCollisionDetected)
     {
       bCollisionDetected = false;
       GameOverLabel.visible = false;
       cc.director.resume();
       snakeJSGameLayer.CreateSnake();
       snakeJSGameLayer.CreateFood();
       return true;      
     }
                    switch(selfPointer.getTag())
                    {
                     case Enum.touchdown:
                     {
                        if(direction != "up") direction = "down";
                      break;
                     }
                     case Enum.touchup:
                     {
                      if(direction != "down") direction = "up";
                      break;
                     }
                     case Enum.touchright:
                     {
                         if(direction != "left") direction = "right";
                      break;
                     }
                     case Enum.touchleft:
                     {
                         if(direction != "right") direction = "left";
                      break;
                     }
                    }
                    return true;
                }
                return false;
            },
            onTouchMoved: function (touch, event) {
                //this.setPosition(this.getPosition() + touch.getDelta());
            },
            onTouchEnded: function (touch, event) {
                selfPointer.setColor(cc.color.WHITE);
                if(selfPointer._removeListenerOnTouchEnded) {
                    cc.eventManager.removeListener(selfPointer._listener);
                    selfPointer._listener = null;
                }
            }
        });

        if(this._fixedPriority != 0)
            cc.eventManager.addListener(listener, this._fixedPriority);
        else
            cc.eventManager.addListener(listener, this);
        this._listener = listener;
    },

    onExit: function(){
        this._listener && cc.eventManager.removeListener(this._listener);
        this._super();
    },

    removeListenerOnTouchEnded: function(toRemove){
        this._removeListenerOnTouchEnded = toRemove;
    },

    getListener: function() {
        return this._listener;
    }
});

 
//Game Main container class, holding all game logic functions
SnakeJSGameLayer = cc.Layer.extend({
    sprite:null,
    ctor:function () {
      
       this._super();
       origin = cc.director.getVisibleOrigin();
       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 = new cc.LabelTTF("", "Arial", 30); 
       GameOverLabel.x = winSize.width / 2;
       GameOverLabel.y = winSize.height / 2;  
       GameOverLabel.fillStyle = redColor
       this.addChild(GameOverLabel, 5); 
       GameOverLabel.visible = false;
       this.SetLableText(3);
       //if (cc.sys.os == cc.sys.OS_IOS || cc.sys.os == cc.sys.OS_ANDROID) 
       {
    
         this.CreateTouchControllers();
       }
       //create the snake and the food 
       this.CreateSnake();
       this.CreateFood();
       
       if (cc.sys.os === cc.sys.OS_WINDOWS) 
       {
     
     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 - snakeLength;
        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);
        }
 
    },
 SetLableText:function(mode) {
  if(mode == 1)
  {
   GameOverLabel.setString("Game Over press SPACE to restart!");
  }
  else if(mode == 2)
  {
   GameOverLabel.setString("Game Over press any arrows to restart!");
  }
  else if(mode == 3)
  {
   GameOverLabel.setString("Game Over press any arrows or SPACE to restart!");
  }
 },
    CreateTouchControllers:function() {
     //create controllers   
    spriteTouchDown = new SpriteTouch(30,res.arrow_down);      
       spriteTouchDown.setAnchorPoint(0,0);
       spriteTouchDown.x = 0;
       spriteTouchDown.y = 0;
       spriteTouchDown.setTag(Enum.touchdown); 
        
       spriteTouchUP = new SpriteTouch(30,res.arrow_up);      
       spriteTouchUP.setAnchorPoint(0,0);
       spriteTouchUP.x = 0;
       spriteTouchUP.y = spriteTouchDown.getBoundingBox().height +20;
       spriteTouchUP.setTag(Enum.touchup); 
       
       spriteTouchRight = new SpriteTouch(30,res.arrow_right);      
       spriteTouchRight.setAnchorPoint(0,0);
       spriteTouchRight.x = winSize.width - spriteTouchDown.getBoundingBox().width -20;
       spriteTouchRight.y = 0;
       spriteTouchRight.setTag(Enum.touchright); 
       
       spriteTouchLeft = new SpriteTouch(30,res.arrow_left);      
       spriteTouchLeft.setAnchorPoint(0,0);
       spriteTouchLeft.x = spriteTouchRight.x - spriteTouchLeft.getBoundingBox().width-20;
       spriteTouchLeft.y = 0;
       spriteTouchLeft.setTag(Enum.touchleft); 
              
       this.addChild(spriteTouchUP,10);
       this.addChild(spriteTouchDown,10);
       this.addChild(spriteTouchLeft,10);
       this.addChild(spriteTouchRight,10);
    }  
});
//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);
    }
});



Lets go and examine the code it changed quite a lot. i will cover the main parts that has changed .

  1. Lines 15 - 19 added new enumeration keys to be set to our new controllers tags .
    Lines 25 - 28 added new variables that will hold the new controllers sprites.
  2. Lines 52 - 147 this is the Controller class with be created 4 times to represent our
    game left/right/top/down controllers.
    Lets break this class to parts :
    Lines 57 - 60 The class constructor it must get the priority, and the texture parameters
    The priority must be high so the sprite will be click and not the sprite below it.
    And the texture is the controller image we made in Inkscape.
    Line 66 , onEnter:function()  
    This function will be invoked each time user has touched the Controller sprite .
  3. Lines 80 - 88 this is where the collision is detected while we are controlling our snake movement . if detected reset the game if one of the controllers are clicked. 
  4. Lines 89 - 112 the switch case here will be invoked each touch . and according to
    the tag of the object it setting the directions.
  5. the rest of the "SpriteTouch" class is standard methods that we are not going to use.
    but they are needed to be there .
  6. Line 171 ScoreLabel now changed and no longer has static text , now it is determinate its text
    according to  setLabelString(scroe) function from Line 324 . 
  7. Line 188 this function constructing the controllers sprites 
    I comment the //if (cc.sys.os == cc.sys.OS_IOS || cc.sys.os == cc.sys.OS_ANDROID)
    This line means that you can control the appearance of the CreateTouchControllers() function.

    In this example i decided to enable the controllers no matter what OS the game running on  
  8. Lines 342 - 355 this is the function that was mentioned in section no' 6 Basclly we have 3 modes to display the "End Game" captions feel free to add more
    mode == 1 is when we are on the web or desktop
    mode ==2 is when  we are on any OS
    mode ==3 is when we are on Mobile or Desktop/Web
  9. Lines 356 - 386 this is the function that position the controllers on the game screen
    Here you can also position the them in any way you see it right .

That's It now we have 1 code base for Web , Desktop , iOS .

You can Check the source code and the controllers resource files in this GitHub repository:
https://github.com/meiry/SnakeJS-Final




Sunday, October 4, 2015

Snake Game Using Cocos2d-x Javascript iOS - PART 6

Now we come to the interesting part of this  tutorial series, we will take our game and compile it as iOS game .
For this Tutorial we need some prerequisites before we start .
  1. Mac. 
  2. iOS Device for testing . i strongly recommend to test the games on real device .
  3. Xcode 7, in this version you can test on your device without paying the 99$ .

1. We will use the same project we created in: Snake Game Using Cocos2d-x HTML5 - PART 1
Fill free to create the project again in your Mac and copy only the source files and the res directory.
The source files and the Xcode project files located in ( i create my projects in Projects directory) :
../cocos2d-x-3.8.1/Projects/SnakeJS/frameworks/runtime-src/proj.ios_mac/SnakeJS.xcodeproj


2. Click on SnakeJS.xcodeproj and if you installed Xcode 7 ( Remember 7 ...) it should load the project and you suppose to see this in Xcode :


3. Connect your iOS device into your MAC , Xcode should recognize it .
Then in Xcode go and set the active scheme to: SnakeJS-Mobile -> Your-IPhone-name




4. Lets hit "build" to see that every thing is fine by now ,  but before that we must set in Xcode :
  1. Set the deployment target to 7 and above in the General -> Deployment section.
  2. Also the current Cocos2d-x 8.1 have some kind of bug with bitecode set to Yes
    so lts change it to: No
    Go to Targets : SnakeJS->mobile then on the right screen
    Build Options ->Enable Bitcode = No
If you skip the above section Xcode will show you this error :
XCODE 7 and ENABLE_BITCODE=YES setting does not work

Now when all is set
Go to Product -> Build.
Wait few minutes , let the compilation to end .

5. If all went right you will see the game launching on your device and immediately start to play .
If you try to move it you will notice that none of the Touch controls are working
This is simply because in our code we don't have any touch handling .
We will write the code to handle this in next chapter .

We are ready to continue building our "SnakeJS" game for iOS Device in the next tutorial :
Snake Game Using Cocos2d-x Javascript iOS- PART 7

Play the final SnakeJS game :
http://meiry.github.io/SnakeJS-html5/publish/html5/
The SnakeJS source code:
https://github.com/meiry/SnakeJS-html5



Saturday, October 3, 2015

Snake Game Using Cocos2d-x Javascript Desktop - PART 5

Now that the game coding is done we can use the same code base almost without change for our game desktop version.
To run the main.js file which is the game entry point we need to compile Cocos2d-x RunTime which will load our game.

for this we use:
  1. Visual studio 2013 c++  community version . 
  2. The same code base we created in Snake Game Using Cocos2d-x HTML5 - PART 1 .


1.  Open VC++   then go to FILE -> Open -> Project/Solution . and navigate to where you created the SnakeJS project .  then navigate to deeper directory named:  proj.win32  :
 \cocos2d-x-3.8.1\Projects\SnakeJS\frameworks\runtime-src\proj.win32
And load the "SnakeJS" VC++ solution project called SnakeJS.sln .|



2. Once we done loading the Cocose2d-x Runtime source code into VC++ we ready to compile it.
we don't need Debug version for this example because for now we are not going to change any c++ source code ,
In VC++ :
  1. in the projects list on the right select our "SnakeJS" project . 
  2. go to the upper tool box there is drop down list with "Debug" selected . change it to "Release"




3. In VC++ which SnakeJS project selected go to and click on : BUILD -> Build SnakeJS
Wait until it is done to compile.




When it is done to compile. you should see in the VC++ bottom Output window  scroll to the end .


7>  4 File(s) copied
7>          1 file(s) copied.
7>          1 file(s) copied.
========== Build: 7 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========









4. Now we are ready to start our WindTakeFS game Navigate to :
cocos2d-x-3.8.1\Projects\SnakeJS\frameworks\runtime-src\proj.win32\Release.win32\
look for SnakeJS.exe click it to execute the game .


And your game should look like this :


We are ready to build our "SnakeJS" game for iOS the next tutorial :
Snake Game Using Cocos2d-x Javascript iOS- PART 6 


Play the final SnakeJS game:
http://meiry.github.io/SnakeJS-html5/publish/html5/
The SnakeJS source code:
https://github.com/meiry/SnakeJS-html5


Snake Game Using Cocos2d-x HTML5 - PART 4

In this final tutorial we going to minify all this gigantic cocos2d-x javascript + html framework
To 1 small JavaScript file.
To do this magic we should go back to when we setup the Cocos2d-x first.

1. When you run the python "setup.py" script it ask you to set the ANT_ROOT global variable value .as Shown here:





Once done , we are ready to minify our project , if you follow my tutorial :
Debug Cocos2d-x HTML5 Project using NetBeans IDE and Chrome browser in 10 easy steps.

2. So now it is the time to copy back the files to the cocos2d-x directory structure , in my pc it is under : cocos2d-x-3.8.1\cocos2d-x-3.8.1\Projects\SnakeJS




3. Open command line window in our SnakeJS root and type the following command :

1
cocos compile -p web -m release
4. The Final game should be in this directory :Projects\SnakeJS\publish\html5






 5. That's it ! you can run the game in your server using the good old index.html entry point .


We are ready to build our "SnakeJS" game for Windows desktop  the next tutorial :
Snake Game Using Cocos2d-x Javascript - PART 5 desktop

Play the final SnakeJS game :
http://meiry.github.io/SnakeJS-html5/publish/html5/
The SnakeJS source code:
https://github.com/meiry/SnakeJS-html5

Friday, October 2, 2015

Snake Game Using Cocos2d-x HTML5 - PART 3


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


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
//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