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