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); } }); |
- Lines 1 - 16 global variables , the scope of those will be accessible to all functions and objects.
- 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. - Lines 37 - 217 is the main layer that hold all the games element , all of them .
- 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. - 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. - 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). - 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. - Lines 132 -156 CreateFood function , create 1 food square each time called on random location on the game layer . only 1 food object allowed
- 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 - 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, - 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. - 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 . - Lines 238 -242 generate function simple random number generator. used to place the food object.
- Lines 244 - 248 setLabelString function convert int value type to string type
- Lines 250 - 255 create the game Scene , this is the root node of all our game stage nodes.
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
The game development tutorials look great. One of my friend has been looking for some professional tutorials for game development and I am definitely suggesting this blog to him.
ReplyDeleteAlthough I don't know how to work with codes and use them in the game somehow I was looking for these. Thank-you, better show this to my brother so he can help with it.
ReplyDeleteWow, this is a very cool snake, the algorithm is matched reasonably enough.
ReplyDeleteI wrote an applet program in Java thanks to javascript snake tutorial https://explainjava.com/javascript-snake/ but this solution was much easier than you suggest... I still have to learn a lot of theoretical and practical material on Java, in fact. Other tutorial developed the most effective for learning Java, it's for such beginners as I. All those who are on the first step of learning, this script will suit you the best.