Compare v08 to v09
Produced: 13/1/17 9:09:41 am
   
Mode:  All  
Left file: /Users/peetj/github/cs-js-beg-platformer/versions/game-08.js  
Right file: /Users/peetj/github/cs-js-beg-platformer/versions/game-09.js  
1 1 /*
2     Title: game-08.js
3     Description: In this version of the code, we are going to give our
4                  player a death sequence when he is hit.
5    
6   */var _player = null;
  2   Title: game-09.js
  3   Description: In this version of the code, we are going to ensure that a
  4                level complete message is displayed if the player collects
  5                all the coins.
  6 */
  7  
  8 /* GLOBAL STATE VARIABLES START WITH A __ */
  9 var _player = null;
7 10 var _viewportX = 0;
8 11 var _scoreDisplay = "";
9 12 var _gameStartTime = (new Date()).getTime();
10 13 var _timeLeftDisplay = "";
11 14 var _secondsTotal = 120;
12 15  
13 16 /* State */
14 17 var __STATE = {};
15 18 __STATE.level = 1;
16 19 __STATE.gameStarted = false;
17 20 __STATE.gameOver = false;
18 21 __STATE.timeLeft = _secondsTotal;
19 22 __STATE.currentScore = 0;
20 23 __STATE.gameOverIsDisplaying = false;
21   __STATE.numCoinsToCollect = -1;
  24 __STATE.numCoinsToCollect = 0;
  25 __STATE.levelComplete = false;
22 26  
23 27 const PLAYER_SPEED = 4;
24 28 const WALKER_VELOCITY = -80;
25 29 const EVENT_PLAYER_DIE = "EVENT_PLAYER_DIE";
26 30 const EVENT_PLAYER_HIT_WALKER = "EVENT_PLAYER_HIT_WALKER";
27 31 const EVENT_PLAYER_HIT_WALL = "EVENT_PLAYER_HIT_WALL";
28 32 const EVENT_GAME_OVER = "EVENT_GAME_OVER";
  33 const EVENT_LEVEL_COMPLETE = "EVENT_LEVEL_COMPLETE";
29 34  
30 35 const SCREENWIDTH = 800;
31 36 const SCREENHEIGHT = 600;
32 37 const TILE_WIDTH = 80;
33 38 const TILE_HEIGHT = 60;
34 39 const GRAVITY_STRENGTH = 1000;
35 40 const RIGHT = 0;
36 41 const LEFT = 1;
37 42 const NONE = 2;
38 43  
39 44 var _player = null;
40 45  
41 46 var tileMap = [
42     [[0,1],[8,1],[0,13],[4,1],[0,8],[8,1],[0,5],[4,1],[0,7],[8,1],[0,6],[8,1],[0,2]],
43     [[0,8],[4,1],[0,3],[4,1],[0,6],[8,1],[0,28]],
44     [[0,8],[0,24],[4,1],[0,3],[4,1],[0,6],[8,1],[0,4]],
45     [[0,11],[4,1],[0,15],[4,1],[0,13],[4,1],[0,6]],
46     [[0,16],[8,1],[0,19],[8,1],[0,11]],
47     [[0,9],[4,1],[0,38]],
  47   [[0,1],[8,1],[0,13],[4,1],[0,8],[8,1],[0,5],[4,1],[0,5],[8,1]],
  48   [[0,8],[4,1],[0,3],[4,1],[0,6],[8,1],[0,23]],
  49   [[0,8],[0,19],[4,1],[0,3],[4,1],[0,6],[8,1],[0,4]],
  50   [[0,11],[4,1],[0,12],[4,1],[0,14],[0,6]],
  51   [[0,16],[8,1],[0,13],[8,1],[0,11]],
  52   [[0,9],[4,1],[0,37]],
48 53   [[0,48]],
49 54   [[1,48]]
50 55 ];
51 56  
52 57 Crafty.init(800, 600, document.getElementById('gamecanvas'));
53 58 setupGlobalBindings();
54 59  
55 60 var assets = {'tiles': ['img/tile-1.png', 'img/platform.png', 'img/platformx2.png']};
56 61 var playerSprite = { 'sprites': { 'img/playerSprite.png': { tile: 50, tileh: 77, map: { man_left: [0, 1], man_right: [0, 2], jump_right: [6, 4] } } } };
57 62  
58 63 initialiseGame();
59 64  
60 65 function initialiseGame () {
61 66   Crafty.load(assets, function(){
62 67     reset();
63 68     loadBackground();
64 69     loadSprites();
65 70     generateMap();
66 71     spawnEntities();
67 72     displayText();
68 73     /* Load sounds */
69 74     Crafty.audio.add("coin", "sounds/coin.wav");
70 75     __STATE.gameStarted = true;
71 76   });
72 77 }
73 78  
74 79 function loadBackground () {
75 80   Crafty.background('#3BB9FF');
76 81   //Crafty.background('#FFFFFF url(img/bg.png) repeat-x center center');
77 82 }
78 83  
79 84 function loadSprites () {
80 85   Crafty.load(playerSprite);
81     Crafty.load(walkerSprite);
82 86 }
83 87  
84 88 function spawnEntities () {
85 89   spawnPlayer();
86 90   spawnWalkers();
87 91 }
88 92  
89 93 function spawnPlayer (){
90 94   _player = Crafty.e('Player, 2D, DOM, man_right, SpriteAnimation, Twoway, Collision, Gravity, Tween, Keyboard')
91 95     .attr({
92 96       x: 50,
93 97       y: 263
94 98     })
95 99     .reel('moveRight', 500, [[0, 2], [1, 2], [2, 2], [3, 2], [4, 2], [5, 2], [6, 2], [7, 2]])
96 100     .reel('moveLeft', 500, [[0, 1], [1, 1], [2, 1], [3, 1], [4, 1], [5, 1], [6, 1], [7, 1]])
97 101     .twoway(200, 510)
98 102     .gravity('FloorTile')
99 103     .gravityConst(GRAVITY_STRENGTH)
100 104     .bind('KeyDown',
101 105       function (e) {
  106         if(__STATE.levelComplete)
  107           return;
102 108         if (Crafty.keydown['37'] && Crafty.keydown['39']) {
103 109           this.pauseAnimation();
104 110           this.resetAnimation();
105 111           this.isMoving = false;
106 112           return;
107 113         }
108 114         if (e.key === Crafty.keys.RIGHT_ARROW && !this.isJumping) {
109 115           this.animate('moveRight', -1);
110 116         }
111 117         else if (e.key === Crafty.keys.LEFT_ARROW && !this.isJumping) {
112 118           this.animate('moveLeft', -1);
113 119         }
114 120         this.isMoving = true;
115 121       }
116 122     )
117 123     .bind('KeyUp',
118 124       function (e) {
  125         if(__STATE.levelComplete)
  126           return;
119 127         if ((this.isPlaying('moveRight') && e.key === Crafty.keys.RIGHT_ARROW) ||
120 128           (this.isPlaying('moveLeft') && e.key === Crafty.keys.LEFT_ARROW)) {
121 129           this.pauseAnimation();
122 130           this.resetAnimation();
123 131           /* Check/Set the player's moving state */
124 132           if (e.key === Crafty.keys.RIGHT_ARROW || e.key === Crafty.keys.LEFT_ARROW) {
125 133             this.isMoving = false;
126 134             return;
127 135           }
128 136         }
129 137         /* Kickstart animation if we need to */
130 138         if (this.isDown('RIGHT_ARROW') && !this.isJumping) {
131 139           this.animate('moveRight', -1);
132 140           this.isMoving = true;
133 141         }
134 142         else if (this.isDown('LEFT_ARROW') && !this.isJumping) {
135 143           this.animate('moveLeft', -1);
136 144           this.isMoving = true;
137 145         }
138 146       }
139 147     )
140 148     .bind('Moved', function (obj) {
141 149       if (this.x >= (SCREENWIDTH / 2) && this.x <= 3440) {
142 150         Crafty.viewport.scroll('_x', (this.x - (SCREENWIDTH / 2)) * -1);
143 151         displayScore();
144 152         displayTimeLeft();
145 153       }
146 154     })
147 155     .bind('CheckJumping', function (ground) {
148 156       this.isJumping = true;
149 157       var  canJump, doubleJump;
150 158       if(this.canJump === false && !this.inDoubleJumpMode){
151 159         /* Lets check if there is a platform above us */
152 160         var platforms = Crafty('Tile');
153 161         for(var i=0; i < platforms.length; i++){
154 162           var platform = platforms[i];
155 163           platform = Crafty(platform);
156 164           if(platform.x < this.x && platform.y < this.y && platform.x+160 > this.x){
157 165             /* We probably have a platform above us so don't allow double jump */
158 166             canJump = false;
159 167             doubleJump = false;
160 168             break;
161 169           }
162 170         }
163 171         this.canJump = canJump !== undefined ? canJump : true;
164 172         this.inDoubleJumpMode = doubleJump !== undefined ? doubleJump : true;
165 173       }
166 174       else if(this.inDoubleJumpMode){
167 175         this.canJump = false;
168 176       }
169 177       this.pauseAnimation();
170 178       this.resetAnimation();
171 179       //this.animate('jump', 1)
172 180       this.sprite(this.currentDirection === RIGHT ? 6 : 2, 4);
173 181     })
174 182     .bind('LandedOnGround', function (ground) {
175 183       if (this.isJumping) {
176 184         this.isJumping = false;
177 185         this.gravityConst(GRAVITY_STRENGTH);
178 186         /* We cannot get the direction from the velocity. We'll use the current loaded sprite animation  */
179 187         this.sprite(0, this.getReel().id === 'moveRight' ? 2 : 1);
180 188         /* We may need to enable controls here as we may have disabled them */
181 189         if(this.bounced){
182 190           this.velocity().x = 0;
183 191           this.bounced = false;
184 192         }
185 193         this.inDoubleJumpMode = false;
186 194       }
187 195       /* Will need to enable controls in some circumstances */
188         this.enableControl();
  196       if(!__STATE.levelComplete){
  197         this.enableControl();
  198       }
189 199     })
190 200     .bind('NewDirection', function (obj) {
191 201       /* 0 is neither right nor left so we don't care about it */
192 202       if (obj.x === 0) return;
193 203       this.currentDirection = obj.x === 1 ? RIGHT : LEFT;
194 204       if (this.currentDirection === RIGHT && !this.isJumping) {
195 205         if (this.isMoving) {
196 206           /* Start running again */
197 207           this.animate('moveRight', -1);
198 208         }else {
199 209           this.sprite(0, 2);
200 210         }
201 211       }
202 212       else if (this.currentDirection === LEFT && !this.isJumping) {
203 213         if (this.isMoving) {
204 214           /* Start running again */
205 215           this.animate('moveLeft', -1);
206 216         }else {
207 217           this.sprite(0, 1);
208 218         }
209 219       }
210 220     })
211 221     .checkHits('Platform')
212 222     .bind('HitOn', function(hitData){
213         console.log('Hit Platform:', 'x:', hitData[0].obj.x, 'y:', hitData[0].obj.y, 'PlayerX:', this.x, 'PlayerY:', this.y);
214 223       /* If underneath player, bounce back down */
215 224       this.disableControl();
216 225       this.gravityConst(1000);
217 226       this.velocity().x = 0;
218 227       this.velocity().y = 0;
219 228       Crafty.e("Delay").delay(reinstateMovement, 500, 1);
220 229     })
221 230     .bind('TweenEnd', function(prop){
222 231       if(this.alpha === 0.0){
223 232         this.destroy();
224 233       }
225 234     })
226 235     .bind(EVENT_PLAYER_DIE, function () {
227 236       this.tween({ alpha: 0.0 }, 1000);
228 237       Crafty.trigger(EVENT_GAME_OVER);
229 238     })
230 239     .bind(EVENT_PLAYER_HIT_WALKER, function () {
231 240       this.vy = -400;
232 241       this.tween({ y: this.y - 100 }, 300);
233 242     })
  243     .bind(EVENT_LEVEL_COMPLETE, function () {
  244       this.disableControl();
  245     })
234 246     .bind('EnterFrame', function(){
235 247       if(this.x <= -6) this.x = -5;
236 248       if(this.x >= 3785) this.x = 3784;
237 249     })
238 250  
239 251     /* Set player defaults */
240 252     _player.isJumping = false;
241 253     _player.currentDirection = RIGHT;
242 254     _player.isMoving = false;
243 255     _player.reel('moveRight');
244 256     _player.bounced = false;
245 257     _player.inDoubleJumpMode = false;
246 258 }
247 259  
248 260 function spawnWalkers () {
249 261   Crafty.e('Delay').delay(spawnWalker, Crafty.math.randomInt(2000, 8000), -1);
250 262 }
251 263  
252 264 function spawnWalker () {
253 265   Crafty.e('Walker');
254 266 }
255 267  
  268 /*
  269   The tilemap generation. An example of the first row is shown below
  270  
  271   [[0,1],[8,1],[0,13],[4,1],[0,8],[8,1],[0,5],[4,1],[0,7],[1,1],[8,1]],
  272 */
256 273 function generateMap () {
257 274   const Y_OFFSET = 600 - (tileMap.length * TILE_HEIGHT);
258 275   tileMap.map(function (tileRow, rowIdx) {
259 276     var xPos = 0;
260 277     var yPos = 0;
261 278     tileRow.map(function (tile, tileIdx) {
262 279       yPos = Y_OFFSET + (rowIdx * 60);
263 280       var tileType = tile[0];
264 281       var tileNum = tile[1];
265 282       if (tileType === 0){
266 283         xPos += (tileNum * 80);
267 284       }
268 285       if (tileType === 1) {
269 286         for(var i=0; i < tileNum; i++){
270 287           Crafty.e('FloorTile, 2D, DOM, Image, Collision')
271 288             .attr({ x: xPos, y: yPos, w: TILE_WIDTH, h: TILE_HEIGHT })
272 289             .image(Crafty.assets['img/tile-' + tileType + '.png'].src);
273 290           xPos += 80;
274 291         }
275 292       }
276 293       else{
277 294         if (tileType === 4) {
278 295           Crafty.e('Platform')
279 296             .setImage('img/platform.png')
280 297             .setPlatform(xPos, yPos, 1)
281 298             .addCoins(Crafty.math.randomInt(1,2));
  299           xPos += 160;
282 300         }
283 301         else if (tileType === 8) {
284 302           Crafty.e('Platform')
285 303             .setImage('img/platformx2.png')
286 304             .setPlatform(xPos, yPos, 2)
287 305             .addCoins(Crafty.math.randomInt(1,2));
  306           xPos += 320;
288 307         }
289 308         xPos += 80;
290 309       }
  310       if(rowIdx === 6) console.log('x:',xPos,'y:',yPos);
291 311     });
292 312   });
293 313 }
294 314  
295 315 function displayText () {
296 316   displayScore();
297 317   displayTimeLeft();
298 318 }
299 319  
300 320 function displayTimeLeft () {
301 321   if(_timeLeftDisplay){
302 322     _timeLeftDisplay.destroy();
303 323   }
304 324   _timeLeftDisplay = Crafty.e("2D, DOM, Text")
305 325     .attr({ x: 720 - Crafty.viewport._x, y: 20 })
306 326     .text(__STATE.timeLeft)
307 327     .textColor('#FF0000')
308 328     .textFont({ size: '14px', weight: 'bold', family: 'Courier New' });
309 329 }
310 330  
311 331 function displayScore () {
312 332   if(_scoreDisplay){
313 333     _scoreDisplay.destroy();
314 334   }
315 335   _scoreDisplay = Crafty.e("2D, DOM, Text")
316 336     .attr({ x: 750 - Crafty.viewport._x, y: 20 })
317 337     .text(pad(__STATE.currentScore, 3))
318 338     .textColor('#FF0000')
319 339     .textFont({ size: '14px', weight: 'bold', family: 'Courier New' });
320 340 }
321 341  
322 342 function displayGameOver(){
323 343   if(Crafty("GameOver"))
324 344     Crafty("GameOver").destroy();
325 345  
326 346   Crafty.e("GameOver, 2D, DOM, Text")
327 347     .attr({ x: 280 - Crafty.viewport._x, y: 280, z:100, w: 250 })
328 348     .text("GAME OVER").textColor('#FF0000').textFont({ size: '36px', weight: 'bold', family: 'Courier New' });
329 349 }
330 350  
331 351 function displayLevelComplete(){
332 352   if(Crafty("LevelComplete"))
333 353     Crafty("LevelComplete").destroy();
334 354  
335 355   Crafty.e("LevelComplete, 2D, DOM, Text")
336 356     .attr({ x: 270 - Crafty.viewport._x, y: 280, z:100, w: 350 })
337 357     .text("LEVEL COMPLETE").textColor('#33FF33').textFont({ size: '36px', weight: 'bold', family: 'Courier New' });
338 358 }
339 359  
340 360 function updateScore(amt){
341 361   __STATE.currentScore += amt;
342 362   displayScore();
343 363 }
344 364  
345 365 function pad(num, size) {
346 366     var s = num+"";
347 367     while (s.length < size) s = "0" + s;
348 368     return s;
349 369 }
350 370  
351 371 function setupGlobalBindings () {
352 372   Crafty.bind(EVENT_GAME_OVER, function(){
353 373     _player.disableControl();
354 374     pauseAndResetAnimation(_player);
355 375     displayGameOver();
356 376   })
357 377 }
358 378  
359 379 function pauseAndResetAnimation (ent){
360 380   ent.pauseAnimation();
361 381   ent.resetAnimation();
362 382 }
363 383  
364 384 function reinstateMovement () {
365     _player.enableControl();
  385   if(!__STATE.levelComplete){
  386     _player.enableControl();
  387   }
366 388 }
367 389  
368 390 function reset () {
369 391   _player = null;
370 392   _viewportX = 0;
371 393   _scoreDisplay = "";
372 394   _gameStartTime = (new Date()).getTime();
373 395   _timeLeftDisplay = "";
374 396   _secondsTotal = 120;
375 397  
376 398   __STATE.gameStarted = false;
377 399   __STATE.gameOver = false;
378 400   __STATE.timeLeft = _secondsTotal;
379 401   __STATE.currentScore = 0;
380 402   __STATE.gameOverIsDisplaying = false;
381     __STATE.numCoinsToCollect = -1;
  403   __STATE.numCoinsToCollect = 0;
  404   __STATE.levelComplete = false;
382 405  
383 406   /* Position viewport */
384 407   Crafty.viewport.scroll('_x', 0)
385 408 }
386 409  
387 410 Crafty.bind('EnterFrame', function(){
388     if(__STATE.gameStarted === false)
  411   if(!__STATE.gameStarted || __STATE.levelComplete)
389 412     return;
390 413  
391 414   if(Crafty.frame() % 50 === 1){
392 415     __STATE.timeLeft = _secondsTotal - Math.round(((new Date()).getTime() - _gameStartTime) / 1000);
393       displayTimeLeft();
  416     if(__STATE.timeLeft <= -1){
  417       __STATE.gameOver = true;
  418     }
394 419   }
395 420   if(__STATE.gameOver && !__STATE.gameOverIsDisplaying){
396 421     /* Display Game Over */
397 422     Crafty.trigger(EVENT_GAME_OVER);
398 423     __STATE.gameOverIsDisplaying = true;
399 424   }
  425   if(!__STATE.gameOver){
  426     displayTimeLeft();
  427   }
400 428 })
  429  
  430 Crafty.bind(EVENT_LEVEL_COMPLETE, function(){
  431   console.log('Level Complete...');
  432   __STATE.levelComplete = true;
  433   displayLevelComplete();
  434 })