├── Procfile
├── .gitignore
├── test
├── mocks
│ ├── styleMock.js
│ └── fileMock.js
├── game.test.js
├── localStorage-mock.js
├── game-mock.js
├── localStorage-mock.test.js
├── leaderboardCall-mock.test.js
└── leaderboardCall-mock.js
├── Images
├── arrow.jpg
├── sceneAB.png
├── sceneGO.png
├── sceneLB.png
├── sceneMM.png
├── sprBtn.png
├── sceneGame.png
├── sceneIntro.png
├── space-key.png
├── masteryBadge.png
├── rebel-symbol.png
├── sprBtn-Down.png
└── sprBtn-Hover.png
├── dist
├── content
│ ├── about.png
│ ├── arrows.png
│ ├── github.png
│ ├── mFalcon.png
│ ├── sprBg0.png
│ ├── sprBg1.png
│ ├── swVader.wav
│ ├── twitter.png
│ ├── xWing.png
│ ├── deathStar.png
│ ├── gameTitle.png
│ ├── linkedin.png
│ ├── saberFour.png
│ ├── saberOne.png
│ ├── saberTwo.png
│ ├── space-key.png
│ ├── sprEnemy1.png
│ ├── gameTitle2.png
│ ├── githubHover.png
│ ├── leaderBoard.png
│ ├── longTimeAgo.png
│ ├── phaserLogo.png
│ ├── r2d2-scream.mp3
│ ├── saberEmpty.png
│ ├── saberThree.png
│ ├── sndBtnDown.wav
│ ├── sndBtnOver.wav
│ ├── sndExplode0.wav
│ ├── sndExplode1.wav
│ ├── sprBtnAbout.png
│ ├── sprBtnPlay.png
│ ├── sprBtnRecord.png
│ ├── sprExplosion.png
│ ├── swForceTheme.mp3
│ ├── swUseForce.wav
│ ├── tieAdvanced.png
│ ├── tieFighterp.png
│ ├── twitterHover.png
│ ├── blaster-firing.wav
│ ├── imperialShutle.png
│ ├── linkedinHover.png
│ ├── saberComplete.png
│ ├── sprBtnPlayDown.png
│ ├── sprBtnRestart.png
│ ├── sprLaserEnemy0.png
│ ├── sprLaserPlayer.png
│ ├── starWarsTheme.mp3
│ ├── swBattleTheme.mp3
│ ├── swVictoryTheme.mp3
│ ├── titleGameOver.png
│ ├── titleGameOver2.png
│ ├── vaderGameOver.jpg
│ ├── sprBtnAboutDown.png
│ ├── sprBtnAboutHover.png
│ ├── sprBtnPlayHover.png
│ ├── sprBtnRecordDown.png
│ ├── sprBtnRecordHover.png
│ ├── sprBtnRestartDown.png
│ ├── swImperialMarch.mp3
│ └── sprBtnRestartHover.png
├── css
│ └── style.css
└── index.html
├── babel.config.js
├── webpack.config.js
├── src
├── entities
│ ├── entityEnemyLaser.js
│ ├── entityPlayerLaser.js
│ ├── entityImperialShutle.js
│ ├── entityScrollingBackground.js
│ ├── Entities.js
│ ├── entityTieFighter.js
│ ├── entityBomb.js
│ ├── entityTieAdvanced.js
│ └── entityPlayer.js
├── localStorage.js
├── game.js
├── scenes
│ ├── SceneIntro.js
│ ├── SceneGameOver.js
│ ├── SceneLeaderBoard.js
│ ├── SceneMainMenu.js
│ ├── SceneAbout.js
│ └── SceneMain.js
└── leaderboardCall.js
├── .eslintrc.json
├── server.js
├── babelrc.js
├── .stickler.yml
├── LICENSE
├── package.json
└── README.md
/Procfile:
--------------------------------------------------------------------------------
1 | web: node server.js
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | node_modules/
--------------------------------------------------------------------------------
/test/mocks/styleMock.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
--------------------------------------------------------------------------------
/test/mocks/fileMock.js:
--------------------------------------------------------------------------------
1 | module.exports = 'test-file-stub';
--------------------------------------------------------------------------------
/Images/arrow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/arrow.jpg
--------------------------------------------------------------------------------
/Images/sceneAB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sceneAB.png
--------------------------------------------------------------------------------
/Images/sceneGO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sceneGO.png
--------------------------------------------------------------------------------
/Images/sceneLB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sceneLB.png
--------------------------------------------------------------------------------
/Images/sceneMM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sceneMM.png
--------------------------------------------------------------------------------
/Images/sprBtn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sprBtn.png
--------------------------------------------------------------------------------
/Images/sceneGame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sceneGame.png
--------------------------------------------------------------------------------
/Images/sceneIntro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sceneIntro.png
--------------------------------------------------------------------------------
/Images/space-key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/space-key.png
--------------------------------------------------------------------------------
/Images/masteryBadge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/masteryBadge.png
--------------------------------------------------------------------------------
/Images/rebel-symbol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/rebel-symbol.png
--------------------------------------------------------------------------------
/Images/sprBtn-Down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sprBtn-Down.png
--------------------------------------------------------------------------------
/Images/sprBtn-Hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/Images/sprBtn-Hover.png
--------------------------------------------------------------------------------
/dist/content/about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/about.png
--------------------------------------------------------------------------------
/dist/content/arrows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/arrows.png
--------------------------------------------------------------------------------
/dist/content/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/github.png
--------------------------------------------------------------------------------
/dist/content/mFalcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/mFalcon.png
--------------------------------------------------------------------------------
/dist/content/sprBg0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBg0.png
--------------------------------------------------------------------------------
/dist/content/sprBg1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBg1.png
--------------------------------------------------------------------------------
/dist/content/swVader.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/swVader.wav
--------------------------------------------------------------------------------
/dist/content/twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/twitter.png
--------------------------------------------------------------------------------
/dist/content/xWing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/xWing.png
--------------------------------------------------------------------------------
/dist/content/deathStar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/deathStar.png
--------------------------------------------------------------------------------
/dist/content/gameTitle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/gameTitle.png
--------------------------------------------------------------------------------
/dist/content/linkedin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/linkedin.png
--------------------------------------------------------------------------------
/dist/content/saberFour.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/saberFour.png
--------------------------------------------------------------------------------
/dist/content/saberOne.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/saberOne.png
--------------------------------------------------------------------------------
/dist/content/saberTwo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/saberTwo.png
--------------------------------------------------------------------------------
/dist/content/space-key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/space-key.png
--------------------------------------------------------------------------------
/dist/content/sprEnemy1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprEnemy1.png
--------------------------------------------------------------------------------
/dist/content/gameTitle2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/gameTitle2.png
--------------------------------------------------------------------------------
/dist/content/githubHover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/githubHover.png
--------------------------------------------------------------------------------
/dist/content/leaderBoard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/leaderBoard.png
--------------------------------------------------------------------------------
/dist/content/longTimeAgo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/longTimeAgo.png
--------------------------------------------------------------------------------
/dist/content/phaserLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/phaserLogo.png
--------------------------------------------------------------------------------
/dist/content/r2d2-scream.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/r2d2-scream.mp3
--------------------------------------------------------------------------------
/dist/content/saberEmpty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/saberEmpty.png
--------------------------------------------------------------------------------
/dist/content/saberThree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/saberThree.png
--------------------------------------------------------------------------------
/dist/content/sndBtnDown.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sndBtnDown.wav
--------------------------------------------------------------------------------
/dist/content/sndBtnOver.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sndBtnOver.wav
--------------------------------------------------------------------------------
/dist/content/sndExplode0.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sndExplode0.wav
--------------------------------------------------------------------------------
/dist/content/sndExplode1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sndExplode1.wav
--------------------------------------------------------------------------------
/dist/content/sprBtnAbout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnAbout.png
--------------------------------------------------------------------------------
/dist/content/sprBtnPlay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnPlay.png
--------------------------------------------------------------------------------
/dist/content/sprBtnRecord.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnRecord.png
--------------------------------------------------------------------------------
/dist/content/sprExplosion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprExplosion.png
--------------------------------------------------------------------------------
/dist/content/swForceTheme.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/swForceTheme.mp3
--------------------------------------------------------------------------------
/dist/content/swUseForce.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/swUseForce.wav
--------------------------------------------------------------------------------
/dist/content/tieAdvanced.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/tieAdvanced.png
--------------------------------------------------------------------------------
/dist/content/tieFighterp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/tieFighterp.png
--------------------------------------------------------------------------------
/dist/content/twitterHover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/twitterHover.png
--------------------------------------------------------------------------------
/dist/content/blaster-firing.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/blaster-firing.wav
--------------------------------------------------------------------------------
/dist/content/imperialShutle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/imperialShutle.png
--------------------------------------------------------------------------------
/dist/content/linkedinHover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/linkedinHover.png
--------------------------------------------------------------------------------
/dist/content/saberComplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/saberComplete.png
--------------------------------------------------------------------------------
/dist/content/sprBtnPlayDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnPlayDown.png
--------------------------------------------------------------------------------
/dist/content/sprBtnRestart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnRestart.png
--------------------------------------------------------------------------------
/dist/content/sprLaserEnemy0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprLaserEnemy0.png
--------------------------------------------------------------------------------
/dist/content/sprLaserPlayer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprLaserPlayer.png
--------------------------------------------------------------------------------
/dist/content/starWarsTheme.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/starWarsTheme.mp3
--------------------------------------------------------------------------------
/dist/content/swBattleTheme.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/swBattleTheme.mp3
--------------------------------------------------------------------------------
/dist/content/swVictoryTheme.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/swVictoryTheme.mp3
--------------------------------------------------------------------------------
/dist/content/titleGameOver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/titleGameOver.png
--------------------------------------------------------------------------------
/dist/content/titleGameOver2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/titleGameOver2.png
--------------------------------------------------------------------------------
/dist/content/vaderGameOver.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/vaderGameOver.jpg
--------------------------------------------------------------------------------
/dist/content/sprBtnAboutDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnAboutDown.png
--------------------------------------------------------------------------------
/dist/content/sprBtnAboutHover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnAboutHover.png
--------------------------------------------------------------------------------
/dist/content/sprBtnPlayHover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnPlayHover.png
--------------------------------------------------------------------------------
/dist/content/sprBtnRecordDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnRecordDown.png
--------------------------------------------------------------------------------
/dist/content/sprBtnRecordHover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnRecordHover.png
--------------------------------------------------------------------------------
/dist/content/sprBtnRestartDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnRestartDown.png
--------------------------------------------------------------------------------
/dist/content/swImperialMarch.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/swImperialMarch.mp3
--------------------------------------------------------------------------------
/dist/content/sprBtnRestartHover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phalado/JS-Capstone/HEAD/dist/content/sprBtnRestartHover.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | node: 'current',
8 | },
9 | },
10 | ],
11 | ],
12 | };
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | entry: './src/game.js',
5 | mode: 'development',
6 | output: {
7 | filename: 'main.js',
8 | path: path.resolve(__dirname, 'dist'),
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/src/entities/entityEnemyLaser.js:
--------------------------------------------------------------------------------
1 | import Entity from './Entities';
2 |
3 | class EnemyLaser extends Entity {
4 | constructor(scene, x, y) {
5 | super(scene, x, y, 'sprLaserEnemy0');
6 | this.body.velocity.y = 200;
7 | }
8 | }
9 |
10 | export default EnemyLaser;
--------------------------------------------------------------------------------
/src/entities/entityPlayerLaser.js:
--------------------------------------------------------------------------------
1 | import Entity from './Entities';
2 |
3 | class PlayerLaser extends Entity {
4 | constructor(scene, x, y) {
5 | super(scene, x, y, 'sprLaserPlayer');
6 | this.body.velocity.y = -200;
7 | }
8 | }
9 |
10 | export default PlayerLaser;
--------------------------------------------------------------------------------
/dist/css/style.css:
--------------------------------------------------------------------------------
1 | canvas {
2 | display: flex;
3 | margin: auto;
4 | }
5 |
6 | .body {
7 | background: black;
8 | color: #ffbe00;
9 | font-family: sans-serif;
10 | font-size: 5vw;
11 | line-height: 1.3;
12 | overflow: hidden;
13 | }
14 |
15 | div {
16 | left: 0;
17 | right: 0;
18 | margin-right: auto!important;
19 | margin-left: auto;
20 | }
21 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "jest": true
6 | },
7 | "parserOptions": {
8 | "ecmaVersion": 2018,
9 | "sourceType": "module"
10 | },
11 | "extends": ["airbnb-base"],
12 | "rules": {
13 | "no-shadow": "off",
14 | "no-param-reassign": "off",
15 | "eol-last": "off",
16 | "arrow-parens": "off"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-unresolved
2 | const express = require('express');
3 | const path = require('path');
4 |
5 | const port = process.env.PORT || 8080;
6 | const app = express();
7 |
8 | app.use(express.static(`${__dirname}/dist`));
9 |
10 | app.get('*', (req, res) => {
11 | res.sendFile(path.resolve(__dirname, 'index.html'));
12 | });
13 |
14 | app.listen(port);
--------------------------------------------------------------------------------
/babelrc.js:
--------------------------------------------------------------------------------
1 | const presets = [
2 | [
3 | '@babel/env',
4 | {
5 | targets: {
6 | browsers: ['>0.25%', 'not ie 11', 'not op_mini all'],
7 | },
8 | modules: false,
9 | },
10 | ],
11 | '@babel/preset-react',
12 | ];
13 | const plugins = [
14 | '@babel/plugin-proposal-class-properties',
15 | '@babel/plugin-transform-modules-commonjs',
16 | ];
17 | module.exports = { presets, plugins };
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Star Wars - Space Shooter
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/game.test.js:
--------------------------------------------------------------------------------
1 | // import Player from './entityPlayer';
2 | import gameRun from './game-mock';
3 |
4 | describe('Tests on a Mocked game', () => {
5 | const game = gameRun();
6 | test('Receive an object in return when call gameRun', () => {
7 | expect(typeof game).toBe('object');
8 | });
9 |
10 | test('Expect to see the object that contains all the games scenes', () => {
11 | expect(typeof game.scene.scenes).toBe('object');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/.stickler.yml:
--------------------------------------------------------------------------------
1 | # add the linters you want stickler to use for this project
2 | linters:
3 | eslint:
4 | # indicate where is the config file for stylelint
5 | config: './.eslintrc.json'
6 |
7 | # add the files here you want to be ignored by stylelint
8 | files:
9 | ignore:
10 | - 'dist/*'
11 | - 'src/phaser.js'
12 |
13 | # PLEASE DO NOT enable auto fixing options
14 | # if you need extra support from you linter - do it in your local env as described in README for this config
15 |
16 | # find full documentation here: https://stickler-ci.com/docs
17 |
--------------------------------------------------------------------------------
/src/localStorage.js:
--------------------------------------------------------------------------------
1 | function localStoreScore(score) {
2 | const scr = JSON.stringify(score);
3 | localStorage.setItem('scores', scr);
4 | }
5 |
6 | function getLocalScores() {
7 | const score = localStorage.getItem('scores');
8 | let result = JSON.parse(score);
9 | if (result === null) {
10 | result = [0, 0];
11 | localStoreScore(result);
12 | }
13 | return result;
14 | }
15 |
16 | function storeScores(score) {
17 | const localScore = getLocalScores();
18 | localScore[0] = score;
19 | localScore[1] = Math.max(...localScore);
20 | localStoreScore(localScore);
21 | }
22 |
23 | export { localStoreScore, getLocalScores, storeScores };
--------------------------------------------------------------------------------
/test/localStorage-mock.js:
--------------------------------------------------------------------------------
1 | function localStoreScore(score) {
2 | const scr = JSON.stringify(score);
3 | localStorage.setItem('scores', scr);
4 | }
5 |
6 | function getLocalScores() {
7 | const score = localStorage.getItem('scores');
8 | let result = JSON.parse(score);
9 | if (result === null) {
10 | result = [0, 0];
11 | localStoreScore(result);
12 | }
13 | return result;
14 | }
15 |
16 | function storeScores(score) {
17 | const localScore = getLocalScores();
18 | localScore[0] = score;
19 | localScore[1] = Math.max(...localScore);
20 | localStoreScore(localScore);
21 | }
22 |
23 | export { localStoreScore, getLocalScores, storeScores };
--------------------------------------------------------------------------------
/src/entities/entityImperialShutle.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import Entity from './Entities';
3 |
4 | class ImperialShutle extends Entity {
5 | constructor(scene, x, y) {
6 | super(scene, x, y, 'imperialShutle', 'ImperialShutle');
7 | this.body.velocity.y = Phaser.Math.Between(50, 100);
8 | this.play('imperialShutle');
9 | this.setData('health', 3);
10 | this.setData('score', 500);
11 | }
12 |
13 | updateHealth() {
14 | if (this.getData('health') > 0) {
15 | this.scene.sfx.explosions[0].play();
16 | this.setData('health', this.getData('health') - 1);
17 | this.body.velocity.y = Phaser.Math.Between(50, 100);
18 | return false;
19 | }
20 |
21 | return true;
22 | }
23 | }
24 |
25 | export default ImperialShutle;
--------------------------------------------------------------------------------
/src/game.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import SceneMainMenu from './scenes/SceneMainMenu';
3 | import SceneMain from './scenes/SceneMain';
4 | import SceneIntro from './scenes/SceneIntro';
5 | import SceneAbout from './scenes/SceneAbout';
6 | import SceneGameOver from './scenes/SceneGameOver';
7 | import SceneLeaderBoard from './scenes/SceneLeaderBoard';
8 |
9 | const config = {
10 | type: Phaser.WEBGL,
11 | parent: 'divld',
12 | width: 480,
13 | height: 640,
14 | backgroundColor: 'black',
15 | dom: {
16 | createContainer: true,
17 | },
18 | physics: {
19 | default: 'arcade',
20 | arcade: {
21 | gravity: { x: 0, y: 0 },
22 | },
23 | },
24 | scene: [
25 | SceneIntro,
26 | SceneMainMenu,
27 | SceneAbout,
28 | SceneMain,
29 | SceneGameOver,
30 | SceneLeaderBoard,
31 | ],
32 | pixelArt: true,
33 | roundPixels: true,
34 | };
35 |
36 | // eslint-disable-next-line no-unused-vars
37 | const game = new Phaser.Game(config);
38 |
--------------------------------------------------------------------------------
/src/scenes/SceneIntro.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | class SceneIntro extends Phaser.Scene {
4 | constructor() {
5 | super({ key: 'SceneIntro' });
6 | }
7 |
8 | preload() {
9 | this.load.image('gameT', 'content/gameTitle.png');
10 | this.load.image('longTimeAgo', 'content/longTimeAgo.png');
11 | }
12 |
13 | create() {
14 | this.title = this.add.image(
15 | this.game.config.width * 0.5,
16 | this.game.config.height * 0.5,
17 | 'longTimeAgo',
18 | );
19 |
20 | this.tweens.add({
21 | targets: this.title,
22 | alpha: { from: 0, to: 1 },
23 | ease: 'Linear',
24 | duration: 4000,
25 | repeat: 0,
26 | yoyo: true,
27 | onComplete: () => {
28 | this.scene.start('SceneMainMenu');
29 | },
30 | });
31 |
32 | this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
33 | }
34 |
35 | update() {
36 | if (this.keySpace.isDown) {
37 | this.scene.start('SceneMainMenu');
38 | }
39 | }
40 | }
41 |
42 | export default SceneIntro;
--------------------------------------------------------------------------------
/test/game-mock.js:
--------------------------------------------------------------------------------
1 | import Phaser from '../src/phaser';
2 | import SceneMainMenu from '../src/scenes/SceneMainMenu';
3 | import SceneMain from '../src/scenes/SceneMain';
4 | import SceneIntro from '../src/scenes/SceneIntro';
5 | import SceneAbout from '../src/scenes/SceneAbout';
6 | import SceneGameOver from '../src/scenes/SceneGameOver';
7 | import SceneLeaderBoard from '../src/scenes/SceneLeaderBoard';
8 |
9 | function gameRun() {
10 | const config = {
11 | type: Phaser.WEBGL,
12 | parent: 'divld',
13 | width: 480,
14 | height: 640,
15 | backgroundColor: 'black',
16 | dom: {
17 | createContainer: true,
18 | },
19 | physics: {
20 | default: 'arcade',
21 | arcade: {
22 | gravity: { x: 0, y: 0 },
23 | },
24 | },
25 | scene: [
26 | SceneIntro,
27 | SceneMainMenu,
28 | SceneMain,
29 | SceneAbout,
30 | SceneGameOver,
31 | SceneLeaderBoard,
32 | ],
33 | pixelArt: true,
34 | roundPixels: true,
35 | };
36 |
37 | const game = new Phaser.Game(config);
38 |
39 | return game;
40 | }
41 |
42 | export default gameRun;
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Raphael Cordeiro
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/localStorage-mock.test.js:
--------------------------------------------------------------------------------
1 | import { localStoreScore, getLocalScores, storeScores } from './localStorage-mock';
2 |
3 | describe('Test modules belongin to gameHelper file', () => {
4 | const scores = getLocalScores();
5 | test('Receive [0, 0] when localStorage is empty', () => {
6 | expect(scores.length).toBe(2);
7 | expect(scores[0]).toBe(0);
8 | expect(scores[1]).toBe(0);
9 | scores[0] = 1000;
10 | scores[1] = 2000;
11 | });
12 |
13 | test('Store an array and request it back', () => {
14 | const scores = [1000, 2000];
15 | localStoreScore(scores);
16 | const result = getLocalScores();
17 | expect(result.length).toBe(scores.length);
18 | expect(result[0]).toBe(scores[0]);
19 | expect(result[1]).toBe(scores[1]);
20 | });
21 |
22 | test('Store an array, then store a bigger value and request it back', () => {
23 | const scores = [1000, 2000];
24 | localStoreScore(scores);
25 | scores[0] = 3000;
26 | storeScores(scores[0]);
27 | const result = getLocalScores();
28 | expect(result.length).toBe(scores.length);
29 | expect(result[0]).toBe(scores[0]);
30 | expect(result[1]).toBe(scores[0]);
31 | });
32 | });
--------------------------------------------------------------------------------
/test/leaderboardCall-mock.test.js:
--------------------------------------------------------------------------------
1 | import { submitHighScore, getScoreBoard, createGame } from './leaderboardCall-mock';
2 |
3 | describe('Test to add a game, add a score to it and request it back', () => {
4 | test('Add a mock game and receive a message with the id', () => {
5 | let id = '';
6 | const result1 = createGame();
7 | result1.then(answer1 => {
8 | expect(answer1).toMatch(/(Game with ID).*(added)/);
9 | // eslint-disable-next-line prefer-destructuring
10 | id = answer1.split(' ')[3];
11 |
12 | test('Add a record for the previous id', () => {
13 | const user = 'UserName';
14 | const score = 5000;
15 | const result2 = submitHighScore(user, score, id);
16 | result2.then(answer2 => {
17 | expect(answer2).toBe('Leaderboard score created correctly.');
18 | });
19 |
20 | test('Get the record added previously', () => {
21 | const result3 = getScoreBoard(id);
22 | result3.then(answer3 => {
23 | expect(answer3.user).toBe('UserName');
24 | expect(answer3.score).toBe(5000);
25 | });
26 | });
27 | });
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/entities/entityScrollingBackground.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | class ScrollingBackground {
4 | constructor(scene, key, velocityY) {
5 | this.scene = scene;
6 | this.key = key;
7 | this.velocityY = velocityY;
8 |
9 | this.layers = this.scene.add.group();
10 | this.createLayers();
11 | }
12 |
13 | createLayers() {
14 | for (let i = 0; i < 2; i += 1) {
15 | const layer = this.scene.add.sprite(0, 0, this.key);
16 | layer.y = (layer.displayHeight * i);
17 |
18 | const flipX = Phaser.Math.Between(0, 10) >= 5 ? -1 : 1;
19 | const flipY = Phaser.Math.Between(0, 10) >= 5 ? -1 : 1;
20 |
21 | layer.setScale(flipX * 2, flipY * 2);
22 | layer.setDepth(-5 - (i - 1));
23 | this.scene.physics.world.enableBody(layer, 0);
24 | layer.body.velocity.y = this.velocityY;
25 |
26 | this.layers.add(layer);
27 | }
28 | }
29 |
30 | update() {
31 | if (this.layers.getChildren()[0].y > 0) {
32 | for (let i = 0; i < this.layers.getChildren().length; i += 1) {
33 | const layer = this.layers.getChildren()[i];
34 | layer.y = (-layer.displayHeight) + (layer.displayHeight * i);
35 | }
36 | }
37 | }
38 | }
39 |
40 | export default ScrollingBackground;
--------------------------------------------------------------------------------
/src/entities/Entities.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | class Entity extends Phaser.GameObjects.Sprite {
4 | constructor(scene, x, y, key, type) {
5 | super(scene, x, y, key);
6 |
7 | this.scene = scene;
8 | this.scene.add.existing(this);
9 | this.scene.physics.world.enableBody(this, 0);
10 | this.setData('type', type);
11 | this.setData('isDead', false);
12 | }
13 |
14 | explode(canDestroy) {
15 | if (!this.getData('isDead')) {
16 | this.setTexture('sprExplosion');
17 | this.setData('score', 0);
18 | this.play('sprExplosion');
19 |
20 | const sound = Phaser.Math.Between(0, this.scene.sfx.explosions.length - 1);
21 | this.scene.sfx.explosions[sound].play();
22 |
23 | if (this.shootTimer !== undefined) {
24 | if (this.shootTimer) {
25 | this.shootTimer.remove(false);
26 | }
27 | }
28 |
29 | this.setAngle(0);
30 | this.body.setVelocity(0, 0);
31 |
32 | this.on('animationcomplete', () => {
33 | if (canDestroy) {
34 | this.destroy();
35 | } else {
36 | this.setVisible(false);
37 | }
38 | }, this);
39 |
40 | this.setData('isDead', true);
41 | }
42 | }
43 | }
44 |
45 | export default Entity;
46 |
--------------------------------------------------------------------------------
/src/entities/entityTieFighter.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import Entity from './Entities';
3 | import EnemyLaser from './entityEnemyLaser';
4 |
5 | class TieFighter extends Entity {
6 | constructor(scene, x, y) {
7 | super(scene, x, y, 'tieFighter', 'TieFighter');
8 | this.body.velocity.y = Phaser.Math.Between(50, 100);
9 | this.shootTimer = this.scene.time.addEvent({
10 | delay: 1500,
11 | callback() {
12 | const laser = new EnemyLaser(
13 | this.scene,
14 | this.x,
15 | this.y,
16 | );
17 | laser.setScale(this.scaleX);
18 | this.scene.enemyLasers.add(laser);
19 | },
20 | callbackScope: this,
21 | loop: true,
22 | });
23 | this.play('tieFighter');
24 | this.setData('health', 2);
25 | this.setData('score', 300);
26 | }
27 |
28 | onDestroy() {
29 | if (this.shootTimer !== undefined) {
30 | if (this.shootTimer) {
31 | this.shootTimer.remove(false);
32 | }
33 | }
34 | }
35 |
36 | updateHealth() {
37 | if (this.getData('health') > 0) {
38 | this.scene.sfx.explosions[1].play();
39 | this.setData('health', this.getData('health') - 1);
40 | this.body.velocity.y = Phaser.Math.Between(50, 100);
41 | return false;
42 | }
43 |
44 | return true;
45 | }
46 | }
47 |
48 | export default TieFighter;
--------------------------------------------------------------------------------
/src/entities/entityBomb.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import Entity from './Entities';
3 |
4 | class Bomb extends Entity {
5 | constructor(scene, x, y) {
6 | super(scene, x, y, 'bomb', 'Bomb');
7 | this.body.velocity.y = Phaser.Math.Between(50, 100);
8 | this.states = {
9 | MOVE_DOWN: 'MOVE_DOWN',
10 | CHASE: 'CHASE',
11 | };
12 | this.state = this.states.MOVE_DOWN;
13 | this.setData('score', 100);
14 | this.answer = true;
15 | }
16 |
17 | update() {
18 | if (!this.getData('isDead') && this.scene.player) {
19 | if (Phaser.Math.Distance.Between(
20 | this.x,
21 | this.y,
22 | this.scene.player.x,
23 | this.scene.player.y,
24 | ) < 320) {
25 | this.state = this.states.CHASE;
26 | }
27 |
28 | if (this.state === this.states.CHASE) {
29 | const dx = this.scene.player.x - this.x;
30 | const dy = this.scene.player.y - this.y;
31 | const angle = Math.atan2(dy, dx);
32 | const speed = 100;
33 |
34 | this.body.setVelocity(
35 | Math.cos(angle) * speed,
36 | Math.sin(angle) * speed,
37 | );
38 | }
39 |
40 | if (this.x < this.scene.player.x) {
41 | this.angle -= 5;
42 | } else {
43 | this.angle += 5;
44 | }
45 | }
46 | }
47 |
48 | updateHealth() {
49 | return this.answer;
50 | }
51 | }
52 |
53 | export default Bomb;
--------------------------------------------------------------------------------
/test/leaderboardCall-mock.js:
--------------------------------------------------------------------------------
1 | async function createGame() {
2 | const game = {
3 | name: 'Mock Game',
4 | };
5 | const post = JSON.stringify(game);
6 | const address = 'https://us-central1-js-capstone-backend.cloudfunctions.net/api/games/';
7 | const settings = {
8 | method: 'POST',
9 | headers: {
10 | Accept: 'application/json',
11 | 'Content-Type': 'application/json',
12 | },
13 | body: post,
14 | };
15 | const response = await fetch(address, settings);
16 | const answer = await response.json();
17 |
18 | return answer;
19 | }
20 |
21 | async function submitHighScore(userName, scoreValue, id) {
22 | const submit = {
23 | user: userName,
24 | score: scoreValue,
25 | };
26 | const post = JSON.stringify(submit);
27 | const address = `https://us-central1-js-capstone-backend.cloudfunctions.net/api/games/${id}/scores/`;
28 | const settings = {
29 | method: 'POST',
30 | headers: {
31 | Accept: 'application/json',
32 | 'Content-Type': 'application/json',
33 | },
34 | body: post,
35 | };
36 | const response = await fetch(address, settings);
37 | const answer = await response.json();
38 | return answer;
39 | }
40 |
41 | function sorting(obj) {
42 | const array = [];
43 | for (let i = 0; i < obj.length; i += 1) {
44 | array.push([obj[i].score, obj[i].user]);
45 | }
46 | return Array.from(array).sort((a, b) => b[0] - a[0]);
47 | }
48 |
49 | async function getScoreBoard(id) {
50 | const address = `https://us-central1-js-capstone-backend.cloudfunctions.net/api/games/${id}/scores/`;
51 | const settings = {
52 | method: 'GET',
53 | headers: {
54 | Accept: 'application/json',
55 | 'Content-Type': 'application/json',
56 | },
57 | };
58 | const response = await fetch(address, settings);
59 | const answer = await response.json();
60 |
61 | return sorting(answer.result);
62 | }
63 |
64 | export { submitHighScore, getScoreBoard, createGame };
65 |
--------------------------------------------------------------------------------
/src/leaderboardCall.js:
--------------------------------------------------------------------------------
1 | async function createGame() {
2 | const game = {
3 | name: 'Star Wars - Space Shooter',
4 | };
5 | const post = JSON.stringify(game);
6 | const address = 'https://us-central1-js-capstone-backend.cloudfunctions.net/api/games/';
7 | const settings = {
8 | method: 'POST',
9 | headers: {
10 | Accept: 'application/json',
11 | 'Content-Type': 'application/json',
12 | },
13 | body: post,
14 | };
15 | const response = await fetch(address, settings);
16 | const answer = await response.json();
17 |
18 | return answer;
19 | }
20 |
21 | async function submitHighScore(userName, scoreValue) {
22 | const submit = {
23 | user: userName,
24 | score: scoreValue,
25 | };
26 | const post = JSON.stringify(submit);
27 | const address = 'https://us-central1-js-capstone-backend.cloudfunctions.net/api/games/N9E2TejbOkDiI58nb6Vu/scores/';
28 | const settings = {
29 | method: 'POST',
30 | headers: {
31 | Accept: 'application/json',
32 | 'Content-Type': 'application/json',
33 | },
34 | body: post,
35 | };
36 | const response = await fetch(address, settings);
37 | const answer = await response.json();
38 | return answer;
39 | }
40 |
41 | function sorting(obj) {
42 | const array = [];
43 | for (let i = 0; i < obj.length; i += 1) {
44 | array.push([obj[i].score, obj[i].user]);
45 | }
46 | return Array.from(array).sort((a, b) => b[0] - a[0]);
47 | }
48 |
49 | async function getScoreBoard() {
50 | const address = 'https://us-central1-js-capstone-backend.cloudfunctions.net/api/games/N9E2TejbOkDiI58nb6Vu/scores/';
51 | const settings = {
52 | method: 'GET',
53 | headers: {
54 | Accept: 'application/json',
55 | 'Content-Type': 'application/json',
56 | },
57 | };
58 | const response = await fetch(address, settings);
59 | const answer = await response.json();
60 |
61 | return sorting(answer.result);
62 | }
63 |
64 | export { submitHighScore, getScoreBoard, createGame };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "JS-Capstone",
3 | "version": "1.0.0",
4 | "description": "Capstone project in the JavaScript course in Microverse",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "jest",
8 | "jest-watch": "jest --watch",
9 | "jest-init": "jest --init",
10 | "install-web": "npm install --save-dev webpack",
11 | "build": "webpack",
12 | "watch": "webpack --watch",
13 | "npx-fix": "npx eslint src/ --fix",
14 | "start": "node server.js",
15 | "heroku-postbuild": "webpack -p"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/phalado/JS-Capstone.git"
20 | },
21 | "keywords": [],
22 | "author": "",
23 | "license": "ISC",
24 | "bugs": {
25 | "url": "https://github.com/phalado/JS-Capstone/issues"
26 | },
27 | "homepage": "https://github.com/phalado/JS-Capstone#readme",
28 | "jest": {
29 | "transform": {
30 | "^.+\\.jsx?$": "babel-jest"
31 | },
32 | "setupFiles": [
33 | "jest-canvas-mock"
34 | ],
35 | "moduleFileExtensions": [
36 | "js",
37 | "jsx"
38 | ],
39 | "moduleNameMapper": {
40 | "\\.(css|less|sass|scss)$": "/test/mocks/styleMock.js",
41 | "\\.(gif|ttf|eot|svg|png)$": "/test/mocks/fileMock.js"
42 | }
43 | },
44 | "devDependencies": {
45 | "@babel/core": "^7.8.7",
46 | "@babel/plugin-proposal-class-properties": "^7.8.3",
47 | "@babel/plugin-transform-modules-commonjs": "^7.8.3",
48 | "@babel/preset-env": "^7.8.7",
49 | "babel-cli": "^6.26.0",
50 | "babel-core": "^6.26.3",
51 | "babel-jest": "^25.1.0",
52 | "eslint": "^6.8.0",
53 | "eslint-config-airbnb-base": "^14.1.0",
54 | "eslint-plugin-import": "^2.20.1",
55 | "express": "^4.17.1",
56 | "jest": "^25.1.0",
57 | "jest-canvas-mock": "^2.2.0",
58 | "webpack": "^4.42.0",
59 | "webpack-cli": "^3.3.11"
60 | },
61 | "dependencies": {
62 | "express": "^4.17.1",
63 | "phaser": "^3.22.0",
64 | "phaser3-project-template": "^1.0.9"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/entities/entityTieAdvanced.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import Entity from './Entities';
3 | import EnemyLaser from './entityEnemyLaser';
4 |
5 | class TieAdvanced extends Entity {
6 | constructor(scene, x, y) {
7 | super(scene, x, y, 'tieAdvanced', 'TieAdvanced');
8 | this.body.velocity.y = Phaser.Math.Between(50, 100);
9 | this.shootTimer = this.scene.time.addEvent({
10 | delay: 1500,
11 | callback() {
12 | const laserL = new EnemyLaser(
13 | this.scene,
14 | this.x - 20,
15 | this.y,
16 | );
17 | laserL.setScale(this.scaleX);
18 | this.scene.enemyLasers.add(laserL);
19 | const laserR = new EnemyLaser(
20 | this.scene,
21 | this.x + 20,
22 | this.y,
23 | );
24 | laserR.setScale(this.scaleX);
25 | this.scene.enemyLasers.add(laserR);
26 | },
27 | callbackScope: this,
28 | loop: true,
29 | });
30 | this.states = {
31 | MOVE_DOWN: 'MOVE_DOWN',
32 | DISTANCE: 'DISTANCE',
33 | };
34 | this.play('tieAdvanced');
35 | this.setData('health', 20);
36 | this.setData('score', 5000);
37 | }
38 |
39 | onDestroy() {
40 | if (this.shootTimer !== undefined) {
41 | if (this.shootTimer) {
42 | this.shootTimer.remove(false);
43 | }
44 | }
45 | }
46 |
47 | updateHealth() {
48 | if (this.getData('health') > 0) {
49 | this.scene.sfx.explosions[1].play();
50 | this.setData('health', this.getData('health') - 1);
51 | this.body.velocity.y = Phaser.Math.Between(50, 100);
52 | return false;
53 | }
54 |
55 | return true;
56 | }
57 |
58 | update() {
59 | if (Phaser.Math.Distance.Between(
60 | this.x,
61 | this.y,
62 | this.scene.player.x,
63 | this.scene.player.y,
64 | ) < 320) {
65 | this.state = this.states.DISTANCE;
66 | }
67 |
68 | if (this.state === this.states.DISTANCE) {
69 | const dy = this.scene.player.y - this.y;
70 | this.body.velocity.x = Phaser.Math.Between(-50, 50);
71 | this.body.velocity.y = dy - 200;
72 | }
73 | }
74 | }
75 |
76 | export default TieAdvanced;
--------------------------------------------------------------------------------
/src/entities/entityPlayer.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import Entity from './Entities';
3 | import PlayerLaser from './entityPlayerLaser';
4 | import { storeScores } from '../localStorage';
5 |
6 | class Player extends Entity {
7 | constructor(scene, x, y, key) {
8 | super(scene, x, y, key, 'Player');
9 | this.setData('speed', 200);
10 | this.play('sprPlayer');
11 | this.setData('isShooting', false);
12 | this.setData('timerShootDelay', 10);
13 | this.setData('timerShootTick', this.getData('timerShootDelay') - 1);
14 | this.setData('health', 5);
15 | this.setData('score', 0);
16 | }
17 |
18 | moveUp() {
19 | this.body.velocity.y = -this.getData('speed');
20 | }
21 |
22 | moveDown() {
23 | this.body.velocity.y = this.getData('speed');
24 | }
25 |
26 | moveLeft() {
27 | this.body.velocity.x = -this.getData('speed');
28 | }
29 |
30 | moveRight() {
31 | this.body.velocity.x = this.getData('speed');
32 | }
33 |
34 | onDestroy() {
35 | this.scene.time.addEvent({
36 | delay: 1000,
37 | callback() {
38 | this.scene.scene.start('SceneGameOver');
39 | },
40 | callbackScope: this,
41 | loop: false,
42 | });
43 | }
44 |
45 | updateHealth() {
46 | if (this.getData('health') > 0) {
47 | if (this.getData('health') === 1) {
48 | this.scene.sfx.useForce.play();
49 | } else {
50 | this.scene.sfx.r2d2Scream.play();
51 | }
52 | this.setData('health', this.getData('health') - 1);
53 | this.scene.cameras.main.shake(250, 0.02);
54 | return false;
55 | }
56 |
57 | return true;
58 | }
59 |
60 | setScore(value) {
61 | if (!this.getData('isDead')) {
62 | this.setData('score', this.getData('score') + value);
63 | storeScores(this.getData('score'));
64 | }
65 | }
66 |
67 | update() {
68 | this.body.setVelocity(0, 0);
69 |
70 | this.x = Phaser.Math.Clamp(this.x, 0, this.scene.game.config.width);
71 | this.y = Phaser.Math.Clamp(this.y, 0, this.scene.game.config.height);
72 |
73 | if (this.getData('isShooting')) {
74 | if (this.getData('timerShootTick') < this.getData('timerShootDelay')) {
75 | this.setData('timerShootTick', this.getData('timerShootTick') + 1);
76 | } else {
77 | const laser = new PlayerLaser(this.scene, this.x, this.y);
78 | this.scene.playerLasers.add(laser);
79 |
80 | this.scene.sfx.laser.play();
81 | this.setData('timerShootTick', 0);
82 | }
83 | }
84 | }
85 | }
86 |
87 | export default Player;
--------------------------------------------------------------------------------
/src/scenes/SceneGameOver.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import ScrollingBackground from '../entities/entityScrollingBackground';
3 | import { getLocalScores } from '../localStorage';
4 | import { submitHighScore } from '../leaderboardCall';
5 |
6 | class SceneGameOver extends Phaser.Scene {
7 | constructor() {
8 | super({ key: 'SceneGameOver' });
9 | }
10 |
11 | preload() {
12 | this.load.audio('gameOver', 'content/swImperialMarch.mp3');
13 | this.load.image('vader', 'content/vaderGameOver.jpg');
14 | this.load.image('gameOverTitle', 'content/titleGameOver2.png');
15 | }
16 |
17 | create() {
18 | this.gameOverTitle = this.add.image(
19 | this.game.config.width * 0.5,
20 | this.game.config.height * 0.1,
21 | 'gameOverTitle',
22 | );
23 |
24 | this.gameOverImage = this.add.image(
25 | this.game.config.width * 0.5,
26 | this.game.config.height * 0.4,
27 | 'vader',
28 | );
29 |
30 | this.scores = getLocalScores();
31 | this.gameOverSceneScore = this.add.text(
32 | this.game.config.width * 0.6,
33 | this.game.config.height * 0.72,
34 | `Score: ${this.scores[0]}`, {
35 | color: '#d0c600',
36 | fontFamily: 'sans-serif',
37 | fontSize: '30px',
38 | lineHeight: 1.3,
39 | align: 'center',
40 | },
41 | );
42 |
43 | this.sfx = {
44 | btnOver: this.sound.add('sndBtnOver', { volume: 0.1 }),
45 | btnDown: this.sound.add('sndBtnDown', { volume: 0.1 }),
46 | };
47 |
48 | this.song = this.sound.add('gameOver', { volume: 0.3 });
49 | this.song.play();
50 |
51 | this.btnRestart = this.add.sprite(
52 | this.game.config.width * 0.5,
53 | this.game.config.height * 0.9,
54 | 'sprBtnRestart',
55 | );
56 |
57 | this.btnRestart.setInteractive();
58 | this.createButton(this.btnRestart, 'sprBtnRestart', 'sprBtnRestartHover', 'sprBtnRestartDown');
59 | this.btnRestart.on('pointerup', () => {
60 | this.btnRestart.setTexture('sprBtnRestart');
61 | this.song.stop();
62 | this.scene.start('SceneMain');
63 | }, this);
64 |
65 | this.btnRecord = this.add.sprite(
66 | this.game.config.width * 0.85,
67 | this.game.config.height * 0.9,
68 | 'sprBtnRecord',
69 | );
70 |
71 | this.btnRecord.setInteractive();
72 | this.createButton(this.btnRecord, 'sprBtnRecord', 'sprBtnRecordHover', 'sprBtnRecordDown');
73 | this.btnRecord.on('pointerup', () => {
74 | this.btnRecord.setTexture('sprBtnRecord');
75 | this.song.stop();
76 | this.scene.start('SceneLeaderBoard');
77 | }, this);
78 |
79 | this.btnAbout = this.add.sprite(
80 | this.game.config.width * 0.15,
81 | this.game.config.height * 0.9,
82 | 'sprBtnAbout',
83 | );
84 |
85 | this.btnAbout.setInteractive();
86 | this.createButton(this.btnAbout, 'sprBtnAbout', 'sprBtnAboutHover', 'sprBtnAboutDown');
87 | this.btnAbout.on('pointerup', () => {
88 | this.btnAbout.setTexture('sprBtnAbout');
89 | this.song.stop();
90 | this.scene.start('SceneAbout');
91 | }, this);
92 |
93 | this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
94 |
95 | this.backgrounds = [];
96 | for (let i = 0; i < 5; i += 1) {
97 | const keys = ['sprBg0', 'sprBg1'];
98 | const key = keys[Phaser.Math.Between(0, keys.length - 1)];
99 | const bg = new ScrollingBackground(this, key, i * 10);
100 | this.backgrounds.push(bg);
101 | }
102 |
103 | this.userName = '';
104 |
105 | const div = document.createElement('div');
106 | div.innerHTML = `
107 |
108 |
109 | `;
110 |
111 | const element = this.add.dom(280, 480, div);
112 | element.addListener('click');
113 |
114 | element.on('click', (event) => {
115 | if (event.target.name === 'submitButton') {
116 | const inputText = document.getElementById('nameField');
117 | if (inputText.value !== '') {
118 | element.removeListener('click');
119 | element.setVisible(false);
120 | this.userName = inputText.value;
121 | this.submit = submitHighScore(this.userName, this.scores[0]);
122 | this.submit.then(() => {
123 | this.scene.scene.song.stop();
124 | this.scene.start('SceneLeaderBoard');
125 | });
126 | }
127 | }
128 | });
129 | }
130 |
131 | update() {
132 | for (let i = 0; i < this.backgrounds.length; i += 1) {
133 | this.backgrounds[i].update();
134 | }
135 |
136 | if (this.keySpace.isDown) {
137 | this.song.stop();
138 | this.scene.start('SceneMain');
139 | }
140 | }
141 |
142 | createButton(btn, spr, sprHover, sprDown) {
143 | btn.on('pointerover', () => {
144 | btn.setTexture(sprHover);
145 | this.sfx.btnOver.play();
146 | }, this);
147 |
148 | btn.on('pointerout', () => {
149 | btn.setTexture(spr);
150 | });
151 |
152 | btn.on('pointerdown', () => {
153 | btn.setTexture(sprDown);
154 | this.sfx.btnDown.play();
155 | }, this);
156 | }
157 | }
158 |
159 | export default SceneGameOver;
--------------------------------------------------------------------------------
/src/scenes/SceneLeaderBoard.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import ScrollingBackground from '../entities/entityScrollingBackground';
3 | import { getScoreBoard } from '../leaderboardCall';
4 |
5 | class SceneLeaderBoard extends Phaser.Scene {
6 | constructor() {
7 | super({ key: 'SceneLeaderBoard' });
8 | }
9 |
10 | preload() {
11 | this.load.audio('victoryTheme', 'content/swVictoryTheme.mp3');
12 | this.load.image('leaderBoardTitle', 'content/leaderBoard.png');
13 |
14 | this.load.scenePlugin({
15 | key: 'rexuiplugin',
16 | url: 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexuiplugin.min.js',
17 | sceneKey: 'rexUI',
18 | });
19 | }
20 |
21 | create() {
22 | this.gameTitle = this.add.image(
23 | this.game.config.width * 0.5,
24 | this.game.config.height * 0.1,
25 | 'leaderBoardTitle',
26 | );
27 |
28 | this.sfx = {
29 | btnOver: this.sound.add('sndBtnOver', { volume: 0.1 }),
30 | btnDown: this.sound.add('sndBtnDown', { volume: 0.1 }),
31 | };
32 |
33 | this.song = this.sound.add('victoryTheme', { volume: 0.3 });
34 | this.song.play();
35 |
36 | this.btnRestart = this.add.sprite(
37 | this.game.config.width * 0.3,
38 | this.game.config.height * 0.9,
39 | 'sprBtnRestart',
40 | );
41 |
42 | this.btnRestart.setInteractive();
43 | this.createButton(this.btnRestart, 'sprBtnRestart', 'sprBtnRestartHover', 'sprBtnRestartDown');
44 | this.btnRestart.on('pointerup', () => {
45 | this.btnRestart.setTexture('sprBtnRestart');
46 | this.song.stop();
47 | this.scene.start('SceneMain');
48 | }, this);
49 |
50 | this.btnAbout = this.add.sprite(
51 | this.game.config.width * 0.7,
52 | this.game.config.height * 0.9,
53 | 'sprBtnAbout',
54 | );
55 |
56 | this.btnAbout.setInteractive();
57 | this.createButton(this.btnAbout, 'sprBtnAbout', 'sprBtnAboutHover', 'sprBtnAboutDown');
58 | this.btnAbout.on('pointerup', () => {
59 | this.btnAbout.setTexture('sprBtnAbout');
60 | this.song.stop();
61 | this.scene.start('SceneAbout');
62 | }, this);
63 |
64 | this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
65 |
66 | this.backgrounds = [];
67 | for (let i = 0; i < 5; i += 1) {
68 | const keys = ['sprBg0', 'sprBg1'];
69 | const key = keys[Phaser.Math.Between(0, keys.length - 1)];
70 | const bg = new ScrollingBackground(this, key, i * 10);
71 | this.backgrounds.push(bg);
72 | }
73 |
74 | this.getScores = getScoreBoard();
75 |
76 | this.getScores.then(scores => {
77 | this.config = {
78 | color: '#d0c600',
79 | fontFamily: 'sans-serif',
80 | fontSize: '3vw',
81 | lineHeight: 1.3,
82 | align: 'center',
83 | };
84 |
85 | const scrollMode = 0;
86 | this.rexUI.add.gridTable({
87 | x: this.game.config.width * 0.46,
88 | y: 320,
89 | width: 400,
90 | height: 420,
91 | scrollMode,
92 | table: {
93 | cellWidth: (scrollMode === 0) ? undefined : 60,
94 | cellHeight: (scrollMode === 0) ? 60 : undefined,
95 | columns: 3,
96 | mask: {
97 | padding: 2,
98 | },
99 | reuseCellContainer: true,
100 | },
101 | slider: {
102 | track: this.rexUI.add.roundRectangle(0, 0, 20, 10, 10, 0xfcf8a2),
103 | thumb: this.rexUI.add.roundRectangle(0, 0, 0, 0, 13, 0x847d00),
104 | },
105 | createCellContainerCallback(cell, cellContainer) {
106 | const { scene } = cell;
107 | const { width } = cell;
108 | const { height } = cell;
109 | const { item } = cell;
110 | if (cellContainer === null) {
111 | cellContainer = scene.rexUI.add.label({
112 | width,
113 | height,
114 | align: 'center',
115 | orientation: scrollMode,
116 | text: scene.add.text(0, 0, '', {
117 | color: '#d0c600',
118 | fontFamily: 'sans-serif',
119 | fontSize: '2vw',
120 | lineHeight: 1.3,
121 | }),
122 | });
123 | }
124 |
125 | cellContainer.setMinSize(width, height);
126 | cellContainer.getElement('text').setText(item);
127 | return cellContainer;
128 | },
129 | items: this.getItems(20, scores),
130 | })
131 | .layout();
132 | });
133 |
134 | this.getItems = (count, score) => {
135 | const data = ['Rank', 'User', 'Score'];
136 |
137 | for (let i = 0; i < count; i += 1) {
138 | if (score[i]) {
139 | data.push(i + 1);
140 | data.push(score[i][1]);
141 | data.push(score[i][0]);
142 | }
143 | }
144 | return data;
145 | };
146 | }
147 |
148 | update() {
149 | if (this.keySpace.isDown) {
150 | this.song.stop();
151 | this.scene.start('SceneMain');
152 | }
153 | }
154 |
155 | createButton(btn, spr, sprHover, sprDown) {
156 | btn.on('pointerover', () => {
157 | btn.setTexture(sprHover);
158 | this.sfx.btnOver.play();
159 | }, this);
160 |
161 | btn.on('pointerout', () => {
162 | btn.setTexture(spr);
163 | });
164 |
165 | btn.on('pointerdown', () => {
166 | btn.setTexture(sprDown);
167 | this.sfx.btnDown.play();
168 | }, this);
169 | }
170 | }
171 |
172 | export default SceneLeaderBoard;
--------------------------------------------------------------------------------
/src/scenes/SceneMainMenu.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import ScrollingBackground from '../entities/entityScrollingBackground';
3 | import { getLocalScores } from '../localStorage';
4 |
5 | class SceneMainMenu extends Phaser.Scene {
6 | constructor() {
7 | super({ key: 'SceneMainMenu' });
8 | }
9 |
10 | preload() {
11 | this.load.image('sprBg0', 'content/sprBg0.png');
12 | this.load.image('sprBg1', 'content/sprBg1.png');
13 |
14 | this.load.image('sprBtnPlay', 'content/sprBtnPlay.png');
15 | this.load.image('sprBtnPlayHover', 'content/sprBtnPlayHover.png');
16 | this.load.image('sprBtnPlayDown', 'content/sprBtnPlayDown.png');
17 |
18 | this.load.image('sprBtnRestart', 'content/sprBtnRestart.png');
19 | this.load.image('sprBtnRestartHover', 'content/sprBtnRestartHover.png');
20 | this.load.image('sprBtnRestartDown', 'content/sprBtnRestartDown.png');
21 |
22 | this.load.image('sprBtnRecord', 'content/sprBtnRecord.png');
23 | this.load.image('sprBtnRecordHover', 'content/sprBtnRecordHover.png');
24 | this.load.image('sprBtnRecordDown', 'content/sprBtnRecordDown.png');
25 |
26 | this.load.image('sprBtnAbout', 'content/sprBtnAbout.png');
27 | this.load.image('sprBtnAboutHover', 'content/sprBtnAboutHover.png');
28 | this.load.image('sprBtnAboutDown', 'content/sprBtnAboutDown.png');
29 |
30 | this.load.image('gameTitle', 'content/gameTitle2.png');
31 | this.load.image('arrowKeys', 'content/arrows.png');
32 | this.load.image('spaceKey', 'content/space-key.png');
33 |
34 | this.load.audio('sndBtnOver', 'content/sndBtnOver.wav');
35 | this.load.audio('sndBtnDown', 'content/sndBtnDown.wav');
36 | this.load.audio('theme', 'content/starWarsTheme.mp3');
37 | }
38 |
39 | create() {
40 | this.sfx = {
41 | btnOver: this.sound.add('sndBtnOver', { volume: 0.1 }),
42 | btnDown: this.sound.add('sndBtnDown', { volume: 0.1 }),
43 | };
44 |
45 | this.gameTitle = this.add.image(
46 | this.game.config.width * 0.5,
47 | this.game.config.height * 0.3,
48 | 'gameTitle',
49 | );
50 |
51 | this.btnPlay = this.add.sprite(
52 | this.game.config.width * 0.25,
53 | this.game.config.height * 0.65,
54 | 'sprBtnPlay',
55 | );
56 |
57 | this.btnPlay.setInteractive();
58 | this.createButton(this.btnPlay, 'sprBtnPlay', 'sprBtnPlayHover', 'sprBtnPlayDown');
59 | this.btnPlay.on('pointerup', () => {
60 | this.btnPlay.setTexture('sprBtnPlay');
61 | this.song.stop();
62 | this.scene.start('SceneMain');
63 | }, this);
64 |
65 | this.btnRecord = this.add.sprite(
66 | this.game.config.width * 0.25,
67 | this.game.config.height * 0.75,
68 | 'sprBtnRecord',
69 | );
70 |
71 | this.btnRecord.setInteractive();
72 | this.createButton(this.btnRecord, 'sprBtnRecord', 'sprBtnRecordHover', 'sprBtnRecordDown');
73 | this.btnRecord.on('pointerup', () => {
74 | this.btnRecord.setTexture('sprBtnRecord');
75 | this.song.stop();
76 | this.scene.start('SceneLeaderBoard');
77 | }, this);
78 |
79 | this.btnAbout = this.add.sprite(
80 | this.game.config.width * 0.25,
81 | this.game.config.height * 0.55,
82 | 'sprBtnAbout',
83 | );
84 |
85 | this.btnAbout.setInteractive();
86 | this.createButton(this.btnAbout, 'sprBtnAbout', 'sprBtnAboutHover', 'sprBtnAboutDown');
87 | this.btnAbout.on('pointerup', () => {
88 | this.btnAbout.setTexture('sprBtnAbout');
89 | this.song.stop();
90 | this.scene.start('SceneAbout');
91 | }, this);
92 |
93 | this.textConfig = {
94 | color: '#d0c600',
95 | fontFamily: 'sans-serif',
96 | fontSize: '20px',
97 | lineHeight: 1.3,
98 | align: 'justify',
99 | wordWrap: {
100 | width: this.game.config.width * 0.8,
101 | useAdvancedWrap: true,
102 | },
103 | };
104 |
105 | this.add.text(
106 | this.game.config.width * 0.6,
107 | this.game.config.height * 0.55,
108 | 'Controls:',
109 | this.textConfig,
110 | );
111 |
112 | this.arrowKeys = this.add.image(
113 | this.game.config.width * 0.65,
114 | this.game.config.height * 0.65,
115 | 'arrowKeys',
116 | );
117 |
118 | this.add.text(
119 | this.game.config.width * 0.8,
120 | this.game.config.height * 0.65,
121 | 'Move.',
122 | this.textConfig,
123 | );
124 |
125 | this.spaceKey = this.add.image(
126 | this.game.config.width * 0.65,
127 | this.game.config.height * 0.75,
128 | 'spaceKey',
129 | );
130 |
131 | this.add.text(
132 | this.game.config.width * 0.8,
133 | this.game.config.height * 0.73,
134 | 'Shoot.',
135 | this.textConfig,
136 | );
137 |
138 | this.scores = getLocalScores();
139 |
140 | this.scoreTextConfig = {
141 | color: '#d0c600',
142 | fontFamily: 'sans-serif',
143 | fontSize: '2vw',
144 | lineHeight: 1.3,
145 | textAlign: 'center',
146 | };
147 |
148 | this.sceneScore = this.add.text(
149 | this.game.config.width * 0.05,
150 | this.game.config.height * 0.85,
151 | `Last Score: ${this.scores[0]}`,
152 | this.scoreTextConfig,
153 | );
154 |
155 | this.sceneScore = this.add.text(
156 | this.game.config.width * 0.05,
157 | this.game.config.height * 0.9,
158 | `High Score: ${this.scores[1]}`,
159 | this.scoreTextConfig,
160 | );
161 |
162 | this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
163 |
164 | this.backgrounds = [];
165 | for (let i = 0; i < 5; i += 1) {
166 | const keys = ['sprBg0', 'sprBg1'];
167 | const key = keys[Phaser.Math.Between(0, keys.length - 1)];
168 | const bg = new ScrollingBackground(this, key, i * 10);
169 | this.backgrounds.push(bg);
170 | }
171 |
172 | this.song = this.sound.add('theme', { volume: 0.1 });
173 | this.song.play();
174 | }
175 |
176 | update() {
177 | for (let i = 0; i < this.backgrounds.length; i += 1) {
178 | this.backgrounds[i].update();
179 | }
180 |
181 | if (this.keySpace.isDown) {
182 | this.song.stop();
183 | this.scene.start('SceneMain');
184 | }
185 | }
186 |
187 | createButton(btn, spr, sprHover, sprDown) {
188 | btn.on('pointerover', () => {
189 | btn.setTexture(sprHover);
190 | this.sfx.btnOver.play();
191 | }, this);
192 |
193 | btn.on('pointerout', () => {
194 | btn.setTexture(spr);
195 | });
196 |
197 | btn.on('pointerdown', () => {
198 | btn.setTexture(sprDown);
199 | this.sfx.btnDown.play();
200 | }, this);
201 | }
202 | }
203 |
204 | export default SceneMainMenu;
--------------------------------------------------------------------------------
/src/scenes/SceneAbout.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | import ScrollingBackground from '../entities/entityScrollingBackground';
4 |
5 | class SceneAbout extends Phaser.Scene {
6 | constructor() {
7 | super({ key: 'SceneAbout' });
8 | }
9 |
10 | preload() {
11 | this.load.audio('victoryTheme', 'content/swVictoryTheme.mp3');
12 | this.load.image('aboutTitle', 'content/about.png');
13 | this.load.image('phaserLogo', 'content/phaserLogo.png');
14 |
15 | this.load.image('github', 'content/github.png');
16 | this.load.image('githubHover', 'content/githubHover.png');
17 | this.load.image('twitter', 'content/twitter.png');
18 | this.load.image('twitterHover', 'content/twitterHover.png');
19 | this.load.image('linkedin', 'content/linkedin.png');
20 | this.load.image('linkedinHover', 'content/linkedinHover.png');
21 | }
22 |
23 | create() {
24 | this.gameTitle = this.add.image(
25 | this.game.config.width * 0.5,
26 | this.game.config.height * 0.08,
27 | 'aboutTitle',
28 | );
29 |
30 | this.sfx = {
31 | btnOver: this.sound.add('sndBtnOver', { volume: 0.1 }),
32 | btnDown: this.sound.add('sndBtnDown', { volume: 0.1 }),
33 | };
34 |
35 | this.song = this.sound.add('victoryTheme', { volume: 0.3 });
36 | this.song.play();
37 |
38 | this.btnPlay = this.add.sprite(
39 | this.game.config.width * 0.3,
40 | this.game.config.height * 0.92,
41 | 'sprBtnPlay',
42 | );
43 |
44 | this.btnPlay.setInteractive();
45 | this.createButton(this.btnPlay, 'sprBtnPlay', 'sprBtnPlayHover', 'sprBtnPlayDown');
46 | this.btnPlay.on('pointerup', () => {
47 | this.btnPlay.setTexture('sprBtnPlay');
48 | this.song.stop();
49 | this.scene.start('SceneMain');
50 | }, this);
51 |
52 | this.btnRecord = this.add.sprite(
53 | this.game.config.width * 0.7,
54 | this.game.config.height * 0.92,
55 | 'sprBtnRecord',
56 | );
57 |
58 | this.btnRecord.setInteractive();
59 | this.createButton(this.btnRecord, 'sprBtnRecord', 'sprBtnRecordHover', 'sprBtnRecordDown');
60 | this.btnRecord.on('pointerup', () => {
61 | this.btnRecord.setTexture('sprBtnRecord');
62 | this.song.stop();
63 | this.scene.start('SceneLeaderBoard');
64 | }, this);
65 |
66 | this.message = [];
67 | this.message.push('This game was produced by Raphael Cordeiro as prerequisite to complete JavaScript program in Microverse.');
68 | this.message.push('To know more about the game and some of its mechanics consider visit its repository: https://github.com/phalado/JS-Capstone/');
69 | this.message.push('This game was produced using Phaser 3 framwork. You can visit theyr website clicking on the logo bellow.');
70 | this.message.push('Feel free to visit my social medias and send me a hello clicking on the icons bellow.');
71 | this.message.push('© 2020 Star Wars & ™ Lucasfilm Ltd. All rights reserved.');
72 |
73 | this.textConfig = {
74 | color: '#d0c600',
75 | fontFamily: 'sans-serif',
76 | fontSize: '17px',
77 | lineHeight: 1.3,
78 | align: 'justify',
79 | wordWrap: {
80 | width: this.game.config.width * 0.8,
81 | useAdvancedWrap: true,
82 | },
83 | };
84 |
85 | this.add.text(
86 | this.game.config.width * 0.1,
87 | this.game.config.height * 0.15,
88 | this.message[0],
89 | this.textConfig,
90 | );
91 |
92 | this.add.text(
93 | this.game.config.width * 0.1,
94 | this.game.config.height * 0.27,
95 | this.message[1],
96 | this.textConfig,
97 | );
98 |
99 | this.add.text(
100 | this.game.config.width * 0.1,
101 | this.game.config.height * 0.4,
102 | this.message[2],
103 | this.textConfig,
104 | );
105 |
106 | this.phaserLogo = this.add.image(
107 | this.game.config.width * 0.5,
108 | this.game.config.height * 0.52,
109 | 'phaserLogo',
110 | );
111 |
112 | this.phaserLogo.setInteractive();
113 | this.phaserLogo.on('pointerup', () => {
114 | window.open('https://phaser.io/', '_blank');
115 | }, this);
116 | this.phaserLogo.on('pointerover', () => {
117 | this.phaserLogo.setScale(1.1);
118 | }, this);
119 | this.phaserLogo.on('pointerout', () => {
120 | this.phaserLogo.setScale(0.91);
121 | });
122 |
123 | this.add.text(
124 | this.game.config.width * 0.1,
125 | this.game.config.height * 0.57,
126 | this.message[3],
127 | this.textConfig,
128 | );
129 |
130 | this.githubIcon = this.createIcon(
131 | this.githubIcon,
132 | this.game.config.width * 0.5,
133 | this.game.config.height * 0.71,
134 | 'https://github.com/phalado',
135 | 'github',
136 | 'githubHover',
137 | );
138 |
139 | this.twitterIcon = this.createIcon(
140 | this.twitterIcon,
141 | this.game.config.width * 0.25,
142 | this.game.config.height * 0.71,
143 | 'https://twitter.com/Phalado',
144 | 'twitter',
145 | 'twitterHover',
146 | );
147 |
148 | this.linkedinIcon = this.createIcon(
149 | this.linkedinIcon,
150 | this.game.config.width * 0.75,
151 | this.game.config.height * 0.71,
152 | 'https://www.linkedin.com/in/raphael-cordeiro/',
153 | 'linkedin',
154 | 'linkedinHover',
155 | );
156 |
157 | this.add.text(
158 | this.game.config.width * 0.1,
159 | this.game.config.height * 0.8,
160 | this.message[4],
161 | this.textConfig,
162 | );
163 |
164 | this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
165 |
166 | this.backgrounds = [];
167 | for (let i = 0; i < 5; i += 1) {
168 | const keys = ['sprBg0', 'sprBg1'];
169 | const key = keys[Phaser.Math.Between(0, keys.length - 1)];
170 | const bg = new ScrollingBackground(this, key, i * 10);
171 | this.backgrounds.push(bg);
172 | }
173 | }
174 |
175 | update() {
176 | if (this.keySpace.isDown) {
177 | this.song.stop();
178 | this.scene.start('SceneMain');
179 | }
180 | }
181 |
182 | createButton(btn, spr, sprHover, sprDown) {
183 | btn.on('pointerover', () => {
184 | btn.setTexture(sprHover);
185 | this.sfx.btnOver.play();
186 | }, this);
187 |
188 | btn.on('pointerout', () => {
189 | btn.setTexture(spr);
190 | });
191 |
192 | btn.on('pointerdown', () => {
193 | btn.setTexture(sprDown);
194 | this.sfx.btnDown.play();
195 | }, this);
196 | }
197 |
198 | createIcon(icon, x, y, link, spr, sprHover) {
199 | icon = this.add.image(x, y, spr);
200 | icon.setInteractive();
201 |
202 | icon.on('pointerup', () => {
203 | window.open(link, '_blank');
204 | }, this);
205 |
206 | icon.on('pointerover', () => {
207 | icon.setScale(1.1);
208 | icon.setTexture(sprHover);
209 | }, this);
210 |
211 | icon.on('pointerout', () => {
212 | icon.setTexture(spr);
213 | icon.setScale(0.91);
214 | });
215 |
216 | return icon;
217 | }
218 | }
219 |
220 | export default SceneAbout;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript Capstone Project: Build a Shooter Game
2 |
3 | 
4 |
5 | This is Microverse's final project in Javascript's course.
6 |
7 | In this project, I build a shooter game using JavaScript's framework [Phaser 3][phaser-url], a "fast, free and fun open-source framework for Canvas and WebGL powered browser games".
8 |
9 |
10 | ## Table of Contents
11 |
12 | - [JavaScript Capstone Project: Build a Shooter Game](#javascript-capstone-project-build-a-shooter-game)
13 | - [Table of Contents](#table-of-contents)
14 | - [About](#about)
15 | - [The game](#the-game)
16 | - [How to play](#how-to-play)
17 | - [Design](#design)
18 | - [Player's ship](#players-ship)
19 | - [Enemies](#enemies)
20 | - [Scenes](#scenes)
21 | - [Technologies used](#technologies-used)
22 | - [Video presentation](#video-presentation)
23 | - [Future work](#future-work)
24 | - [Learning Objectives](#learning-objectives)
25 | - [Transversal](#transversal)
26 | - [Soft skills](#soft-skills)
27 | - [Technical](#technical)
28 | - [Contact](#contact)
29 | - [Acknowledgements](#acknowledgements)
30 |
31 |
32 | ## About
33 |
34 | This project's objective was to build a shooter game similar to York Computer's [tutorial][sg-tutorial]. So I used the main idea but I created my own Star Wars Space Shooter.
35 |
36 | The assignment can be seen [here][assignment].
37 |
38 | Link to a live version [here][live-version].
39 |
40 | Repository: https://github.com/phalado/JS-Capstone
41 |
42 |
43 | ## The game
44 |
45 | This is a simple endless runner. Enemies appear at the top of the canvas Some shoot you, some pass straight, some pursue you. You control Luke Skywalker's X-Wing with **keyboard arrows** while you shoot with **space**. Just destroy as many enemies as you can before your HP runs out and you die.
46 |
47 | In the end, you can add your name to our Leader Board and check the high scores.
48 |
49 |
50 | ### How to play
51 |
52 | First the most important: how to play.
53 |
54 | The commands were explained in the previous section **![wasd][wasd]** to move ![space][space] to shoot.
55 |
56 |
57 | You can play the game online clicking [here][live-version] or locally following these steps:
58 |
59 | * Click on the green button "Clone or Download"
60 | * Click on Download ZIP
61 | * Extract the game
62 | * In your terminal, navigate to the game's folder
63 | * Run 'node server.js'
64 | * Open, in your browser, 'localhost:8080'
65 | * Beat my record and make my name disappear from the Leader Board
66 |
67 |
68 | ### Design
69 |
70 | First of all: this is a simple pixel game. You can't expect to see high-quality graphics. Or medium-quality graphics... So, the ship's designs are just a low-quality version of the movie's design. The only "enemy" that you can see on the screen that is not a Star Wars original is the bomb.
71 |
72 |
73 | #### Player's ship
74 |
75 | The player's ship is Luke's X-Wing: ![X-Wing][X-Wing]
76 |
77 | Your HP is 5 and is represented by Anakin Skywalker's saber at the top of the screen: ![HP-Bar]
78 |
79 | Each damage receive makes R2D2 complain. In your last strenth, a friend comes to give you a wise advise.
80 |
81 |
82 | #### Enemies
83 |
84 | The tie fighter is the most common enemy: ![Tie-Fighter][Tie-Fighter]
85 |
86 | It's HP is 3 and it will shoot you. It gives you 300 points when destroyed.
87 |
88 |
89 | Next, we have the Imperial Shuttle: ![Imp-Sh][Imp-Sh]
90 |
91 | It's HP is 4, it won't shoot you and will give you 500 points.
92 |
93 |
94 | The bomb will pursue you until be destroyed or give you some damage: ![Bomb][Bomb]
95 |
96 | It's destroyed with a single shoot and gives only 100 points, but it's a good idea to get rid of it as soon as possible.
97 |
98 |
99 | The most difficult is Vader himself. He comes in his Tie Advanced: ![Tie-Adv][Tie-Adv]
100 |
101 | His HP is 20 and he gives 5000 points. He won't leave the screen like the others and will shoot you.
102 |
103 | If you manage to destroy his ship don't fool yourself. He will be back, after all (SPOILER ALERT) he is your father!!! (Nooooooooooooooooo!!!!!!!!)
104 |
105 |
106 | #### Scenes
107 |
108 | This game is composed by six scenes each one with its music:
109 |
110 | * Introduction:
111 |
112 | ![SC-Intro][SC-Intro]
113 |
114 | * Main Menu:
115 |
116 | ![SC-MM][SC-MM]
117 |
118 | * Game:
119 |
120 | ![SC-Game][SC-Game]
121 |
122 | * Game Over:
123 |
124 | ![SC-GO][SC-GO]
125 |
126 | * Leader Board:
127 |
128 | ![SC-LB][SC-LB]
129 |
130 | * About:
131 |
132 | ![SCAB][SC-AB]
133 |
134 |
135 | It is important to mention that the Leader Board will only show 20 names.
136 |
137 | It is also important to mention that **Han shot first!!!**
138 |
139 |
140 | ## Technologies used
141 |
142 | To create this game I used:
143 |
144 | * JavaScript
145 | * A bit of HTML and CSS for the front end
146 | * Phaser 3
147 | * Webpack
148 | * Eslint
149 | * Babel
150 | * Jest in the tests
151 | * Express
152 | * Github
153 | * [Heroku](https://www.heroku.com/) for the deployment
154 | * [Leaderboard API service][LB-API] for the leaderboard
155 |
156 |
157 | ## Video presentation
158 |
159 |
160 | https://www.loom.com/share/50085eb2e29b42129edaa0ce6d59d191
161 |
162 |
163 | ## Future Work
164 |
165 | * Mobile version
166 | * Possibility to play with Milenium Falcon
167 | * Stage mode
168 |
169 |
170 |
171 | ## Learning Objectives
172 |
173 |
174 | ### Transversal
175 |
176 | * Use linters (code standards) ![mast][mast]
177 | * Maintain professional Github repos ![mast][mast]
178 | * Deploy apps (Heroku, Netlify) ![mast][mast]
179 |
180 |
181 | ### Soft skills
182 |
183 | * Strong English written communication ![mast][mast]
184 | * Ability to communicate information effectively to non-technical people ![mast][mast]
185 | * Ability to translate business requirements into software solutions ![mast][mast]
186 | * Sets high standards of performance to oneself ![mast][mast]
187 | * Shows a desire to take personal responsibility to complete tasks and solve problems ![mast][mast]
188 | * Ability to multitask and effectively manage time and prioritzation ![mast][mast]
189 |
190 |
191 | ### Technical
192 |
193 | * Create effective JavaScript code that solves the problem ![mast][mast]
194 | * Encapsulate JS code in modules ![mast][mast]
195 | * Use Webpack ![mast][mast]
196 | * Use EcmaScript+ ![mast][mast]
197 | * Deal with async code ![mast][mast]
198 | * Send and receive data from a back-end endpoint ![mast][mast]
199 | * Use JSON format ![mast][mast]
200 | * Use DOM (read/write data) listen to events ![mast][mast]
201 | * Test JS code ![mast][mast]
202 |
203 |
204 | ## Contact
205 |
206 | Author: Raphael Cordeiro
207 |
208 | Follow me on [twitter][rapha-twitter], visit my [Github portfolio][rapha-github], my [Linkedin][rapha-linkedin] or my [personal portfolio][rapha-personal].
209 |
210 |
211 | ## Acknowledgements
212 |
213 | [Microverse][mcvs]
214 |
215 |
216 |
217 |
218 |
219 | [assignment]: https://www.notion.so/Shooter-game-203e819041c7486bb36f9e65faecba27
220 | [live-version]: https://starwars-spaceshooter.herokuapp.com/
221 | [phaser-url]: https://phaser.io/
222 | [sg-tutorial]: https://learn.yorkcs.com/category/tutorials/gamedev/phaser-3/build-a-space-shooter-with-phaser-3/
223 | [LB-API]: https://www.notion.so/Leaderboard-API-service-24c0c3c116974ac49488d4eb0267ade3
224 | [mcvs]: https://www.microverse.org/
225 | [rapha-github]: https://github.com/phalado
226 | [rapha-twitter]: https://twitter.com/phalado
227 | [rapha-linkedin]: https://www.linkedin.com/in/raphael-cordeiro/
228 | [rapha-personal]: https://phalado.github.io/
229 |
230 |
231 | [logo]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/dist/content/gameTitle2.png
232 | [wasd]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/arrow.jpg
233 | [space]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/space-key.png
234 | [X-Wing]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/dist/content/xWing.png
235 | [Tie-Fighter]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/dist/content/tieFighterp.png
236 | [Tie-Adv]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/dist/content/tieAdvanced.png
237 | [Imp-Sh]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/dist/content/imperialShutle.png
238 | [Bomb]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/dist/content/sprEnemy1.png
239 | [HP-Bar]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/dist/content/saberComplete.png
240 | [SC-Intro]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/sceneIntro.png
241 | [SC-MM]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/sceneMM.png
242 | [SC-Game]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/sceneGame.png
243 | [SC-AB]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/sceneAB.png
244 | [SC-GO]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/sceneGO.png
245 | [SC-LB]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/sceneLB.png
246 | [mast]: https://raw.githubusercontent.com/phalado/JS-Capstone/development/Images/masteryBadge.png
247 |
248 |
249 | [video]: https://www.loom.com/share/50085eb2e29b42129edaa0ce6d59d191
250 |
--------------------------------------------------------------------------------
/src/scenes/SceneMain.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 | import Player from '../entities/entityPlayer';
3 | import ScrollingBackground from '../entities/entityScrollingBackground';
4 | import ImperialShutle from '../entities/entityImperialShutle';
5 | import TieFighter from '../entities/entityTieFighter';
6 | import Bomb from '../entities/entityBomb';
7 | import TieAdvanced from '../entities/entityTieAdvanced';
8 |
9 | class SceneMain extends Phaser.Scene {
10 | constructor() {
11 | super({ key: 'SceneMain' });
12 | }
13 |
14 | preload() {
15 | this.load.image('sprBg0', 'content/sprBg0.png');
16 | this.load.image('sprBg1', 'content/sprBg1.png');
17 | this.load.image('deathStar', 'content/deathStar.png');
18 | this.load.spritesheet('sprExplosion', 'content/sprExplosion.png', {
19 | frameWidth: 32,
20 | frameHeight: 32,
21 | });
22 |
23 | this.load.spritesheet('tieFighter', 'content/tieFighterp.png', {
24 | frameWidth: 16,
25 | frameHeight: 16,
26 | });
27 |
28 | this.load.spritesheet('tieAdvanced', 'content/tieAdvanced.png', {
29 | frameWidth: 40,
30 | frameHeight: 35,
31 | });
32 |
33 | this.load.image('bomb', 'content/sprEnemy1.png');
34 |
35 | this.load.spritesheet('imperialShutle', 'content/imperialShutle.png', {
36 | frameWidth: 32,
37 | frameHeight: 26,
38 | });
39 |
40 | this.load.image('sprLaserEnemy0', 'content/sprLaserEnemy0.png');
41 | this.load.image('sprLaserPlayer', 'content/sprLaserPlayer.png');
42 | this.load.spritesheet('sprPlayer', 'content/xWing.png', {
43 | frameWidth: 32,
44 | frameHeight: 37,
45 | });
46 |
47 | this.load.audio('sndExplode0', 'content/sndExplode0.wav');
48 | this.load.audio('sndExplode1', 'content/sndExplode1.wav');
49 | this.load.audio('sndLaser', 'content/blaster-firing.wav');
50 | this.load.audio('battleTheme', 'content/swBattleTheme.mp3');
51 | this.load.audio('r2d2Scream', 'content/r2d2-scream.mp3');
52 | this.load.audio('useForce', 'content/swUseForce.wav');
53 | this.load.audio('vaderBreath', 'content/swVader.wav');
54 |
55 | this.load.image('hp0Of5', 'content/saberEmpty.png');
56 | this.load.image('hp1Of5', 'content/saberOne.png');
57 | this.load.image('hp2Of5', 'content/saberTwo.png');
58 | this.load.image('hp3Of5', 'content/saberThree.png');
59 | this.load.image('hp4Of5', 'content/saberFour.png');
60 | this.load.image('hp5Of5', 'content/saberComplete.png');
61 | }
62 |
63 | create() {
64 | this.anims.create({
65 | key: 'tieFighter',
66 | frames: this.anims.generateFrameNumbers('tieFighter'),
67 | frameRate: 20,
68 | repeat: -1,
69 | });
70 |
71 | this.anims.create({
72 | key: 'tieAdvanced',
73 | frames: this.anims.generateFrameNumbers('tieAdvanced'),
74 | frameRate: 20,
75 | repeat: -1,
76 | });
77 |
78 | this.anims.create({
79 | key: 'imperialShutle',
80 | frames: this.anims.generateFrameNumbers('imperialShutle'),
81 | frameRate: 20,
82 | repeat: -1,
83 | });
84 |
85 | this.anims.create({
86 | key: 'sprExplosion',
87 | frames: this.anims.generateFrameNumbers('sprExplosion'),
88 | frameRate: 20,
89 | repeat: 0,
90 | });
91 |
92 | this.anims.create({
93 | key: 'sprPlayer',
94 | frames: this.anims.generateFrameNumbers('sprPlayer'),
95 | frameRate: 20,
96 | repeat: -1,
97 | });
98 |
99 | this.sfx = {
100 | explosions: [
101 | this.sound.add('sndExplode0', { volume: 0.1 }),
102 | this.sound.add('sndExplode1', { volume: 0.1 }),
103 | ],
104 | laser: this.sound.add('sndLaser', { volume: 0.1 }),
105 | r2d2Scream: this.sound.add('r2d2Scream', { volume: 0.1 }),
106 | useForce: this.sound.add('useForce', { volume: 0.3 }),
107 | vaderBreath: this.sound.add('vaderBreath', { volume: 0.1 }),
108 | };
109 |
110 |
111 | this.song = this.sound.add('battleTheme', { volume: 0.1 });
112 | this.song.play();
113 |
114 | this.backgrounds = [];
115 | for (let i = 0; i < 5; i += 1) {
116 | const bg = new ScrollingBackground(this, 'sprBg0', i * 10);
117 | this.backgrounds.push(bg);
118 | }
119 |
120 | this.deathStar = this.add.image(
121 | this.game.config.width * 0.75,
122 | this.game.config.height * 0.25,
123 | 'deathStar',
124 | );
125 |
126 | this.player = new Player(
127 | this,
128 | this.game.config.width * 0.5,
129 | this.game.config.height * 0.5,
130 | 'sprPlayer',
131 | );
132 |
133 | this.hpBar = [
134 | 'hp0Of5', 'hp1Of5', 'hp2Of5', 'hp3Of5', 'hp4Of5', 'hp5Of5',
135 | ];
136 |
137 | this.sceneScore = this.add.text(
138 | this.game.config.width * 0.025,
139 | this.game.config.height * 0.925,
140 | `Score: ${this.player.getData('score')}`, {
141 | color: '#d0c600',
142 | fontFamily: 'sans-serif',
143 | fontSize: '3vw',
144 | lineHeight: 1.3,
145 | },
146 | );
147 |
148 | this.updateHPBar(this.player);
149 |
150 | this.keyW = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP);
151 | this.keyS = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN);
152 | this.keyA = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT);
153 | this.keyD = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT);
154 | this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
155 |
156 | this.enemies = this.add.group();
157 | this.enemyLasers = this.add.group();
158 | this.playerLasers = this.add.group();
159 |
160 | this.physics.add.collider(this.playerLasers, this.enemies, (playerLaser, enemy) => {
161 | if (enemy && !this.player.getData('isDead')) {
162 | if (enemy.updateHealth()) {
163 | if (enemy.onDestroy !== undefined) {
164 | enemy.onDestroy();
165 | }
166 | this.player.setScore(enemy.getData('score'));
167 | enemy.explode(true);
168 | }
169 | playerLaser.destroy();
170 | }
171 | });
172 |
173 | this.physics.add.collider(this.player, this.enemyLasers, (player, laser) => {
174 | if (!player.getData('isDead')
175 | && !laser.getData('isDead')) {
176 | if (player.updateHealth()) {
177 | player.explode(false);
178 | laser.destroy();
179 | this.song.stop();
180 | player.onDestroy();
181 | } else {
182 | laser.destroy();
183 | this.updateHPBar(this.player);
184 | }
185 | }
186 | });
187 |
188 | this.physics.add.collider(this.player, this.enemies, (player, enemy) => {
189 | if (!player.getData('isDead')
190 | && !enemy.getData('isDead')) {
191 | if (player.updateHealth()) {
192 | player.explode(false);
193 |
194 | if (enemy.onDestroy !== undefined) {
195 | enemy.onDestroy();
196 | }
197 | player.setScore(enemy.getData('score'));
198 | enemy.destroy();
199 |
200 | this.song.stop();
201 | player.onDestroy();
202 | } else {
203 | if (enemy.onDestroy !== undefined) {
204 | player.setScore(enemy.getData('score'));
205 | enemy.onDestroy();
206 | }
207 | enemy.destroy();
208 | this.updateHPBar(this.player);
209 | }
210 | }
211 | });
212 |
213 | this.time.addEvent({
214 | delay: 1000,
215 | callback() {
216 | let enemy = null;
217 |
218 | if (Phaser.Math.Between(0, 10) >= 3) {
219 | enemy = new TieFighter(
220 | this,
221 | Phaser.Math.Between(0, this.game.config.width),
222 | 0,
223 | );
224 | } else if (Phaser.Math.Between(0, 10) >= 5) {
225 | if (this.getEnemiesByType('Bomb').length < 5) {
226 | enemy = new Bomb(
227 | this,
228 | Phaser.Math.Between(0, this.game.config.width),
229 | 0,
230 | );
231 | }
232 | } else {
233 | enemy = new ImperialShutle(
234 | this,
235 | Phaser.Math.Between(0, this.game.config.width),
236 | 0,
237 | );
238 | }
239 |
240 | if (enemy !== null) {
241 | enemy.setScale(Phaser.Math.Between(10, 20) * 0.1);
242 | this.enemies.add(enemy);
243 | }
244 | },
245 |
246 | callbackScope: this,
247 | loop: true,
248 | });
249 |
250 | this.time.addEvent({
251 | delay: 30000,
252 | callback() {
253 | let enemy = null;
254 | enemy = new TieAdvanced(
255 | this,
256 | this.player.x,
257 | 0,
258 | );
259 | if (enemy !== null) {
260 | enemy.setScale(2);
261 | this.sfx.vaderBreath.play();
262 | this.enemies.add(enemy);
263 | }
264 | },
265 | callbackScope: this,
266 | loop: true,
267 | });
268 | }
269 |
270 | update() {
271 | this.player.update();
272 |
273 | this.sceneScore.text = `Score: ${this.player.getData('score')}`;
274 |
275 | if (!this.player.getData('isDead')) {
276 | if (this.keyW.isDown) {
277 | this.player.moveUp();
278 | } else if (this.keyS.isDown) {
279 | this.player.moveDown();
280 | } else if (this.keyA.isDown) {
281 | this.player.moveLeft();
282 | } else if (this.keyD.isDown) {
283 | this.player.moveRight();
284 | }
285 |
286 | if (this.keySpace.isDown) {
287 | this.player.setData('isShooting', true);
288 | } else {
289 | this.player.setData('timerShootTick', this.player.getData('timerShootDelay') - 1);
290 | this.player.setData('isShooting', false);
291 | }
292 | }
293 |
294 | for (let i = 0; i < this.enemies.getChildren().length; i += 1) {
295 | const enemy = this.enemies.getChildren()[i];
296 | enemy.update();
297 |
298 | if (enemy.x < -enemy.displayWidth
299 | || enemy.x > this.game.config.width + enemy.displayWidth
300 | || enemy.y < -enemy.displayHeight * 4
301 | || enemy.y > this.game.config.height + enemy.displayHeight) {
302 | if (enemy) {
303 | if (enemy.onDestroy !== undefined) {
304 | enemy.onDestroy();
305 | }
306 | enemy.destroy();
307 | }
308 | }
309 | }
310 |
311 | for (let i = 0; i < this.enemyLasers.getChildren().length; i += 1) {
312 | const laser = this.enemyLasers.getChildren()[i];
313 | laser.update();
314 | if (laser.x < -laser.displayWidth
315 | || laser.x > this.game.config.width + laser.displayWidth
316 | || laser.y < -laser.displayHeight * 4
317 | || laser.y > this.game.config.height + laser.displayHeight) {
318 | if (laser) {
319 | laser.destroy();
320 | }
321 | }
322 | }
323 |
324 | for (let i = 0; i < this.playerLasers.getChildren().length; i += 1) {
325 | const laser = this.playerLasers.getChildren()[i];
326 | laser.update();
327 | if (laser.x < -laser.displayWidth
328 | || laser.x > this.game.config.width + laser.displayWidth
329 | || laser.y < -laser.displayHeight * 4
330 | || laser.y > this.game.config.height + laser.displayHeight) {
331 | if (laser) {
332 | laser.destroy();
333 | }
334 | }
335 | }
336 |
337 | for (let i = 0; i < this.backgrounds.length; i += 1) {
338 | this.backgrounds[i].update();
339 | }
340 | }
341 |
342 | getEnemiesByType(type) {
343 | const arr = [];
344 | for (let i = 0; i < this.enemies.getChildren().length; i += 1) {
345 | const enemy = this.enemies.getChildren()[i];
346 | if (enemy.getData('type') === type) {
347 | arr.push(enemy);
348 | }
349 | }
350 | return arr;
351 | }
352 |
353 | updateHPBar(player) {
354 | this.sceneHPBar = this.add.image(
355 | this.game.config.width * 0.3,
356 | this.game.config.height * 0.05,
357 | this.hpBar[player.getData('health')],
358 | );
359 | }
360 | }
361 |
362 | export default SceneMain;
--------------------------------------------------------------------------------