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