OLD | NEW |
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 (function() { | 4 (function() { |
5 'use strict'; | 5 'use strict'; |
6 /** | 6 /** |
7 * T-Rex runner. | 7 * T-Rex runner. |
8 * @param {string} outerContainerId Outer containing element id. | 8 * @param {string} outerContainerId Outer containing element id. |
9 * @param {object} opt_config | 9 * @param {object} opt_config |
10 * @constructor | 10 * @constructor |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
80 | 80 |
81 /** @const */ | 81 /** @const */ |
82 var IS_HIDPI = window.devicePixelRatio > 1; | 82 var IS_HIDPI = window.devicePixelRatio > 1; |
83 | 83 |
84 /** @const */ | 84 /** @const */ |
85 var IS_MOBILE = window.navigator.userAgent.indexOf('Mobi') > -1; | 85 var IS_MOBILE = window.navigator.userAgent.indexOf('Mobi') > -1; |
86 | 86 |
87 /** @const */ | 87 /** @const */ |
88 var IS_TOUCH_ENABLED = 'ontouchstart' in window; | 88 var IS_TOUCH_ENABLED = 'ontouchstart' in window; |
89 | 89 |
| 90 /** @const */ |
| 91 var IS_IOS = window.navigator.userAgent.indexOf('CriOS') > -1; |
90 | 92 |
91 /** | 93 /** |
92 * Default game configuration. | 94 * Default game configuration. |
93 * @enum {number} | 95 * @enum {number} |
94 */ | 96 */ |
95 Runner.config = { | 97 Runner.config = { |
96 ACCELERATION: 0.001, | 98 ACCELERATION: 0.001, |
97 BG_CLOUD_SPEED: 0.2, | 99 BG_CLOUD_SPEED: 0.2, |
98 BOTTOM_PAD: 10, | 100 BOTTOM_PAD: 10, |
99 CLEAR_TIME: 3000, | 101 CLEAR_TIME: 3000, |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 var imgSource = imageSources[i]; | 246 var imgSource = imageSources[i]; |
245 this.images[imgSource.name] = document.getElementById(imgSource.id); | 247 this.images[imgSource.name] = document.getElementById(imgSource.id); |
246 } | 248 } |
247 this.init(); | 249 this.init(); |
248 }, | 250 }, |
249 | 251 |
250 /** | 252 /** |
251 * Load and decode base 64 encoded sounds. | 253 * Load and decode base 64 encoded sounds. |
252 */ | 254 */ |
253 loadSounds: function() { | 255 loadSounds: function() { |
254 this.audioContext = new AudioContext(); | 256 if (!IS_IOS) { |
255 var resourceTemplate = | 257 this.audioContext = new AudioContext(); |
256 document.getElementById(this.config.RESOURCE_TEMPLATE_ID).content; | 258 var resourceTemplate = |
| 259 document.getElementById(this.config.RESOURCE_TEMPLATE_ID).content; |
257 | 260 |
258 for (var sound in Runner.sounds) { | 261 for (var sound in Runner.sounds) { |
259 var soundSrc = resourceTemplate.getElementById(Runner.sounds[sound]).src; | 262 var soundSrc = |
260 soundSrc = soundSrc.substr(soundSrc.indexOf(',') + 1); | 263 resourceTemplate.getElementById(Runner.sounds[sound]).src; |
261 var buffer = decodeBase64ToArrayBuffer(soundSrc); | 264 soundSrc = soundSrc.substr(soundSrc.indexOf(',') + 1); |
| 265 var buffer = decodeBase64ToArrayBuffer(soundSrc); |
262 | 266 |
263 // Async, so no guarantee of order in array. | 267 // Async, so no guarantee of order in array. |
264 this.audioContext.decodeAudioData(buffer, function(index, audioData) { | 268 this.audioContext.decodeAudioData(buffer, function(index, audioData) { |
265 this.soundFx[index] = audioData; | 269 this.soundFx[index] = audioData; |
266 }.bind(this, sound)); | 270 }.bind(this, sound)); |
| 271 } |
267 } | 272 } |
268 }, | 273 }, |
269 | 274 |
270 /** | 275 /** |
271 * Sets the game speed. Adjust the speed accordingly if on a smaller screen. | 276 * Sets the game speed. Adjust the speed accordingly if on a smaller screen. |
272 * @param {number} opt_speed | 277 * @param {number} opt_speed |
273 */ | 278 */ |
274 setSpeed: function(opt_speed) { | 279 setSpeed: function(opt_speed) { |
275 var speed = opt_speed || this.currentSpeed; | 280 var speed = opt_speed || this.currentSpeed; |
276 | 281 |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
450 this.canvasCtx.clearRect(0, 0, this.dimensions.WIDTH, | 455 this.canvasCtx.clearRect(0, 0, this.dimensions.WIDTH, |
451 this.dimensions.HEIGHT); | 456 this.dimensions.HEIGHT); |
452 }, | 457 }, |
453 | 458 |
454 /** | 459 /** |
455 * Update the game frame. | 460 * Update the game frame. |
456 */ | 461 */ |
457 update: function() { | 462 update: function() { |
458 this.drawPending = false; | 463 this.drawPending = false; |
459 | 464 |
460 var now = performance.now(); | 465 var now = getTimeStamp(); |
461 var deltaTime = now - (this.time || now); | 466 var deltaTime = now - (this.time || now); |
462 this.time = now; | 467 this.time = now; |
463 | 468 |
464 if (this.activated) { | 469 if (this.activated) { |
465 this.clearCanvas(); | 470 this.clearCanvas(); |
466 | 471 |
467 if (this.tRex.jumping) { | 472 if (this.tRex.jumping) { |
468 this.tRex.updateJump(deltaTime, this.config); | 473 this.tRex.updateJump(deltaTime, this.config); |
469 } | 474 } |
470 | 475 |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
616 var isjumpKey = Runner.keycodes.JUMP[keyCode] || | 621 var isjumpKey = Runner.keycodes.JUMP[keyCode] || |
617 e.type == Runner.events.TOUCHEND || | 622 e.type == Runner.events.TOUCHEND || |
618 e.type == Runner.events.MOUSEDOWN; | 623 e.type == Runner.events.MOUSEDOWN; |
619 | 624 |
620 if (this.isRunning() && isjumpKey) { | 625 if (this.isRunning() && isjumpKey) { |
621 this.tRex.endJump(); | 626 this.tRex.endJump(); |
622 } else if (Runner.keycodes.DUCK[keyCode]) { | 627 } else if (Runner.keycodes.DUCK[keyCode]) { |
623 this.tRex.speedDrop = false; | 628 this.tRex.speedDrop = false; |
624 } else if (this.crashed) { | 629 } else if (this.crashed) { |
625 // Check that enough time has elapsed before allowing jump key to restart. | 630 // Check that enough time has elapsed before allowing jump key to restart. |
626 var deltaTime = performance.now() - this.time; | 631 var deltaTime = getTimeStamp() - this.time; |
627 | 632 |
628 if (Runner.keycodes.RESTART[keyCode] || | 633 if (Runner.keycodes.RESTART[keyCode] || |
629 (e.type == Runner.events.MOUSEUP && e.target == this.canvas) || | 634 (e.type == Runner.events.MOUSEUP && e.target == this.canvas) || |
630 (deltaTime >= this.config.GAMEOVER_CLEAR_TIME && | 635 (deltaTime >= this.config.GAMEOVER_CLEAR_TIME && |
631 Runner.keycodes.JUMP[keyCode])) { | 636 Runner.keycodes.JUMP[keyCode])) { |
632 this.restart(); | 637 this.restart(); |
633 } | 638 } |
634 } else if (this.paused && isjumpKey) { | 639 } else if (this.paused && isjumpKey) { |
635 this.play(); | 640 this.play(); |
636 } | 641 } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
676 this.gameOverPanel.draw(); | 681 this.gameOverPanel.draw(); |
677 } | 682 } |
678 | 683 |
679 // Update the high score. | 684 // Update the high score. |
680 if (this.distanceRan > this.highestScore) { | 685 if (this.distanceRan > this.highestScore) { |
681 this.highestScore = Math.ceil(this.distanceRan); | 686 this.highestScore = Math.ceil(this.distanceRan); |
682 this.distanceMeter.setHighScore(this.highestScore); | 687 this.distanceMeter.setHighScore(this.highestScore); |
683 } | 688 } |
684 | 689 |
685 // Reset the time clock. | 690 // Reset the time clock. |
686 this.time = performance.now(); | 691 this.time = getTimeStamp(); |
687 }, | 692 }, |
688 | 693 |
689 stop: function() { | 694 stop: function() { |
690 this.activated = false; | 695 this.activated = false; |
691 this.paused = true; | 696 this.paused = true; |
692 cancelAnimationFrame(this.raqId); | 697 cancelAnimationFrame(this.raqId); |
693 this.raqId = 0; | 698 this.raqId = 0; |
694 }, | 699 }, |
695 | 700 |
696 play: function() { | 701 play: function() { |
697 if (!this.crashed) { | 702 if (!this.crashed) { |
698 this.activated = true; | 703 this.activated = true; |
699 this.paused = false; | 704 this.paused = false; |
700 this.tRex.update(0, Trex.status.RUNNING); | 705 this.tRex.update(0, Trex.status.RUNNING); |
701 this.time = performance.now(); | 706 this.time = getTimeStamp(); |
702 this.update(); | 707 this.update(); |
703 } | 708 } |
704 }, | 709 }, |
705 | 710 |
706 restart: function() { | 711 restart: function() { |
707 if (!this.raqId) { | 712 if (!this.raqId) { |
708 this.playCount++; | 713 this.playCount++; |
709 this.runningTime = 0; | 714 this.runningTime = 0; |
710 this.activated = true; | 715 this.activated = true; |
711 this.crashed = false; | 716 this.crashed = false; |
712 this.distanceRan = 0; | 717 this.distanceRan = 0; |
713 this.setSpeed(this.config.SPEED); | 718 this.setSpeed(this.config.SPEED); |
714 | 719 |
715 this.time = performance.now(); | 720 this.time = getTimeStamp(); |
716 this.containerEl.classList.remove(Runner.classes.CRASHED); | 721 this.containerEl.classList.remove(Runner.classes.CRASHED); |
717 this.clearCanvas(); | 722 this.clearCanvas(); |
718 this.distanceMeter.reset(this.highestScore); | 723 this.distanceMeter.reset(this.highestScore); |
719 this.horizon.reset(); | 724 this.horizon.reset(); |
720 this.tRex.reset(); | 725 this.tRex.reset(); |
721 this.playSound(this.soundFx.BUTTON_PRESS); | 726 this.playSound(this.soundFx.BUTTON_PRESS); |
722 | 727 |
723 this.update(); | 728 this.update(); |
724 } | 729 } |
725 }, | 730 }, |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
801 function getRandomNum(min, max) { | 806 function getRandomNum(min, max) { |
802 return Math.floor(Math.random() * (max - min + 1)) + min; | 807 return Math.floor(Math.random() * (max - min + 1)) + min; |
803 } | 808 } |
804 | 809 |
805 | 810 |
806 /** | 811 /** |
807 * Vibrate on mobile devices. | 812 * Vibrate on mobile devices. |
808 * @param {number} duration Duration of the vibration in milliseconds. | 813 * @param {number} duration Duration of the vibration in milliseconds. |
809 */ | 814 */ |
810 function vibrate(duration) { | 815 function vibrate(duration) { |
811 if (IS_MOBILE) { | 816 if (IS_MOBILE && window.navigator.vibrate) { |
812 window.navigator['vibrate'](duration); | 817 window.navigator.vibrate(duration); |
813 } | 818 } |
814 } | 819 } |
815 | 820 |
816 | 821 |
817 /** | 822 /** |
818 * Create canvas element. | 823 * Create canvas element. |
819 * @param {HTMLElement} container Element to append canvas to. | 824 * @param {HTMLElement} container Element to append canvas to. |
820 * @param {number} width | 825 * @param {number} width |
821 * @param {number} height | 826 * @param {number} height |
822 * @param {string} opt_classname | 827 * @param {string} opt_classname |
(...skipping 21 matching lines...) Expand all Loading... |
844 var arrayBuffer = new ArrayBuffer(len); | 849 var arrayBuffer = new ArrayBuffer(len); |
845 var bytes = new Uint8Array(arrayBuffer); | 850 var bytes = new Uint8Array(arrayBuffer); |
846 | 851 |
847 for (var i = 0; i < len; i++) { | 852 for (var i = 0; i < len; i++) { |
848 bytes[i] = str.charCodeAt(i); | 853 bytes[i] = str.charCodeAt(i); |
849 } | 854 } |
850 return bytes.buffer; | 855 return bytes.buffer; |
851 } | 856 } |
852 | 857 |
853 | 858 |
| 859 /** |
| 860 * Return the current timestamp. |
| 861 * @return {number} |
| 862 */ |
| 863 function getTimeStamp() { |
| 864 return IS_IOS ? new Date().getTime() : performance.now(); |
| 865 } |
| 866 |
| 867 |
854 //****************************************************************************** | 868 //****************************************************************************** |
855 | 869 |
856 | 870 |
857 /** | 871 /** |
858 * Game over panel. | 872 * Game over panel. |
859 * @param {!HTMLCanvasElement} canvas | 873 * @param {!HTMLCanvasElement} canvas |
860 * @param {!HTMLImage} textSprite | 874 * @param {!HTMLImage} textSprite |
861 * @param {!HTMLImage} restartImg | 875 * @param {!HTMLImage} restartImg |
862 * @param {!Object} dimensions Canvas dimensions. | 876 * @param {!Object} dimensions Canvas dimensions. |
863 * @constructor | 877 * @constructor |
(...skipping 553 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1417 this.timer += deltaTime; | 1431 this.timer += deltaTime; |
1418 | 1432 |
1419 // Update the status. | 1433 // Update the status. |
1420 if (opt_status) { | 1434 if (opt_status) { |
1421 this.status = opt_status; | 1435 this.status = opt_status; |
1422 this.currentFrame = 0; | 1436 this.currentFrame = 0; |
1423 this.msPerFrame = Trex.animFrames[opt_status].msPerFrame; | 1437 this.msPerFrame = Trex.animFrames[opt_status].msPerFrame; |
1424 this.currentAnimFrames = Trex.animFrames[opt_status].frames; | 1438 this.currentAnimFrames = Trex.animFrames[opt_status].frames; |
1425 | 1439 |
1426 if (opt_status == Trex.status.WAITING) { | 1440 if (opt_status == Trex.status.WAITING) { |
1427 this.animStartTime = performance.now(); | 1441 this.animStartTime = getTimeStamp(); |
1428 this.setBlinkDelay(); | 1442 this.setBlinkDelay(); |
1429 } | 1443 } |
1430 } | 1444 } |
1431 | 1445 |
1432 // Game intro animation, T-rex moves in from the left. | 1446 // Game intro animation, T-rex moves in from the left. |
1433 if (this.playingIntro && this.xPos < this.config.START_X_POS) { | 1447 if (this.playingIntro && this.xPos < this.config.START_X_POS) { |
1434 this.xPos += Math.round((this.config.START_X_POS / | 1448 this.xPos += Math.round((this.config.START_X_POS / |
1435 this.config.INTRO_DURATION) * deltaTime); | 1449 this.config.INTRO_DURATION) * deltaTime); |
1436 } | 1450 } |
1437 | 1451 |
1438 if (this.status == Trex.status.WAITING) { | 1452 if (this.status == Trex.status.WAITING) { |
1439 this.blink(performance.now()); | 1453 this.blink(getTimeStamp()); |
1440 } else { | 1454 } else { |
1441 this.draw(this.currentAnimFrames[this.currentFrame], 0); | 1455 this.draw(this.currentAnimFrames[this.currentFrame], 0); |
1442 } | 1456 } |
1443 | 1457 |
1444 // Update the frame position. | 1458 // Update the frame position. |
1445 if (this.timer >= this.msPerFrame) { | 1459 if (this.timer >= this.msPerFrame) { |
1446 this.currentFrame = this.currentFrame == | 1460 this.currentFrame = this.currentFrame == |
1447 this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1; | 1461 this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1; |
1448 this.timer = 0; | 1462 this.timer = 0; |
1449 } | 1463 } |
(...skipping 779 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2229 | 2243 |
2230 /** | 2244 /** |
2231 * Add a new cloud to the horizon. | 2245 * Add a new cloud to the horizon. |
2232 */ | 2246 */ |
2233 addCloud: function() { | 2247 addCloud: function() { |
2234 this.clouds.push(new Cloud(this.canvas, this.cloudImg, | 2248 this.clouds.push(new Cloud(this.canvas, this.cloudImg, |
2235 this.dimensions.WIDTH)); | 2249 this.dimensions.WIDTH)); |
2236 } | 2250 } |
2237 }; | 2251 }; |
2238 })(); | 2252 })(); |
OLD | NEW |