├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── bower.json ├── gulpfile.babel.js ├── package.json ├── readme.md ├── src ├── audio │ ├── music │ │ ├── menu.mp3 │ │ └── play.mp3 │ └── sound │ │ ├── bullet-hit.mp3 │ │ ├── enemy-explosion.mp3 │ │ ├── enemy-shot.mp3 │ │ ├── game-over.mp3 │ │ ├── menu-click.mp3 │ │ ├── menu-out.mp3 │ │ ├── menu-over.mp3 │ │ ├── player-explosion.mp3 │ │ └── player-shot.mp3 ├── data │ └── spritesheet │ │ ├── alien.json │ │ ├── button.json │ │ └── smallfighter.json ├── img │ ├── bullet.png │ ├── farback.jpg │ ├── healthbar.png │ ├── hud-bg.png │ ├── loader-bar.png │ ├── loader-bg.png │ ├── particle.gif │ └── spritesheet │ │ ├── alien.png │ │ ├── button.png │ │ └── smallfighter.png ├── index.html └── js │ ├── extensions │ └── textbutton.js │ ├── game.js │ ├── prefabs │ ├── bullet.js │ ├── enemy.js │ ├── hud.js │ └── player.js │ └── states │ ├── boot.js │ ├── index.js │ ├── menu.js │ ├── over.js │ ├── play.js │ └── preload.js └── tasks ├── config.js ├── dev ├── audio.js ├── clean.js ├── data.js ├── html.js ├── img.js ├── js.js ├── serve.js └── watch.js └── prod ├── audio.js ├── clean.js ├── data.js ├── html.js ├── img.js ├── js.js └── rev.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-es5-property-mutators", "transform-object-assign"] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.{json,scss}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "eslint:recommended", 4 | "env": { 5 | "browser": true, 6 | "node": true, 7 | "es6": true 8 | }, 9 | "ecmaFeatures": { 10 | "modules": true 11 | }, 12 | "rules": { 13 | "no-console": 0 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | bower_components 3 | builds 4 | node_modules 5 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phaser-es6-demo", 3 | "description": "", 4 | "authors": [ 5 | "Csaba Tuncsik" 6 | ], 7 | "license": "WTFPL", 8 | "private": true, 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "test", 14 | "tests" 15 | ], 16 | "devDependencies": { 17 | "phaser": "~2.4.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import sequence from 'run-sequence'; 3 | import requireDir from 'require-dir'; 4 | 5 | // Require production tasks 6 | requireDir('./tasks/prod'); 7 | 8 | // Initiate production tasks 9 | gulp.task('build', () => { 10 | sequence('clean:prod', 11 | [ 12 | 'audio:prod', 13 | 'data:prod', 14 | 'img:prod', 15 | 'js:prod' 16 | ], 17 | 'html:prod', 18 | 'rev:prod' 19 | ); 20 | }); 21 | 22 | // Require development tasks 23 | requireDir('./tasks/dev'); 24 | 25 | // Initiate development tasks 26 | gulp.task('default', () => { 27 | sequence('clean:dev', 28 | [ 29 | 'audio:dev', 30 | 'data:dev', 31 | 'img:dev', 32 | 'js:dev', 33 | 'html:dev' 34 | ], 35 | 'watch', 36 | 'serve' 37 | ); 38 | }); 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phaser-es6-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "repository": "cstuncsik/phaser-es6-demo", 7 | "author": "Csaba Tuncsik", 8 | "license": "WTFPL", 9 | "scripts": { 10 | "build": "gulp build", 11 | "dev": "gulp", 12 | "postinstall": "bower install" 13 | }, 14 | "devDependencies": { 15 | "babel-core": "^6.3.21", 16 | "babel-plugin-transform-es5-property-mutators": "^6.3.13", 17 | "babel-plugin-transform-object-assign": "^6.3.13", 18 | "babel-preset-es2015": "^6.3.13", 19 | "babelify": "^7.2.0", 20 | "bower": "^1.7.1", 21 | "browser-sync": "^2.10.1", 22 | "browserify": "^12.0.1", 23 | "del": "^2.2.0", 24 | "gulp": "^3.9.0", 25 | "gulp-htmlmin": "^1.3.0", 26 | "gulp-imagemin": "^2.4.0", 27 | "gulp-json-minify": "^1.0.1", 28 | "gulp-replace": "^0.5.4", 29 | "gulp-rev-all": "^0.8.22", 30 | "gulp-sourcemaps": "^1.6.0", 31 | "gulp-uglify": "^1.5.1", 32 | "gulp-usemin": "^0.3.16", 33 | "gulp-util": "^3.0.7", 34 | "require-dir": "^0.3.0", 35 | "run-sequence": "^1.1.5", 36 | "vinyl-buffer": "^1.0.0", 37 | "vinyl-source-stream": "^1.1.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Phaser ES6 Demo 2 | 3 | This is small shoot'em up demo game to show how you can build a [phaser](http://phaser.io/) game with the next generation of javascript (ES6/2015). 4 | 5 | You can also use this as a boilerplate. 6 | 7 | ## Technology 8 | 9 | The project is based on [Node.js](https://nodejs.org/en/), if you are not familiar with it I'd suggest to check it out first. 10 | If you are familiar just have a look at **package.json** and gulp tasks in **tasks** folder. 11 | If you are familiar and lazy just run: 12 | 13 | ```sh 14 | npm i && npm run dev 15 | ``` 16 | 17 | To build production: 18 | 19 | ```sh 20 | npm run build 21 | ``` 22 | 23 | To try it out now click [here](http://cstuncsik.github.io/phaser-es6-demo) 24 | 25 | ## Structure 26 | 27 | Source files are in **src** folder 28 | 29 | Gulp task are in **tasks** folder as separate files in **dev** and **prod** folders. There are some redundancy but in this way it is very easy to maintain, scale and copy tasks from one project to another. 30 | 31 | Development and production builds are going to **builds** folder. 32 | 33 | ## Features 34 | 35 | In production mode (dev mode is nearly just copying files into builds/dev) 36 | 37 | - optimizing images 38 | - minifying html, js and json 39 | - file revisioning to prevent browser cache 40 | 41 | ## Assets 42 | 43 | Here you can find links about the images and audio I used. 44 | 45 | ### Sprites & Background 46 | 47 | Spaceship and UFO 48 | 49 | [http://millionthvector.blogspot.com.es/p/free-sprites.html](http://millionthvector.blogspot.com.es/p/free-sprites.html) 50 | 51 | Background 52 | 53 | [http://gamedevelopment.tutsplus.com/articles/enjoy-these-totally-free-space-based-shoot-em-up-sprites--gamedev-2368](http://gamedevelopment.tutsplus.com/articles/enjoy-these-totally-free-space-based-shoot-em-up-sprites--gamedev-2368) 54 | 55 | ### Sounds & Music 56 | 57 | Menu 58 | 59 | [http://opengameart.org/content/some-small-themes](http://opengameart.org/content/some-small-themes) 60 | 61 | Play 62 | 63 | [http://opengameart.org/content/railjet-long-seamless-loop](http://opengameart.org/content/railjet-long-seamless-loop) 64 | 65 | Sounds 66 | 67 | [http://opengameart.org/content/collection-gun-sounds](http://opengameart.org/content/collection-gun-sounds) 68 | 69 | [http://opengameart.org/content/space-battle-game-sounds-astromenace](http://opengameart.org/content/space-battle-game-sounds-astromenace) 70 | 71 | [http://opengameart.org/content/sci-fi-sound-effects-library](http://opengameart.org/content/sci-fi-sound-effects-library) 72 | 73 | 74 | ## License 75 | 76 | Copyright © 2016 Csaba Tuncsik 77 | 78 | This work is free. You can redistribute it and/or modify it under the 79 | terms of the Do What The Fuck You Want To Public License, Version 2, 80 | as published by Sam Hocevar. See [WTFPL](http://www.wtfpl.net) ![WTFPL icon](http://i.imgur.com/AsWaQQl.png) for more details. 81 | -------------------------------------------------------------------------------- /src/audio/music/menu.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/music/menu.mp3 -------------------------------------------------------------------------------- /src/audio/music/play.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/music/play.mp3 -------------------------------------------------------------------------------- /src/audio/sound/bullet-hit.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/bullet-hit.mp3 -------------------------------------------------------------------------------- /src/audio/sound/enemy-explosion.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/enemy-explosion.mp3 -------------------------------------------------------------------------------- /src/audio/sound/enemy-shot.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/enemy-shot.mp3 -------------------------------------------------------------------------------- /src/audio/sound/game-over.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/game-over.mp3 -------------------------------------------------------------------------------- /src/audio/sound/menu-click.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/menu-click.mp3 -------------------------------------------------------------------------------- /src/audio/sound/menu-out.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/menu-out.mp3 -------------------------------------------------------------------------------- /src/audio/sound/menu-over.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/menu-over.mp3 -------------------------------------------------------------------------------- /src/audio/sound/player-explosion.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/player-explosion.mp3 -------------------------------------------------------------------------------- /src/audio/sound/player-shot.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/audio/sound/player-shot.mp3 -------------------------------------------------------------------------------- /src/data/spritesheet/alien.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "alien10001.png": 4 | { 5 | "frame": {"x":1,"y":1,"w":64,"h":64}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 9 | "sourceSize": {"w":64,"h":64} 10 | }, 11 | "alien10002.png": 12 | { 13 | "frame": {"x":67,"y":1,"w":64,"h":64}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 17 | "sourceSize": {"w":64,"h":64} 18 | }, 19 | "alien10003.png": 20 | { 21 | "frame": {"x":133,"y":1,"w":64,"h":64}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 25 | "sourceSize": {"w":64,"h":64} 26 | }, 27 | "alien10004.png": 28 | { 29 | "frame": {"x":199,"y":1,"w":64,"h":64}, 30 | "rotated": false, 31 | "trimmed": false, 32 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 33 | "sourceSize": {"w":64,"h":64} 34 | }, 35 | "alien10005.png": 36 | { 37 | "frame": {"x":265,"y":1,"w":64,"h":64}, 38 | "rotated": false, 39 | "trimmed": false, 40 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 41 | "sourceSize": {"w":64,"h":64} 42 | }, 43 | "alien10006.png": 44 | { 45 | "frame": {"x":331,"y":1,"w":64,"h":64}, 46 | "rotated": false, 47 | "trimmed": false, 48 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 49 | "sourceSize": {"w":64,"h":64} 50 | }, 51 | "alien10007.png": 52 | { 53 | "frame": {"x":397,"y":1,"w":64,"h":64}, 54 | "rotated": false, 55 | "trimmed": false, 56 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 57 | "sourceSize": {"w":64,"h":64} 58 | }, 59 | "alien10008.png": 60 | { 61 | "frame": {"x":463,"y":1,"w":64,"h":64}, 62 | "rotated": false, 63 | "trimmed": false, 64 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 65 | "sourceSize": {"w":64,"h":64} 66 | }, 67 | "alien10009.png": 68 | { 69 | "frame": {"x":529,"y":1,"w":64,"h":64}, 70 | "rotated": false, 71 | "trimmed": false, 72 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 73 | "sourceSize": {"w":64,"h":64} 74 | }, 75 | "alien10010.png": 76 | { 77 | "frame": {"x":595,"y":1,"w":64,"h":64}, 78 | "rotated": false, 79 | "trimmed": false, 80 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 81 | "sourceSize": {"w":64,"h":64} 82 | }, 83 | "alien10011.png": 84 | { 85 | "frame": {"x":661,"y":1,"w":64,"h":64}, 86 | "rotated": false, 87 | "trimmed": false, 88 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 89 | "sourceSize": {"w":64,"h":64} 90 | }, 91 | "alien10012.png": 92 | { 93 | "frame": {"x":727,"y":1,"w":64,"h":64}, 94 | "rotated": false, 95 | "trimmed": false, 96 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 97 | "sourceSize": {"w":64,"h":64} 98 | }, 99 | "alien10013.png": 100 | { 101 | "frame": {"x":793,"y":1,"w":64,"h":64}, 102 | "rotated": false, 103 | "trimmed": false, 104 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 105 | "sourceSize": {"w":64,"h":64} 106 | }, 107 | "alien10014.png": 108 | { 109 | "frame": {"x":859,"y":1,"w":64,"h":64}, 110 | "rotated": false, 111 | "trimmed": false, 112 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 113 | "sourceSize": {"w":64,"h":64} 114 | }, 115 | "alien10015.png": 116 | { 117 | "frame": {"x":925,"y":1,"w":64,"h":64}, 118 | "rotated": false, 119 | "trimmed": false, 120 | "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, 121 | "sourceSize": {"w":64,"h":64} 122 | }}, 123 | "meta": { 124 | "app": "http://www.codeandweb.com/texturepacker", 125 | "version": "1.0", 126 | "image": "alien.png", 127 | "format": "RGBA8888", 128 | "size": {"w":990,"h":66}, 129 | "scale": "1", 130 | "smartupdate": "$TexturePacker:SmartUpdate:0ea5b3fe4ecf76b47095eb88140b51ba:babca58183b09cb9f0138be8cbfa238d:f5c2820dc1d6657fd77f91f373566390$" 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/data/spritesheet/button.json: -------------------------------------------------------------------------------- 1 | {"frames": [ 2 | 3 | { 4 | "filename": "button-down.png", 5 | "frame": {"x":1,"y":1,"w":150,"h":30}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":150,"h":30}, 9 | "sourceSize": {"w":150,"h":30} 10 | }, 11 | { 12 | "filename": "button-normal.png", 13 | "frame": {"x":153,"y":1,"w":150,"h":30}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":150,"h":30}, 17 | "sourceSize": {"w":150,"h":30} 18 | }, 19 | { 20 | "filename": "button-over.png", 21 | "frame": {"x":305,"y":1,"w":150,"h":30}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":150,"h":30}, 25 | "sourceSize": {"w":150,"h":30} 26 | }], 27 | "meta": { 28 | "app": "http://www.codeandweb.com/texturepacker", 29 | "version": "1.0", 30 | "image": "button.png", 31 | "format": "RGBA8888", 32 | "size": {"w":456,"h":32}, 33 | "scale": "1", 34 | "smartupdate": "$TexturePacker:SmartUpdate:1315866e2673cb53e91f07e62ed04122:856e293996d7aa38c0fa746b83d0aeb7:c39245c3a59c1712d97d26c862b4cbc5$" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/data/spritesheet/smallfighter.json: -------------------------------------------------------------------------------- 1 | {"frames": [ 2 | 3 | { 4 | "filename": "smallfighter0001.png", 5 | "frame": {"x":1,"y":140,"w":51,"h":137}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":51,"h":137}, 9 | "sourceSize": {"w":51,"h":137} 10 | }, 11 | { 12 | "filename": "smallfighter0006.png", 13 | "frame": {"x":1,"y":1,"w":57,"h":137}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":57,"h":137}, 17 | "sourceSize": {"w":57,"h":137} 18 | }, 19 | { 20 | "filename": "smallfighter0011.png", 21 | "frame": {"x":1,"y":279,"w":51,"h":137}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":51,"h":137}, 25 | "sourceSize": {"w":51,"h":137} 26 | }], 27 | "meta": { 28 | "app": "http://www.codeandweb.com/texturepacker", 29 | "version": "1.0", 30 | "image": "smallfighter.png", 31 | "format": "RGBA8888", 32 | "size": {"w":59,"h":417}, 33 | "scale": "1", 34 | "smartupdate": "$TexturePacker:SmartUpdate:0751b4203909b109edf907157cad9443:62824f174bc469d01fbe635659b1a160:4865d22eb96f798d2494cbc26c0cb598$" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/bullet.png -------------------------------------------------------------------------------- /src/img/farback.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/farback.jpg -------------------------------------------------------------------------------- /src/img/healthbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/healthbar.png -------------------------------------------------------------------------------- /src/img/hud-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/hud-bg.png -------------------------------------------------------------------------------- /src/img/loader-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/loader-bar.png -------------------------------------------------------------------------------- /src/img/loader-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/loader-bg.png -------------------------------------------------------------------------------- /src/img/particle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/particle.gif -------------------------------------------------------------------------------- /src/img/spritesheet/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/spritesheet/alien.png -------------------------------------------------------------------------------- /src/img/spritesheet/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/spritesheet/button.png -------------------------------------------------------------------------------- /src/img/spritesheet/smallfighter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstuncsik/phaser-es6-demo/3229af9519304d036e41cad28dc3a0b507c23f3f/src/img/spritesheet/smallfighter.png -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Phaser game ES6 boilerplate 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/js/extensions/textbutton.js: -------------------------------------------------------------------------------- 1 | export default class TextButton extends Phaser.Button { 2 | 3 | constructor({ game, x, y, asset, callback, callbackContext, overFrame, outFrame, downFrame, upFrame, label, style }) { 4 | super(game, x, y, asset, callback, callbackContext, overFrame, outFrame, downFrame, upFrame); 5 | 6 | this.anchor.setTo(0.5); 7 | 8 | this.label = label; 9 | this.style = style; 10 | this.text = new Phaser.Text(this.game, 0, 0, this.label, this.style); 11 | this.text.anchor.setTo(0.5); 12 | 13 | this.addChild(this.text); 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/js/game.js: -------------------------------------------------------------------------------- 1 | import * as states from './states'; 2 | const GAME = new Phaser.Game(800, 1000, Phaser.AUTO); 3 | 4 | Object.keys(states).forEach(state => GAME.state.add(state, states[state])); 5 | 6 | GAME.state.start('Boot'); 7 | -------------------------------------------------------------------------------- /src/js/prefabs/bullet.js: -------------------------------------------------------------------------------- 1 | export default class Bullet extends Phaser.Sprite { 2 | 3 | constructor({ game, x, y, asset, health, tint = 0xff0000 }) { 4 | super(game, x, y, asset); 5 | 6 | this.anchor.setTo(0.5); 7 | this.scale.setTo(0.8); 8 | this.health = health; 9 | this.tint = tint; 10 | this.checkWorldBounds = true; 11 | this.outOfBoundsKill = true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/js/prefabs/enemy.js: -------------------------------------------------------------------------------- 1 | import Bullet from './bullet'; 2 | 3 | export default class Enemy extends Phaser.Sprite { 4 | 5 | constructor({ game, x, y, asset, frame, health, bulletSpeed }) { 6 | super(game, x, y, asset, frame); 7 | 8 | this.game = game; 9 | 10 | this.anchor.setTo(0.5); 11 | this.scale.setTo(0.8); 12 | this.health = health; 13 | this.maxHealth = health; 14 | this.game.physics.arcade.enable(this); 15 | 16 | this.animations.add('spinning', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 30, true); 17 | this.play('spinning'); 18 | 19 | this.bullets = this.game.add.group(); 20 | this.bullets.enableBody = true; 21 | this.bulletSpeed = bulletSpeed; 22 | 23 | this.shotSound = this.game.add.sound('enemyShot'); 24 | 25 | } 26 | 27 | update() { 28 | 29 | if (this.position.x < 0.04 * this.game.world.width) { 30 | this.position.x = 0.04 * this.game.world.width + 2; 31 | this.body.velocity.x *= -1; 32 | } 33 | else if (this.position.x > 0.96 * this.game.world.width) { 34 | this.position.x = 0.96 * this.game.world.width - 2; 35 | this.body.velocity.x *= -1; 36 | } 37 | 38 | if (this.position.y - this.height / 2 > this.game.world.height) { 39 | this.kill(); 40 | } 41 | } 42 | 43 | shoot() { 44 | 45 | this.shotSound.play("",0,0.5); 46 | 47 | let bullet = this.bullets.getFirstExists(false); 48 | 49 | if (!bullet) { 50 | bullet = new Bullet({ 51 | game: this.game, 52 | x: this.x, 53 | y: this.bottom, 54 | health: 2, 55 | asset: 'bullet', 56 | tint: 0xff0000 57 | }); 58 | this.bullets.add(bullet); 59 | } 60 | else { 61 | bullet.reset(this.x, this.bottom, 2); 62 | } 63 | 64 | bullet.body.velocity.y = this.bulletSpeed; 65 | } 66 | 67 | damage(amount) { 68 | super.damage(amount); 69 | } 70 | 71 | reset({ x, y, health, bulletSpeed, speed }) { 72 | super.reset(x, y, health); 73 | this.bulletSpeed = bulletSpeed; 74 | this.body.velocity.x = speed.x; 75 | this.body.velocity.y = speed.y; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/js/prefabs/hud.js: -------------------------------------------------------------------------------- 1 | export default class Hud extends Phaser.Group { 2 | constructor({ game, player }) { 3 | super(game); 4 | this.game = game; 5 | this.player = player; 6 | this.bg = new Phaser.Image(this.game, 0, 0, 'hudBg'); 7 | this.width = 800; 8 | this.healthbar = new Phaser.Sprite(this.game, 2, 2, 'healthbar'); 9 | this.healthbar.scale.setTo(0.995, 11); 10 | 11 | this.score = 0; 12 | this.scoreLabel = 'Score: '; 13 | this.scoreText = new Phaser.Text(this.game, 20, 14, this.scoreLabel + this.score, { 14 | font: '13px Verdana', 15 | fill: 'white', 16 | align: 'center' 17 | 18 | }); 19 | 20 | this.add(this.bg); 21 | this.add(this.healthbar); 22 | this.add(this.scoreText); 23 | } 24 | 25 | updateHealth() { 26 | this.healthbar.crop(new Phaser.Rectangle(0, 0, (this.player.health / this.player.maxHealth) * this.width, 10)); 27 | this.healthbar.updateCrop(); 28 | } 29 | 30 | updateScore(amount) { 31 | this.score += amount; 32 | this.scoreText.text = this.scoreLabel + (this.score * 10); 33 | } 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /src/js/prefabs/player.js: -------------------------------------------------------------------------------- 1 | import Bullet from './bullet'; 2 | 3 | export default class Player extends Phaser.Sprite { 4 | 5 | constructor({ game, x, y, asset, frame, health }) { 6 | super(game, x, y, asset, frame); 7 | 8 | this.game = game; 9 | 10 | this.anchor.setTo(0.5); 11 | this.scale.setTo(0.8); 12 | 13 | this.health = health; 14 | this.maxHealth = health; 15 | 16 | this.game.physics.arcade.enable(this); 17 | 18 | this.lastPos = {x, y}; 19 | 20 | this.diff = { 21 | x: 0, 22 | y: 0 23 | }; 24 | 25 | this.bullets = this.game.add.group(); 26 | this.bullets.enableBody = true; 27 | this.bulletSpeed = -500; 28 | 29 | this.shotSound = this.game.add.sound('playerShot'); 30 | 31 | this.game.input.onDown.add(() => { 32 | if (this.alive) { 33 | let {x,y} = this.game.input.activePointer.position; 34 | this.diff.x = x - this.position.x; 35 | this.diff.y = y - this.position.y; 36 | } 37 | }); 38 | 39 | this.game.input.onUp.add(() => { 40 | if (this.alive) { 41 | this.frame = 1; 42 | } 43 | }); 44 | 45 | } 46 | 47 | update() { 48 | 49 | //this.game.debug.body(this); 50 | 51 | if (this.game.input.activePointer.isDown) { 52 | 53 | let { x, y } = this.game.input.activePointer.position; 54 | 55 | let left = x < this.lastPos.x; 56 | let right = x > this.lastPos.x; 57 | let diff = Math.abs(x - this.lastPos.x); 58 | 59 | this.position.x = x - this.diff.x; 60 | this.position.y = y - this.diff.y; 61 | 62 | if (this.position.x < 0.02 * this.game.world.width) { 63 | this.position.x = 0.02 * this.game.world.width; 64 | } 65 | 66 | if (this.position.x > 0.98 * this.game.world.width) { 67 | this.position.x = 0.98 * this.game.world.width; 68 | } 69 | 70 | if (this.position.y < 0.09 * this.game.world.height) { 71 | this.position.y = 0.09 * this.game.world.height; 72 | } 73 | 74 | if (this.position.y > 0.94 * this.game.world.height) { 75 | this.position.y = 0.94 * this.game.world.height; 76 | } 77 | 78 | if (diff > 3) { 79 | if (left) { 80 | this.frame = 0; 81 | } else if (right) { 82 | this.frame = 2; 83 | } 84 | } else { 85 | if (this.game.time.elapsedMS >= 16) { 86 | this.frame = 1; 87 | } 88 | } 89 | 90 | 91 | this.lastPos.x = x; 92 | this.lastPos.y = y; 93 | } 94 | } 95 | 96 | shoot() { 97 | 98 | this.shotSound.play("",0,0.5); 99 | 100 | let bullet = this.bullets.getFirstExists(false); 101 | 102 | if (!bullet) { 103 | bullet = new Bullet({ 104 | game: this.game, 105 | x: this.x, 106 | y: this.top, 107 | health: 3, 108 | asset: 'bullet', 109 | tint: 0x04c112 110 | }); 111 | this.bullets.add(bullet); 112 | } 113 | else { 114 | bullet.reset(this.x, this.top, 3); 115 | } 116 | 117 | bullet.body.velocity.y = this.bulletSpeed; 118 | } 119 | 120 | damage(amount) { 121 | super.damage(amount); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/js/states/boot.js: -------------------------------------------------------------------------------- 1 | export default class Boot extends Phaser.State { 2 | 3 | preload() { 4 | this.game.stage.backgroundColor = '#000'; 5 | this.load.image('loaderBg', 'img/loader-bg.png'); 6 | this.load.image('loaderBar', 'img/loader-bar.png'); 7 | } 8 | 9 | create() { 10 | this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; 11 | 12 | this.scale.pageAlignHorizontally = true; 13 | this.scale.pageAlignVertically = true; 14 | 15 | this.game.physics.startSystem(Phaser.Physics.ARCADE); 16 | this.state.start('Preload'); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/js/states/index.js: -------------------------------------------------------------------------------- 1 | export {default as Boot} from './boot'; 2 | export {default as Preload} from './preload'; 3 | export {default as Menu} from './menu'; 4 | export {default as Play} from './play'; 5 | export {default as Over} from './over'; 6 | -------------------------------------------------------------------------------- /src/js/states/menu.js: -------------------------------------------------------------------------------- 1 | import TextButton from '../extensions/textbutton'; 2 | 3 | export default class Menu extends Phaser.State { 4 | 5 | create() { 6 | 7 | this.music = this.game.add.audio('menuMusic'); 8 | 9 | this.title = new Phaser.Text(this.game, this.game.world.centerX, this.game.world.centerY-200, 'Shoot\'Em Up', { 10 | font: '36px Tahoma', 11 | fill: 'white', 12 | align: 'center' 13 | }); 14 | this.title.anchor.setTo(0.5); 15 | 16 | this.start = new TextButton({ 17 | game: this.game, 18 | x: this.game.world.centerX, 19 | y: this.game.world.centerY, 20 | asset: 'button', 21 | overFrame: 2, 22 | outFrame: 1, 23 | downFrame: 0, 24 | upFrame: 1, 25 | label: 'Start', 26 | style: { 27 | font: '16px Verdana', 28 | fill: 'white', 29 | align: 'center' 30 | } 31 | }); 32 | 33 | this.btnOverSound = this.add.sound('menuOver'); 34 | this.btnOutSound = this.add.sound('menuOut'); 35 | this.btnDownSound = this.add.sound('menuDown'); 36 | 37 | this.start.setOverSound(this.btnOverSound); 38 | this.start.setOutSound(this.btnOutSound); 39 | this.start.setDownSound(this.btnDownSound); 40 | 41 | this.start.onInputUp.add(()=>{ 42 | this.music.stop(); 43 | this.state.start('Play'); 44 | 45 | }); 46 | 47 | this.menuPanel = this.add.group(); 48 | this.menuPanel.add(this.title); 49 | this.menuPanel.add(this.start); 50 | 51 | this.music.loopFull(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/js/states/over.js: -------------------------------------------------------------------------------- 1 | import TextButton from '../extensions/textbutton'; 2 | 3 | export default class Over extends Phaser.State { 4 | 5 | create() { 6 | 7 | this.gameOverTitle = new Phaser.Text(this.game, this.game.world.centerX, this.game.world.centerY-200, 'Game over', { 8 | font: '36px Tahoma', 9 | fill: 'white', 10 | align: 'center' 11 | }); 12 | this.gameOverTitle.anchor.setTo(0.5); 13 | 14 | this.start = new TextButton({ 15 | game: this.game, 16 | x: this.game.world.centerX, 17 | y: this.game.world.centerY-30, 18 | asset: 'button', 19 | overFrame: 2, 20 | outFrame: 1, 21 | downFrame: 0, 22 | upFrame: 1, 23 | label: 'Try again', 24 | style: { 25 | font: '16px Verdana', 26 | fill: 'white', 27 | align: 'center' 28 | } 29 | }); 30 | 31 | this.menu = new TextButton({ 32 | game: this.game, 33 | x: this.game.world.centerX, 34 | y: this.game.world.centerY+30, 35 | asset: 'button', 36 | overFrame: 2, 37 | outFrame: 1, 38 | downFrame: 0, 39 | upFrame: 1, 40 | label: 'Menu', 41 | style: { 42 | font: '16px Verdana', 43 | fill: 'white', 44 | align: 'center' 45 | } 46 | }); 47 | 48 | this.btnOverSound = this.add.sound('menuOver'); 49 | this.btnOutSound = this.add.sound('menuOut'); 50 | this.btnDownSound = this.add.sound('menuDown'); 51 | 52 | this.start.setOverSound(this.btnOverSound); 53 | this.start.setOutSound(this.btnOutSound); 54 | this.start.setDownSound(this.btnDownSound); 55 | this.menu.setOverSound(this.btnOverSound); 56 | this.menu.setOutSound(this.btnOutSound); 57 | this.menu.setDownSound(this.btnDownSound); 58 | 59 | this.start.onInputDown.add(()=>{ 60 | this.state.start('Play'); 61 | }); 62 | 63 | this.menu.onInputDown.add(()=>{ 64 | this.state.start('Menu'); 65 | }); 66 | 67 | this.gameOverPanel = this.add.group(); 68 | this.gameOverPanel.add(this.gameOverTitle); 69 | this.gameOverPanel.add(this.start); 70 | this.gameOverPanel.add(this.menu); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/js/states/play.js: -------------------------------------------------------------------------------- 1 | import Player from '../prefabs/player'; 2 | import Enemy from '../prefabs/enemy'; 3 | import HUD from '../prefabs/hud'; 4 | 5 | export default class Play extends Phaser.State { 6 | 7 | create() { 8 | 9 | this.farback = this.add.tileSprite(0, 0, 800, 2380, 'farback'); 10 | 11 | this.game.time.slowMotion = 1; 12 | 13 | this.enemies = this.add.group(); 14 | this.enemies.enableBody = true; 15 | 16 | this.player = new Player({ 17 | game: this.game, 18 | x: this.game.world.centerX, 19 | y: 0.92 * this.game.world.height, 20 | health: 100, 21 | asset: 'smallfighter', 22 | frame: 1 23 | }); 24 | this.game.stage.addChild(this.player); 25 | 26 | this.hud = new HUD({ 27 | game: this.game, 28 | player: this.player 29 | }); 30 | 31 | this.game.input.onDown.add(() => { 32 | this.game.time.slowMotion = 1; 33 | }); 34 | 35 | this.game.input.onUp.add(() => { 36 | this.game.time.slowMotion = 3; 37 | }); 38 | 39 | this.enemyTime = 0; 40 | this.enemyInterval = 1.5; 41 | this.enemyShootTime = 0; 42 | this.enemyShootInterval = 1; 43 | this.playerShootTime = 0; 44 | this.playerShootInterval = 0.16; 45 | 46 | this.game.time.events.loop(Phaser.Timer.SECOND * 10, () => { 47 | if(this.enemyInterval > 0.2 ){ 48 | this.enemyInterval -= 0.1; 49 | } 50 | }); 51 | 52 | this.overlayBitmap = this.add.bitmapData(this.game.width, this.game.height); 53 | this.overlayBitmap.ctx.fillStyle = '#000'; 54 | this.overlayBitmap.ctx.fillRect(0, 0, this.game.width, this.game.height); 55 | 56 | this.overlay = this.add.sprite(0, 0, this.overlayBitmap); 57 | this.overlay.visible = false; 58 | this.overlay.alpha = 0.75; 59 | 60 | this.music = this.game.add.audio('playMusic'); 61 | this.bulletHitSound = this.add.sound('bulletHit'); 62 | this.enemyExplosionSound = this.add.sound('enemyExplosion'); 63 | this.playerExplosionSound = this.add.sound('playerExplosion'); 64 | this.gameOverSound = this.add.sound('gameOver'); 65 | 66 | this.music.loopFull(); 67 | } 68 | 69 | update() { 70 | 71 | this.enemyTime += this.game.time.physicsElapsed; 72 | this.enemyShootTime += this.game.time.physicsElapsed; 73 | this.playerShootTime += this.game.time.physicsElapsed; 74 | 75 | if (this.enemyTime > this.enemyInterval) { 76 | this.enemyTime = 0; 77 | 78 | this.createEnemy({ 79 | game: this.game, 80 | x: this.game.rnd.integerInRange(6, 76) * 10, 81 | y: 0, 82 | speed: { 83 | x: this.game.rnd.integerInRange(5, 10) * 10 * (Math.random() > 0.5 ? 1 : -1), 84 | y: this.game.rnd.integerInRange(5, 10) * 10 85 | }, 86 | health: 9, 87 | bulletSpeed: this.game.rnd.integerInRange(10, 20) * 10, 88 | asset: 'alien' 89 | }); 90 | } 91 | 92 | if (this.enemyShootTime > this.enemyShootInterval) { 93 | this.enemyShootTime = 0; 94 | this.enemies.forEachAlive(enemy => enemy.shoot()); 95 | if (!this.player.alive) { 96 | this.game.world.bringToTop(this.overlay); 97 | } 98 | } 99 | 100 | if (this.playerShootTime > this.playerShootInterval) { 101 | this.playerShootTime = 0; 102 | if (this.player.alive) { 103 | this.player.shoot(); 104 | } 105 | } 106 | 107 | this.game.physics.arcade.overlap(this.player.bullets, this.enemies, this.hitEnemy, null, this); 108 | 109 | this.game.physics.arcade.overlap(this.player, this.enemies, this.crashEnemy, null, this); 110 | 111 | this.enemies.forEach(enemy => this.game.physics.arcade.overlap(this.player, enemy.bullets, this.hitPlayer, null, this)); 112 | 113 | this.farback.tilePosition.y += 3; 114 | } 115 | 116 | createEnemy(data) { 117 | 118 | let enemy = this.enemies.getFirstExists(false); 119 | 120 | if (!enemy) { 121 | enemy = new Enemy(data); 122 | this.enemies.add(enemy); 123 | } 124 | enemy.reset(data); 125 | } 126 | 127 | hitEffect(obj, color) { 128 | let tween = this.game.add.tween(obj); 129 | let emitter = this.game.add.emitter(); 130 | let emitterPhysicsTime = 0; 131 | let particleSpeed = 100; 132 | let maxParticles = 10; 133 | 134 | tween.to({tint: 0xff0000}, 100); 135 | tween.onComplete.add(() => { 136 | obj.tint = 0xffffff; 137 | }); 138 | tween.start(); 139 | 140 | emitter.x = obj.x; 141 | emitter.y = obj.y; 142 | emitter.gravity = 0; 143 | emitter.makeParticles('particle'); 144 | 145 | if (obj.health <= 0) { 146 | particleSpeed = 200; 147 | maxParticles = 40; 148 | color = 0xff0000; 149 | } 150 | 151 | emitter.minParticleSpeed.setTo(-particleSpeed, -particleSpeed); 152 | emitter.maxParticleSpeed.setTo(particleSpeed, particleSpeed); 153 | emitter.start(true, 500, null, maxParticles); 154 | emitter.update = () => { 155 | emitterPhysicsTime += this.game.time.physicsElapsed; 156 | if(emitterPhysicsTime >= 0.6){ 157 | emitterPhysicsTime = 0; 158 | emitter.destroy(); 159 | } 160 | 161 | }; 162 | emitter.forEach(particle => particle.tint = color); 163 | if (!this.player.alive) { 164 | this.game.world.bringToTop(this.overlay); 165 | } 166 | } 167 | 168 | hitEnemy(bullet, enemy) { 169 | this.bulletHitSound.play("",0,0.5); 170 | enemy.damage(bullet.health); 171 | this.hitEffect(enemy, bullet.tint); 172 | if (!enemy.alive) { 173 | this.enemyExplosionSound.play("",0,0.5); 174 | this.hud.updateScore(enemy.maxHealth); 175 | } 176 | bullet.kill(); 177 | } 178 | 179 | hitPlayer(player, bullet) { 180 | this.bulletHitSound.play("",0,0.5); 181 | player.damage(bullet.health); 182 | this.hud.updateHealth(); 183 | this.hitEffect(player, bullet.tint); 184 | if (!player.alive) { 185 | this.playerExplosionSound.play(); 186 | this.gameOver(); 187 | } 188 | bullet.kill(); 189 | } 190 | 191 | crashEnemy(player, enemy) { 192 | enemy.damage(enemy.health); 193 | player.damage(enemy.health); 194 | this.hitEffect(player); 195 | this.hitEffect(enemy); 196 | if (!enemy.alive) { 197 | this.enemyExplosionSound.play("",0,0.5); 198 | this.hud.updateScore(enemy.maxHealth); 199 | } 200 | this.hud.updateHealth(); 201 | if (!player.alive) { 202 | this.playerExplosionSound.play(); 203 | this.gameOver(); 204 | } 205 | } 206 | 207 | gameOver(){ 208 | this.game.time.slowMotion = 3; 209 | this.overlay.visible = true; 210 | this.game.world.bringToTop(this.overlay); 211 | let timer = this.game.time.create(this.game, true); 212 | timer.add(3000, () => { 213 | this.music.stop(); 214 | this.gameOverSound.play(); 215 | this.game.state.start('Over'); 216 | }); 217 | timer.start(); 218 | } 219 | 220 | } 221 | -------------------------------------------------------------------------------- /src/js/states/preload.js: -------------------------------------------------------------------------------- 1 | export default class Preload extends Phaser.State { 2 | 3 | preload() { 4 | 5 | this.loaderBg = this.add.sprite(this.game.world.centerX, this.game.world.centerY, 'loaderBg'); 6 | this.loaderBar = this.add.sprite(this.game.world.centerX, this.game.world.centerY, 'loaderBar'); 7 | this.loaderBg.anchor.setTo(0.5); 8 | this.loaderBar.anchor.setTo(0.5); 9 | 10 | this.load.setPreloadSprite(this.loaderBar); 11 | 12 | this.load.atlasJSONArray('smallfighter', 'img/spritesheet/smallfighter.png', 'data/spritesheet/smallfighter.json'); 13 | this.load.atlasJSONArray('alien', 'img/spritesheet/alien.png', 'data/spritesheet/alien.json'); 14 | this.load.atlasJSONArray('button', 'img/spritesheet/button.png', 'data/spritesheet/button.json'); 15 | this.load.image('farback', 'img/farback.jpg'); 16 | this.load.image('bullet', 'img/bullet.png'); 17 | this.load.image('particle', 'img/particle.gif'); 18 | this.load.image('healthbar', 'img/healthbar.png'); 19 | this.load.image('hudBg', 'img/hud-bg.png'); 20 | 21 | this.load.audio('playMusic', ['audio/music/play.mp3']); 22 | this.load.audio('menuMusic', ['audio/music/menu.mp3']); 23 | 24 | this.load.audio('menuOver', ['audio/sound/menu-over.mp3']); 25 | this.load.audio('menuOut', ['audio/sound/menu-out.mp3']); 26 | this.load.audio('menuDown', ['audio/sound/menu-click.mp3']); 27 | 28 | this.load.audio('bulletHit', ['audio/sound/bullet-hit.mp3']); 29 | this.load.audio('enemyShot', ['audio/sound/enemy-shot.mp3']); 30 | this.load.audio('enemyExplosion', ['audio/sound/enemy-explosion.mp3']); 31 | this.load.audio('playerShot', ['audio/sound/player-shot.mp3']); 32 | this.load.audio('playerExplosion', ['audio/sound/player-explosion.mp3']); 33 | 34 | this.load.audio('gameOver', ['audio/sound/game-over.mp3']); 35 | 36 | } 37 | 38 | create() { 39 | this.state.start('Menu'); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /tasks/config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | export default (() => { 4 | 5 | let cfg = { 6 | paths: { 7 | src: { 8 | root: path.join('src', path.sep) 9 | }, 10 | builds: { 11 | root: path.join('builds', path.sep) 12 | } 13 | } 14 | }; 15 | 16 | let globs = { 17 | js: path.join(cfg.paths.src.root, 'js', path.sep), 18 | img: path.join(cfg.paths.src.root, 'img', '**', '*'), 19 | data: path.join(cfg.paths.src.root, 'data', '**', '*.json'), 20 | audio: path.join(cfg.paths.src.root, 'audio', '**', '*') 21 | }; 22 | 23 | Object.keys(globs).forEach(type => cfg.paths.src[type] = globs[type]); 24 | 25 | ['dev', 'prod', 'tmp'].forEach(build => { 26 | let builds = cfg.paths.builds; 27 | builds[build] = {}; 28 | builds[build].root = path.join(builds.root, build, path.sep); 29 | }); 30 | 31 | Object.keys(cfg.paths.builds).forEach(buildType => { 32 | let buildSubDir = cfg.paths.builds[buildType]; 33 | if (typeof buildSubDir !== 'string') { 34 | Object.keys(globs).forEach(type => buildSubDir[type] = path.join(buildSubDir.root, type, path.sep)); 35 | } 36 | }); 37 | 38 | return cfg; 39 | 40 | })(); 41 | -------------------------------------------------------------------------------- /tasks/dev/audio.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | 5 | gulp.task('audio:dev', () => { 6 | return gulp.src(config.paths.src.audio) 7 | .pipe(gulp.dest(config.paths.builds.dev.audio)) 8 | .on('error', util.log); 9 | }); 10 | -------------------------------------------------------------------------------- /tasks/dev/clean.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import del from 'del'; 4 | 5 | gulp.task('clean:dev', () => { 6 | return del.sync(config.paths.builds.dev.root); 7 | }); 8 | -------------------------------------------------------------------------------- /tasks/dev/data.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | 5 | gulp.task('data:dev', () => { 6 | return gulp.src(config.paths.src.data) 7 | .pipe(gulp.dest(config.paths.builds.dev.data)) 8 | .on('error', util.log); 9 | }); 10 | -------------------------------------------------------------------------------- /tasks/dev/html.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | 5 | gulp.task('html:dev', () => { 6 | return gulp.src(config.paths.src.root + 'index.html') 7 | .pipe(gulp.dest(config.paths.builds.dev.root)) 8 | .on('error', util.log); 9 | }); 10 | -------------------------------------------------------------------------------- /tasks/dev/img.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | 5 | gulp.task('img:dev', () => { 6 | return gulp.src(config.paths.src.img) 7 | .pipe(gulp.dest(config.paths.builds.dev.img)) 8 | .on('error', util.log); 9 | }); 10 | -------------------------------------------------------------------------------- /tasks/dev/js.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | import sourcemaps from 'gulp-sourcemaps'; 5 | import source from 'vinyl-source-stream'; 6 | import buffer from 'vinyl-buffer'; 7 | import browserify from 'browserify'; 8 | 9 | gulp.task('js:dev', () => { 10 | return browserify(config.paths.src.js + 'game.js', {debug: true}) 11 | .transform('babelify', { 12 | presets: ['es2015'], 13 | plugins: ['transform-es5-property-mutators', 'transform-object-assign'] 14 | }) 15 | .bundle() 16 | .on('error', function (error) { 17 | util.log(util.colors.red(error)); 18 | this.emit('end'); 19 | }) 20 | .pipe(source('game.js')) 21 | .pipe(buffer()) 22 | .pipe(sourcemaps.init({loadMaps: true})) 23 | .pipe(sourcemaps.write()) 24 | .pipe(gulp.dest(config.paths.builds.dev.js)) 25 | }); 26 | -------------------------------------------------------------------------------- /tasks/dev/serve.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import browserSync from 'browser-sync'; 3 | import gulp from 'gulp'; 4 | 5 | gulp.task('serve', () => { 6 | browserSync({ 7 | server: { 8 | baseDir: config.paths.builds.dev.root, 9 | routes: { 10 | "/bower_components": "bower_components" 11 | } 12 | } 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /tasks/dev/watch.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | 4 | gulp.task('watch', () => { 5 | gulp.watch(config.paths.src.js + '**/*.js', ['js:dev']); 6 | gulp.watch(config.paths.src.img, ['img:dev']); 7 | gulp.watch(config.paths.src.data, ['data:dev']); 8 | gulp.watch(config.paths.src.audio, ['audio:dev']); 9 | gulp.watch(config.paths.src.root + '**/*.html', ['html:dev']); 10 | }); 11 | -------------------------------------------------------------------------------- /tasks/prod/audio.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | 5 | gulp.task('audio:prod', () => { 6 | return gulp.src(config.paths.src.audio) 7 | .pipe(gulp.dest(config.paths.builds.tmp.audio)) 8 | .on('error', util.log); 9 | }); 10 | -------------------------------------------------------------------------------- /tasks/prod/clean.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import del from 'del'; 4 | 5 | gulp.task('clean:prod', () => { 6 | return del.sync([config.paths.builds.tmp.root, config.paths.builds.prod.root]); 7 | }); 8 | -------------------------------------------------------------------------------- /tasks/prod/data.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | import jsonMinify from 'gulp-json-minify'; 5 | import replace from 'gulp-replace'; 6 | 7 | gulp.task('data:prod', () => { 8 | return gulp.src(config.paths.src.data) 9 | .pipe(jsonMinify()) 10 | .pipe(replace(/,"meta":\{.*}$/, '}')) 11 | .pipe(gulp.dest(config.paths.builds.tmp.data)) 12 | .on('error', util.log); 13 | }); 14 | -------------------------------------------------------------------------------- /tasks/prod/html.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | import usemin from 'gulp-usemin'; 5 | import uglify from 'gulp-uglify'; 6 | import htmlmin from 'gulp-htmlmin'; 7 | 8 | gulp.task('html:prod', () => { 9 | return gulp.src(config.paths.src.root + 'index.html') 10 | .pipe(gulp.dest(config.paths.builds.tmp.root)) 11 | .pipe(usemin({ 12 | js: [uglify()], 13 | lib: [uglify()], 14 | html: [htmlmin({ 15 | removeComments: true, 16 | collapseWhitespace: true 17 | })] 18 | })) 19 | .pipe(gulp.dest(config.paths.builds.tmp.root)) 20 | .on('error', util.log); 21 | }); 22 | -------------------------------------------------------------------------------- /tasks/prod/img.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | import imagemin from 'gulp-imagemin'; 5 | 6 | gulp.task('img:prod', () => { 7 | return gulp.src(config.paths.src.img) 8 | .pipe(imagemin()) 9 | .pipe(gulp.dest(config.paths.builds.tmp.img)) 10 | .on('error', util.log); 11 | }); 12 | -------------------------------------------------------------------------------- /tasks/prod/js.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import util from 'gulp-util'; 4 | import source from 'vinyl-source-stream'; 5 | import buffer from 'vinyl-buffer'; 6 | import browserify from 'browserify'; 7 | 8 | gulp.task('js:prod', () => { 9 | return browserify(config.paths.src.js + 'game.js', {debug: true}) 10 | .transform('babelify', { 11 | presets: ['es2015'], 12 | plugins: ['transform-es5-property-mutators', 'transform-object-assign'] 13 | }) 14 | .bundle() 15 | .on('error', util.log) 16 | .pipe(source('game.js')) 17 | .pipe(buffer()) 18 | .pipe(gulp.dest(config.paths.builds.tmp.js)) 19 | }); 20 | -------------------------------------------------------------------------------- /tasks/prod/rev.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js'; 2 | import gulp from 'gulp'; 3 | import RevAll from 'gulp-rev-all'; 4 | import util from 'gulp-util'; 5 | 6 | gulp.task('rev:prod', () => { 7 | 8 | let revAll = new RevAll({ dontRenameFile: [/^\/favicon.ico$/g, /^\/index.html/g] }); 9 | 10 | return gulp.src(config.paths.builds.tmp.root + '**') 11 | .pipe(gulp.dest(config.paths.builds.tmp.root)) 12 | .pipe(revAll.revision()) 13 | .pipe(gulp.dest(config.paths.builds.prod.root)) 14 | .on('error', util.log); 15 | }); 16 | --------------------------------------------------------------------------------