├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .npmrc ├── .vscode ├── launch.json └── tasks.json ├── LICENSE.md ├── README.md ├── README_HEADER.png ├── america-first-icon.png ├── assets ├── atlases │ ├── preload_sprites_array.json │ ├── preload_sprites_array.png │ ├── preload_sprites_hash.json │ ├── preload_sprites_hash.png │ ├── preload_sprites_xml.png │ └── preload_sprites_xml.xml ├── audio │ ├── music.ac3 │ ├── music.m4a │ ├── music.mp3 │ └── music.ogg ├── audiosprites │ ├── sfx.ac3 │ ├── sfx.json │ ├── sfx.m4a │ ├── sfx.mp3 │ └── sfx.ogg ├── fonts │ ├── 2Dumb-webfont.css │ ├── 2Dumb-webfont.eot │ ├── 2Dumb-webfont.svg │ ├── 2Dumb-webfont.ttf │ ├── 2Dumb-webfont.woff │ ├── font_fnt.fnt │ ├── font_fnt.png │ ├── font_xml.png │ └── font_xml.xml ├── images │ └── background_template.png ├── scripts │ ├── BlurX.js │ └── BlurY.js ├── shaders │ └── pixelate.frag ├── spritesheets │ └── metalslug_mummy.[37,45,18,0,0].png └── tilemaps │ └── tilemap.json ├── assets_raw └── images │ ├── preload_bar.png │ └── preload_frame.png ├── coffee-conundrum-icon.png ├── dora-great-run-icon.jpg ├── electron-main.js ├── p0ng-icon.png ├── package-lock.json ├── package.json ├── r0d3nt-icon.png ├── scripts ├── generateAssetsClass.js ├── packageElectronApp.js └── setupGameSize.js ├── src ├── app.ts ├── assets.ts ├── globals.d.ts ├── states │ ├── boot.ts │ ├── preloader.ts │ └── title.ts └── utils │ ├── assetUtils.ts │ └── utils.ts ├── take-back-the-colors.png ├── templates └── index.ejs ├── tsconfig.json ├── tslint.json ├── webpack.dev.config.js └── webpack.dist.config.js /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[REPORT]" 5 | labels: '' 6 | assignees: rroylance 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[REQUEST]" 5 | labels: '' 6 | assignees: rroylance 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ 2 | .idea 3 | 4 | # Node.js 5 | node_modules 6 | 7 | # Game 8 | dist 9 | builds 10 | 11 | # OSX 12 | .DS_Store 13 | 14 | # Visual Studio 15 | .vs -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | loglevel=silent 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Run Server DEV", 11 | "port": 9222, 12 | "url": "http://localhost:9000/", 13 | "webRoot": "${workspaceRoot}", 14 | "sourceMaps": true, 15 | "timeout": 15000, 16 | "trace": "verbose", 17 | "preLaunchTask": "run_server_dev", 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "server:dev", 9 | "problemMatcher": [], 10 | "label": "(Start) Webpack Dev Server", 11 | "identifier": "run_server_dev", 12 | "promptOnClose": true 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phaser-CE NPM Webpack TypeScript Starter Project (catchy name, isn't it?) 2 | 3 | [![GitHub package version](https://img.shields.io/github/package-json/v/rroylance/phaser-ce-npm-webpack-typescript-starter-project.svg?style=for-the-badge)](https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/releases) 4 | [![GitHub last commit](https://img.shields.io/github/last-commit/rroylance/phaser-ce-npm-webpack-typescript-starter-project.svg?style=for-the-badge)](https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/commits/master) 5 | 6 | [![GitHub watchers](https://img.shields.io/github/watchers/rroylance/phaser-ce-npm-webpack-typescript-starter-project.svg?style=for-the-badge)](https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/watchers) 7 | [![GitHub stars](https://img.shields.io/github/stars/rroylance/phaser-ce-npm-webpack-typescript-starter-project.svg?style=for-the-badge)](https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/stargazers) 8 | [![GitHub forks](https://img.shields.io/github/forks/rroylance/phaser-ce-npm-webpack-typescript-starter-project.svg?style=for-the-badge)](https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/network) 9 | 10 | [![GitHub issues](https://img.shields.io/github/issues/rroylance/phaser-ce-npm-webpack-typescript-starter-project.svg?style=for-the-badge)](https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/issues) 11 | [![GitHub pull requests](https://img.shields.io/github/issues-pr/rroylance/phaser-ce-npm-webpack-typescript-starter-project.svg?style=for-the-badge)](https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/pulls) 12 | 13 | [![David](http://img.shields.io/david/rroylance/phaser-ce-npm-webpack-typescript-starter-project.svg?style=for-the-badge)](https://david-dm.org/rroylance/phaser-ce-npm-webpack-typescript-starter-project) 14 | 15 | ![PhaserNPMWebpackTypeScriptStarterProject](https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/master/README_HEADER.png) 16 | 17 | ##### Hit the ground running and make some great games! 18 | 19 | ###### If you use this template/starter project in any capacity; I'd love to hear about your experience with it. Whether you continued with it or decided not to (I really want to hear why you made your decision). 20 | 21 | # Features: 22 | 23 | - Phaser-CE 2.11.1 (npm module, no having to download the library separately...) 24 | - TypeScript + TSLint 25 | - 3 States (Boot, Preloader, Title) showing transition between states and where some things should be done and how a TypeScript state looks 26 | - Google Web Font loader 27 | - Webpack 28 | - Separate Development and Distribution builds 29 | - Live server (builds and reloads the browser on changes) 30 | - No hassle asset management requiring no code, on your part, to load and parse assets 31 | - Assets are required and hashed via webpack, you can now guarantee that when you push an update, everyone will get the new files and not cached ones 32 | - Assets class created automatically allowing you to access all the assets and their frames and sprites (in the case of Atlases and Audiosprites) in a compiler validating way! 33 | - Setting up the game size and scaling through a script that does it all for you 34 | - Automatic template background 35 | - Sets up the size the game so that it is scaled only when absolutely necessary 36 | - Refer to src/utils/utils.ts for an explanation on the background_template and the sizing/scaling style 37 | - Landscape and Portrait support 38 | 39 | ### TODO (in no particular order): 40 | 41 | - Clean up generateAssetsClass.js 42 | - Get Custom/Local Web Fonts hashed by Webpack (to avoid cache issues) 43 | - If anyone has experience webpacking font-face in css style web fonts and loading said fonts via webfontloader, let me know as I was having some trouble getting the font-face src to use the hashed assets. 44 | - Multiple resolution asset loader (@2x, @3x, etc...) 45 | - Yeoman Generator 46 | - Optional Analytics integration 47 | - Optional Cordova integration for iOS and Android builds 48 | 49 | ### Folder Structure: 50 | - **assets/** – This is where your assets that are processed when building goes 51 | - **assets_raw/** – This folder is NOT processed at all and is merely an organizational folder (I use it for things like my individual images that get compiled into a spritesheet, individual sounds that get compiled into an audiosprite, etc...) 52 | - **dist/** – This is where the built game will go 53 | - **node_modules/** – This is where the node modules required for the game will be put with npm install 54 | - **scripts/** – This is where node scripts go 55 | - **src/** – This is where all the games code goes 56 | - **templates/** – This is where the html template that gets built by Webpack goes 57 | - **.gitignore** – List of files and folders that are ignored by git 58 | - **.npmrc** – List of some project wide npm settings 59 | - **electron-main.js** – Entry point and application life controller for electron builds 60 | - **package.json** – Node config for the project 61 | - **README.md** – This is the README displayed ont he GitHub page 62 | - **README_HEADER.png** – This is just the header image for the GitHub README 63 | - **tsconfig.json** – List of some TypeScript settings 64 | - **tslint.json** – List of some TypeScript Linting rules 65 | - **webpack.dev.config.js** – Webpack config for the DEV build 66 | - **webpack.dist.config.js** – Webpack config for the DIST build 67 | 68 | # Setup: 69 | To use this you’ll need to install a few things before you have a working copy of the project. But once you have node.js installed it only takes a few seconds and a couple commands to get going. 70 | 71 | ## 0. Install Git: 72 | 73 | [GIT Installation Instructions and Links][git-scm] 74 | 75 | ## 1. Download or Clone this repo: 76 | 77 | #### Download: 78 | 79 | Download the latest zip/tar.gz from [GitHub Releases][releases], extract it to where you want your project to be. 80 | 81 | ##### If you want a clean project ready for you to just start your own game and not have to worry about cleaning up the examples and samples from the v1.11.0 release, just download the v1.11.0-fresh release instead. 82 | 83 | #### Clone (must've completed Step 0 first): 84 | 85 | Navigate into your workspace directory. 86 | 87 | Run: 88 | 89 | ```git clone https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project.git``` 90 | 91 | ##### If you want a clean project ready for you to just start your own game and not have to worry about cleaning up the examples and samples from the master branch, just pull the 'fresh-start' branch instead of master. 92 | 93 | ## 2. Install node.js and npm (npm is included and installed with node.js): 94 | 95 | [NodeJS Installation Instructions and Links][nodejs] 96 | 97 | ## 3. Install dependencies: 98 | 99 | Navigate to the cloned repo’s directory. 100 | 101 | Run: 102 | 103 | ```npm install``` 104 | 105 | ## 4. Run the dev server: 106 | 107 | Run to use the dev build while developing: 108 | 109 | ```npm run server:dev``` 110 | 111 | Run to use the dist build while developing 112 | 113 | ```npm run server:dist``` 114 | 115 | ###### The only real reason I can think of to use the dist server is if the minification process is breaking something in your game and you need to test using the minified version, or something you excluded with the DEBUG flag shouldn't have been excluded. 116 | 117 | This will run a server that serves your built game straight to the browser and will be built and reloaded automatically anytime a change is detected. 118 | 119 | ## Build for testing/developing/debugging: 120 | 121 | Run: 122 | 123 | ```npm run build:dev``` 124 | 125 | This will build the game with a few caveats; 126 | - A compile time flag, DEBUG, set to true; allowing you to include or not include certain code depending on if it's DEBUG build or not. 127 | - The resulting game.js will not be minified 128 | 129 | ## Build for release: 130 | 131 | Run: 132 | 133 | ```npm run build:dist``` 134 | 135 | This will build the game with a few caveats; 136 | - The compile time flag, DEBUG, set to false; allowing you to include or not include certain code depending on if it's DEBUG build or not. 137 | - The resulting game.min.js will be minified 138 | 139 | ## Generate Assets Class: 140 | 141 | This project will manage your assets for you! All you need to do is drop your assets in assets/ (subdirectories do not matter) and run (you need to run this manually if you change assets while the server is running, otherwise it's run automatically when running a build); 142 | 143 | ```npm run assets``` 144 | 145 | or (if your dev GOOGLE_WEB_FONTS is different from your dist); 146 | 147 | ```npm run assets:dev``` 148 | 149 | src/assets.ts will be generated which contains sections for all your asset types (the generator is smart enough to distinguish what assets are what !) and classes for every asset, it will also generate an enum containing every frame and sprite in Atlases and AudioSprites respectively! 150 | 151 | No need to remember keys, frames, or sprites anymore; which means no more typos resulting in asset not found errors. Just use, for example, Assets.Images.ImagesBackgroundTemplate.getName() or Assets.Audiosprites.AudiospritesSfx.Sprites.Laser1. This also allows the compiler to warn you if you are trying to use an asset that doesn't exist! 152 | 153 | The preloader will use this class to load everything, **you don't have to do anything in code to get your assets loading and available (except for any assets you need for your loading screen...)**! 154 | 155 | Currently supports the following (if you need a new extension or find an issue with one of your assets not exporting correctly, just let me know); 156 | 157 | - Images: 158 | - bmp, gif, jpg, jpeg, png, webp 159 | - Spritesheets: 160 | - bmp, gif, jpg, jpeg, png, webp 161 | - \[frameWidth, frameHeight, frameMax, margin, spacing\] - frameWidth & frameHeight are required. 162 | - Example: spritesheet.\[100, 100\].png 163 | - Atlases: 164 | - bmp, gif, jpg, jpeg, png, webp 165 | - json (the loader figures out if it's a JSONArray or JSONHash, no need to remember or care), xml 166 | - Audio: 167 | - aac, ac3, caf, flac, m4a, mp3, mp4, ogg, wav, webm 168 | - Audiosprites: 169 | - aac, ac3, caf, flac, m4a, mp3, mp4, ogg, wav, webm 170 | - json 171 | - Local Fonts: 172 | - eot, otf, svg, ttf, woff, woff2 173 | - css 174 | - Bitmap Font: 175 | - bmp, gif, jpg, jpeg, png, webp 176 | - xml, fnt 177 | - JSON: 178 | - json 179 | - Tilemap JSON: 180 | - json 181 | - XML: 182 | - xml 183 | - Text: 184 | - txt 185 | - Scripts: 186 | - js 187 | - Shaders: 188 | - frag 189 | 190 | Which version of the audio to load is defined in the webpack.dev.config.js and webpack.dist.config.js under the DefinePlugin 'SOUND_EXTENSIONS_PREFERENCE' section; 191 | - Currently I set the order to: webm, ogg, m4a, mp3, aac, ac3, caf, flac, mp4, wav 192 | - The loader will load the audio using this as the preference; the first supported file that is found is used using the order of this list as most preferred to least preferred 193 | 194 | ## Change the game size and generate a template background: 195 | 196 | Note: This is automatically run after running npm install, however you may want to run it again (if you deleted the background.png and want it back, or if you want to change the game size from the default). 197 | 198 | Run: 199 | 200 | ```npm run setupGameSize``` 201 | 202 | This will run a script that will generate a template background showing the safe and decoration area of your game when it is sized or scaled for different devices as well as updating a couple global values in the webpack configs so that the game knows about the new size when built. 203 | 204 | If you do not want the default 800 x 500 with this scaling style, run the following and all will be updated. 205 | 206 | **DO NOT MODIFY THE (DEFAULT or MAX)\_GAME\_(WIDTH or HEIGHT) OR SCALE_MODE PLUGINS DEFINED IN THE WEBPACK CONFIGS, OR THIS WILL NOT WORK**; 207 | 208 | Run the following for descriptions and default values for all possible options; 209 | 210 | ```npm run setupGameSize -- -h``` 211 | 212 | Run the following specifying some or all of the options; 213 | 214 | ```npm run setupGameSize -- --width [whatever width you want] --height [whatever height you want] --aspect-ratio [If you want a different default aspect ratio] --scale-mode [one of the Phaser Scale Modes] [--no-png]``` 215 | 216 | **The '--' after setupGameSize is not a mistake; it is required to pass arguments along to the script.** 217 | 218 | You can either provide the width **and** height (defaults 800 and 500 respectively) and as long as they result in an aspect ratio of what's set in the script or by --aspect-ratio, or you can provide the width **or** height and the one you didn't provide will be calculated for you using the aspect ratio of what's set in the script or by --aspect-ratio. 219 | 220 | Provide --aspect-ratio to change the desired aspect ratio (default 1.6 or 16:10). The script checks to make sure the width and height match the aspect ratio so you get warned early about an incorrect dimension (maximum precision of 3 decimal places). 221 | 222 | Providing --scale-mode will set this.game.scale.scaleMode to the corresponding Phaser.ScaleManager.SCALE_MODE (default USER_SCALE). 223 | 224 | If you do not want the background to be created just add the flag --no-png (not putting this will let the background generate). 225 | 226 | ## Google Web Fonts: 227 | 228 | Add your desired Google Web Fonts to the webpack.dev.config.js and/or webpack.dist.config.js in the DefinePlugin 'GOOGLE_WEB_FONTS' section and they will then be loaded and available via Assets.GoogleWebFonts. 229 | 230 | ## Custom/Local Web Fonts: 231 | 232 | Add your desired Custom/Local Web Fonts to your assets folder and they will then be loaded and available via Assets.CustomWebFonts 233 | - The various font files, and the css MUST share the same name 234 | - One CSS file per font 235 | 236 | I recommend one of the following generators for generating your font files; 237 | - [Font Squirrel Webfont Generator][fontsquirrel] 238 | - [Everything Fonts font-face generator][everythingfonts] 239 | 240 | ## Plugin management: 241 | 242 | Drop the .js file (or .min.js) of the plugin in the assets/script folder. 243 | Your script(s) will be loaded in the Preloader state with the `AssetUtils.Loader.loadAllAssets` call. 244 | If you want the typescript support, you need to drop the `.d.ts` file somewhere (not in assets) and add it to the `files` array in `tsconfig.json`. 245 | 246 | Here is an example of how to include your plugin in a state: 247 | ``` 248 | export default class MyState extends Phaser.State { 249 | 250 | myPlugin: Phaser.Plugin.MyPlugin; 251 | 252 | public preload(): void { 253 | this.myPlugin = new Phaser.Plugin.MyPlugin(this.game); 254 | this.game.plugins.add(this.myPlugin as any); 255 | } 256 | } 257 | 258 | ``` 259 | 260 | 261 | 262 | ## Desktop Build via Electron 263 | 264 | **Note that I am not, currently, actively using this. So if you do, I'd appreciate if you could pass any changes you make or anything you need out of it. Although I'm not using it, I will still support it as best I can.** 265 | 266 | You can test your game via Electron by running; 267 | 268 | ```npm run electron:dev``` 269 | 270 | or 271 | 272 | ```npm run electron:dist``` 273 | 274 | To build the dev or dist version of your game, respectively, and then open up your game in an Electron instance. 275 | 276 | ## Package Desktop App via Electron 277 | 278 | **Note that I am not, currently, actively using this. So if you do, I'd appreciate if you could pass any changes you make or anything you need out of it. Although I'm not using it, I will still support it as best I can.** 279 | 280 | You can package your game for Windows (win32 ia32/x64), OSX (darwin ia32/x64), Mac App Store (mas ia32/x64), Linux (linux ia32/x64/armv7l) using the following script; 281 | 282 | ```npm run electron:pack:dev``` 283 | 284 | or 285 | 286 | ```npm run electron:pack:dist``` 287 | 288 | To package the dev or dist version of your game, respectively, for the current platform you are on. You can provide many options to build specific platforms, for specific architectures, provide an icon, etc. 289 | 290 | Refer to the [API Documentation][electron-pack-api] for a full list and details; I'm using it kind of oddly in that I'm using the API from the command line and not the command line version... to provide options appaend ' -- ' to the npm run command and then also append '--' to the option name and then either put the value after a space or '=', either way. Examples; 291 | 292 | ```npm run electron:pack:dist -- --platform win32 --arch=ia32 //32 bit Windows exe``` 293 | 294 | ```npm run electron:pack:dist -- --platform win32,darwin --arch=ia32,x64 //32 and 64 bit Windows exe and OSX app``` 295 | 296 | All builds will be in the builds/ folder in appropriately named folders. 297 | 298 | ###### Note that building for Windows from a non windows device requires a little bit of extra setup; refer to [Building Windows apps from non-Windows platforms][electron-pack-windows]. 299 | 300 | ###### Also note that for OSX / MAS target bundles: the .app bundle can only be signed when building on a host OSX platform. 301 | 302 | ## Bugs/Issues? 303 | 304 | If you have any issues please let me know via [GitHub Issues][issues]! 305 | 306 | ## Requests/Suggestions? 307 | 308 | If you have any requests or suggestion for the project please let me know via [GitHub Issues][issues]! 309 | 310 | ## Contributing Code? 311 | 312 | If you would like to have some of your code included; whether a new feature, a cleaned up feature, a bugfix, or whatever. Please open up a [Pull Request][pulls]! 313 | 314 | ## Games made with this project (Click the images to play!) 315 | 316 | 317 | 318 | 319 | Game: America First: The Game 320 | 321 | Author: Enzo Testa 322 | 323 | Github Repo: https://github.com/Shrakka/DoublePong 324 | 325 | Description: This is Enzo's first game! Well done double pong demo. 326 | 327 |
328 | 329 | 330 | 331 | 332 | Game: Coffee Conundrum 333 | 334 | Author: Jonathan Grangien 335 | 336 | Github Repo: https://github.com/codingInSpace/CoffeeConundrum 337 | 338 | Description: Cool little SHMUP. Code looks well done and nice and clean and well thought out. Nice job Jonathan! 339 | 340 |
341 | 342 | 343 | 344 | 345 | Game: P0ng 346 | 347 | Author: ProfitWarning 348 | 349 | Github Repo: https://github.com/ProfitWarning/p0ng 350 | 351 | Description: Nice 2-player pong clone. 352 | 353 |
354 | 355 | 356 | 357 | 358 | Game: Great Run 359 | 360 | Author: Róbert Girhiny 361 | 362 | Github Repo: https://github.com/robcaa/great-run 363 | 364 | Description: Nice Dora The Explorer themed platformer/runner for kids. 365 | 366 |
367 | 368 | 369 | 370 | 371 | Game: Take Back the Colors 372 | 373 | Author: j3soon 374 | 375 | Github Repo: https://github.com/j3soon/TakeBackTheColors 376 | 377 | Description: 2D platformer game with swinging ability. 378 | 379 |
380 | 381 | 382 | 383 | 384 | Game: R0d3nt 385 | 386 | Author: tuskat 387 | 388 | Github Repo: https://github.com/tuskat/R0d3nt 389 | 390 | Description: Futur1st1c P3st C0ntr0l 391 | 392 | [issues]: https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/issues 393 | [pulls]: https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/pulls 394 | [releases]: https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/releases 395 | [fontsquirrel]: https://www.fontsquirrel.com/tools/webfont-generator 396 | [everythingfonts]: https://everythingfonts.com/font-face 397 | [git-scm]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git 398 | [nodejs]: https://nodejs.org/en/ 399 | [electron-distribution]: https://electron.atom.io/docs/tutorial/application-distribution/ 400 | [electron-pack-windows]: https://github.com/electron-userland/electron-packager#building-windows-apps-from-non-windows-platforms 401 | [electron-pack-api]: https://github.com/electron-userland/electron-packager/blob/master/docs/api.md 402 | -------------------------------------------------------------------------------- /README_HEADER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/README_HEADER.png -------------------------------------------------------------------------------- /america-first-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/america-first-icon.png -------------------------------------------------------------------------------- /assets/atlases/preload_sprites_array.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": [ 3 | { 4 | "filename": "preload_bar.png", 5 | "frame": {"x":0, "y":51, "w":200, "h":50}, 6 | "spriteSourceSize": {"x":0,"y":0,"w":200,"h":50}, 7 | "sourceSize": {"w":200,"h":50} 8 | }, 9 | { 10 | "filename": "preload_frame.png", 11 | "frame": {"x":0, "y":0, "w":200, "h":50}, 12 | "spriteSourceSize": {"x":0,"y":0,"w":200,"h":50}, 13 | "sourceSize": {"w":200,"h":50} 14 | } 15 | 16 | ], 17 | "meta": { 18 | "image": "preload_sprites_array.png", 19 | "size": {"w": 256, "h": 128}, 20 | "scale": "1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /assets/atlases/preload_sprites_array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/atlases/preload_sprites_array.png -------------------------------------------------------------------------------- /assets/atlases/preload_sprites_hash.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "preload_bar.png": { 4 | "frame": {"x":0, "y":51, "w":200, "h":50}, 5 | "spriteSourceSize": {"x":0,"y":0,"w":200,"h":50}, 6 | "sourceSize": {"w":200,"h":50} 7 | }, 8 | "preload_frame.png": { 9 | "frame": {"x":0, "y":0, "w":200, "h":50}, 10 | "spriteSourceSize": {"x":0,"y":0,"w":200,"h":50}, 11 | "sourceSize": {"w":200,"h":50} 12 | } 13 | 14 | }, 15 | "meta": { 16 | "image": "preload_sprites_hash.png", 17 | "size": {"w": 256, "h": 128}, 18 | "scale": "1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /assets/atlases/preload_sprites_hash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/atlases/preload_sprites_hash.png -------------------------------------------------------------------------------- /assets/atlases/preload_sprites_xml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/atlases/preload_sprites_xml.png -------------------------------------------------------------------------------- /assets/atlases/preload_sprites_xml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/audio/music.ac3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/audio/music.ac3 -------------------------------------------------------------------------------- /assets/audio/music.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/audio/music.m4a -------------------------------------------------------------------------------- /assets/audio/music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/audio/music.mp3 -------------------------------------------------------------------------------- /assets/audio/music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/audio/music.ogg -------------------------------------------------------------------------------- /assets/audiosprites/sfx.ac3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/audiosprites/sfx.ac3 -------------------------------------------------------------------------------- /assets/audiosprites/sfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources": [ 3 | "sfx.ogg", 4 | "sfx.m4a", 5 | "sfx.mp3", 6 | "sfx.ac3" 7 | ], 8 | "spritemap": { 9 | "laser1": { 10 | "start": 0, 11 | "end": 1.6268480725623582, 12 | "loop": false 13 | }, 14 | "laser2": { 15 | "start": 3, 16 | "end": 4.821315192743764, 17 | "loop": false 18 | }, 19 | "laser3": { 20 | "start": 6, 21 | "end": 7.911292517006803, 22 | "loop": false 23 | }, 24 | "laser4": { 25 | "start": 9, 26 | "end": 10.754557823129252, 27 | "loop": false 28 | }, 29 | "laser5": { 30 | "start": 12, 31 | "end": 14.189931972789115, 32 | "loop": false 33 | }, 34 | "laser6": { 35 | "start": 16, 36 | "end": 17.278548752834467, 37 | "loop": false 38 | }, 39 | "laser7": { 40 | "start": 19, 41 | "end": 20.30467120181406, 42 | "loop": false 43 | }, 44 | "laser8": { 45 | "start": 22, 46 | "end": 23.51655328798186, 47 | "loop": false 48 | }, 49 | "laser9": { 50 | "start": 25, 51 | "end": 26.287256235827666, 52 | "loop": false 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /assets/audiosprites/sfx.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/audiosprites/sfx.m4a -------------------------------------------------------------------------------- /assets/audiosprites/sfx.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/audiosprites/sfx.mp3 -------------------------------------------------------------------------------- /assets/audiosprites/sfx.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/audiosprites/sfx.ogg -------------------------------------------------------------------------------- /assets/fonts/2Dumb-webfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: '2dumbregular'; 3 | src: url('2Dumb-webfont.eot'); 4 | src: url('2Dumb-webfont.eot?#iefix') format('embedded-opentype'), 5 | url('2Dumb-webfont.woff') format('woff'), 6 | url('2Dumb-webfont.ttf') format('truetype'), 7 | url('2Dumb-webfont.svg#2dumbregular') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /assets/fonts/2Dumb-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/fonts/2Dumb-webfont.eot -------------------------------------------------------------------------------- /assets/fonts/2Dumb-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/fonts/2Dumb-webfont.ttf -------------------------------------------------------------------------------- /assets/fonts/2Dumb-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/fonts/2Dumb-webfont.woff -------------------------------------------------------------------------------- /assets/fonts/font_fnt.fnt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /assets/fonts/font_fnt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/fonts/font_fnt.png -------------------------------------------------------------------------------- /assets/fonts/font_xml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/fonts/font_xml.png -------------------------------------------------------------------------------- /assets/fonts/font_xml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /assets/images/background_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/images/background_template.png -------------------------------------------------------------------------------- /assets/scripts/BlurX.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A horizontal blur filter by Mat Groves http://matgroves.com/ @Doormat23 3 | */ 4 | Phaser.Filter.BlurX = function (game) { 5 | 6 | Phaser.Filter.call(this, game); 7 | 8 | this.uniforms.blur = { type: '1f', value: 1 / 512 }; 9 | 10 | this.fragmentSrc = [ 11 | 12 | "precision mediump float;", 13 | "varying vec2 vTextureCoord;", 14 | "varying vec4 vColor;", 15 | "uniform float blur;", 16 | "uniform sampler2D uSampler;", 17 | 18 | "void main(void) {", 19 | 20 | "vec4 sum = vec4(0.0);", 21 | 22 | "sum += texture2D(uSampler, vec2(vTextureCoord.x - 4.0*blur, vTextureCoord.y)) * 0.05;", 23 | "sum += texture2D(uSampler, vec2(vTextureCoord.x - 3.0*blur, vTextureCoord.y)) * 0.09;", 24 | "sum += texture2D(uSampler, vec2(vTextureCoord.x - 2.0*blur, vTextureCoord.y)) * 0.12;", 25 | "sum += texture2D(uSampler, vec2(vTextureCoord.x - blur, vTextureCoord.y)) * 0.15;", 26 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * 0.16;", 27 | "sum += texture2D(uSampler, vec2(vTextureCoord.x + blur, vTextureCoord.y)) * 0.15;", 28 | "sum += texture2D(uSampler, vec2(vTextureCoord.x + 2.0*blur, vTextureCoord.y)) * 0.12;", 29 | "sum += texture2D(uSampler, vec2(vTextureCoord.x + 3.0*blur, vTextureCoord.y)) * 0.09;", 30 | "sum += texture2D(uSampler, vec2(vTextureCoord.x + 4.0*blur, vTextureCoord.y)) * 0.05;", 31 | 32 | "gl_FragColor = sum;", 33 | 34 | "}" 35 | ]; 36 | 37 | }; 38 | 39 | Phaser.Filter.BlurX.prototype = Object.create(Phaser.Filter.prototype); 40 | Phaser.Filter.BlurX.prototype.constructor = Phaser.Filter.BlurX; 41 | 42 | Object.defineProperty(Phaser.Filter.BlurX.prototype, 'blur', { 43 | 44 | get: function() { 45 | return this.uniforms.blur.value / (1/7000); 46 | }, 47 | 48 | set: function(value) { 49 | this.dirty = true; 50 | this.uniforms.blur.value = (1/7000) * value; 51 | } 52 | 53 | }); 54 | -------------------------------------------------------------------------------- /assets/scripts/BlurY.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A vertical blur filter by Mat Groves http://matgroves.com/ @Doormat23 3 | */ 4 | Phaser.Filter.BlurY = function (game) { 5 | 6 | Phaser.Filter.call(this, game); 7 | 8 | this.uniforms.blur = { type: '1f', value: 1 / 512 }; 9 | 10 | this.fragmentSrc = [ 11 | 12 | "precision mediump float;", 13 | "varying vec2 vTextureCoord;", 14 | "varying vec4 vColor;", 15 | "uniform float blur;", 16 | "uniform sampler2D uSampler;", 17 | 18 | "void main(void) {", 19 | 20 | "vec4 sum = vec4(0.0);", 21 | 22 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - 4.0*blur)) * 0.05;", 23 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - 3.0*blur)) * 0.09;", 24 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - 2.0*blur)) * 0.12;", 25 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - blur)) * 0.15;", 26 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * 0.16;", 27 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + blur)) * 0.15;", 28 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + 2.0*blur)) * 0.12;", 29 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + 3.0*blur)) * 0.09;", 30 | "sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + 4.0*blur)) * 0.05;", 31 | 32 | "gl_FragColor = sum;", 33 | 34 | "}" 35 | 36 | ]; 37 | 38 | }; 39 | 40 | Phaser.Filter.BlurY.prototype = Object.create(Phaser.Filter.prototype); 41 | Phaser.Filter.BlurY.prototype.constructor = Phaser.Filter.BlurY; 42 | 43 | Object.defineProperty(Phaser.Filter.BlurY.prototype, 'blur', { 44 | 45 | get: function() { 46 | return this.uniforms.blur.value / (1/7000); 47 | }, 48 | 49 | set: function(value) { 50 | this.dirty = true; 51 | this.uniforms.blur.value = (1/7000) * value; 52 | } 53 | 54 | }); 55 | -------------------------------------------------------------------------------- /assets/shaders/pixelate.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | varying vec2 vTextureCoord; 4 | 5 | uniform sampler2D uSampler; 6 | 7 | void main(void) { 8 | vec2 dimensions = vec2(1000.0, 10000.0); 9 | vec2 pixelSize = vec2(3.0, 3.0); 10 | vec2 coord = vTextureCoord; 11 | vec2 size = dimensions.xy/pixelSize; 12 | vec2 color = floor( ( vTextureCoord * size ) ) / size + pixelSize/dimensions.xy * 0.5; 13 | gl_FragColor = texture2D(uSampler, color); 14 | } 15 | -------------------------------------------------------------------------------- /assets/spritesheets/metalslug_mummy.[37,45,18,0,0].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets/spritesheets/metalslug_mummy.[37,45,18,0,0].png -------------------------------------------------------------------------------- /assets/tilemaps/tilemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "backgroundcolor":"#656667", 3 | "height":4, 4 | "layers":[ ], 5 | "nextobjectid":1, 6 | "orientation":"orthogonal", 7 | "properties":[ 8 | { 9 | "name":"mapProperty1", 10 | "type":"one", 11 | "value":"string" 12 | }, 13 | { 14 | "name":"mapProperty2", 15 | "type":"two", 16 | "value":"string" 17 | }], 18 | "renderorder":"right-down", 19 | "tileheight":32, 20 | "tilesets":[ ], 21 | "tilewidth":32, 22 | "version":1, 23 | "tiledversion":"1.0.3", 24 | "width":4 25 | } -------------------------------------------------------------------------------- /assets_raw/images/preload_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets_raw/images/preload_bar.png -------------------------------------------------------------------------------- /assets_raw/images/preload_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/assets_raw/images/preload_frame.png -------------------------------------------------------------------------------- /coffee-conundrum-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/coffee-conundrum-icon.png -------------------------------------------------------------------------------- /dora-great-run-icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/dora-great-run-icon.jpg -------------------------------------------------------------------------------- /electron-main.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | // Module to control application life. 3 | const app = electron.app; 4 | // Module to create native browser window. 5 | const BrowserWindow = electron.BrowserWindow; 6 | 7 | // Keep a global reference of the window object, if you don't, the window will 8 | // be closed automatically when the JavaScript object is garbage collected. 9 | var mainWindow; 10 | 11 | function createWindow () { 12 | // Create the browser window. 13 | mainWindow = new BrowserWindow({ 14 | fullscreen: false, 15 | fullscreenable: false, 16 | height: /*[[DEFAULT_GAME_HEIGHT*/500/*DEFAULT_GAME_HEIGHT]]*/, // This is changed via the setupGameSize script, change DEFAULT_GAME_HEIGHT to MAX_GAME_HEIGHT if you'd rather use the full size instead of only the safe zone size. 17 | maximizable: false, 18 | resizeable: false, 19 | show: false, 20 | useContentSize: true, 21 | width: /*[[DEFAULT_GAME_WIDTH*/800/*DEFAULT_GAME_WIDTH]]*/ // This is changed via the setupGameSize script, change DEFAULT_GAME_WIDTH to MAX_GAME_WIDTH if you'd rather use the full size instead of only the safe zone size. 22 | }); 23 | 24 | mainWindow.once('ready-to-show', function() { 25 | mainWindow.show(); 26 | }); 27 | 28 | // and load the index.html of the app. 29 | mainWindow.loadURL('file://' + __dirname + '/dist/index.html'); 30 | 31 | // Open the DevTools. 32 | // mainWindow.webContents.openDevTools(); 33 | 34 | // Emitted when the window is closed. 35 | mainWindow.on('closed', function () { 36 | // Dereference the window object, usually you would store windows 37 | // in an array if your app supports multi windows, this is the time 38 | // when you should delete the corresponding element. 39 | mainWindow = null; 40 | }); 41 | } 42 | 43 | // This method will be called when Electron has finished 44 | // initialization and is ready to create browser windows. 45 | // Some APIs can only be used after this event occurs. 46 | app.on('ready', createWindow); 47 | 48 | // Quit when all windows are closed. 49 | app.on('window-all-closed', function () { 50 | // On OS X it is common for applications and their menu bar 51 | // to stay active until the user quits explicitly with Cmd + Q 52 | if (process.platform !== 'darwin') { 53 | app.quit(); 54 | } 55 | }); 56 | 57 | app.on('activate', function () { 58 | // On OS X it's common to re-create a window in the app when the 59 | // dock icon is clicked and there are no other windows open. 60 | if (mainWindow === null) { 61 | createWindow(); 62 | } 63 | }); 64 | 65 | // In this file you can include the rest of your app's specific main process 66 | // code. You can also put them in separate files and require them here. 67 | -------------------------------------------------------------------------------- /p0ng-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/p0ng-icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phaser-ce-npm-webpack-typescript-starter-project", 3 | "productName": "phaser-ce-npm-webpack-typescript-starter-project", 4 | "version": "1.11.1", 5 | "main": "electron-main.js", 6 | "scripts": { 7 | "webpack:dev": "webpack --config webpack.dev.config.js --progress --colors", 8 | "webpack:dist": "webpack --config webpack.dist.config.js --progress --colors", 9 | "build:dev": "echo-cli 'Building DEV ...' && npm run webpack:dev && echo-cli 'Build Complete ✔'", 10 | "build:dist": "echo-cli 'Building DIST ...' && npm run webpack:dist && echo-cli 'Build Complete ✔'", 11 | "server:dev": "webpack-dev-server --config webpack.dev.config.js --open", 12 | "server:dist": "webpack-dev-server --config webpack.dist.config.js --open", 13 | "setupGameSize": "node ./scripts/setupGameSize.js", 14 | "assets:dev": "node ./scripts/generateAssetsClass.js --dev", 15 | "assets": "node ./scripts/generateAssetsClass.js", 16 | "postinstall": "npm run setupGameSize", 17 | "electron:dev": "npm run build:dev && electron .", 18 | "electron:dist": "npm run build:dist && electron .", 19 | "electron:pack:dev": "npm run build:dev && node ./scripts/packageElectronApp.js", 20 | "electron:pack:dist": "npm run build:dist && node ./scripts/packageElectronApp.js", 21 | "checkDepsVersions": "npm-check --skip-unused -u" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project.git" 26 | }, 27 | "author": "Richard Roylance ", 28 | "bugs": { 29 | "url": "https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/issues" 30 | }, 31 | "homepage": "https://github.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project#readme", 32 | "dependencies": { 33 | "@types/node": "^10.12.12", 34 | "@types/webfontloader": "^1.6.29", 35 | "clean-webpack-plugin": "^1.0.0", 36 | "commander": "^2.19.0", 37 | "copy-webpack-plugin": "^4.6.0", 38 | "echo-cli": "^1.0.8", 39 | "electron": "^3.0.10", 40 | "electron-packager": "^12.2.0", 41 | "expose-loader": "^0.7.5", 42 | "file-loader": "^2.0.0", 43 | "html-webpack-plugin": "^3.2.0", 44 | "imagejs": "^0.0.9", 45 | "npm-check": "^5.9.0", 46 | "phaser-ce": "^2.11.1", 47 | "pngjs-image": "^0.11.7", 48 | "shelljs": "^0.8.3", 49 | "ts-loader": "^5.3.1", 50 | "tslint": "^5.11.0", 51 | "tslint-loader": "^3.5.4", 52 | "typescript": "^3.2.1", 53 | "webfontloader": "^1.6.28", 54 | "webpack": "^4.27.0", 55 | "webpack-cli": "^3.1.2", 56 | "webpack-dev-server": "^3.1.14", 57 | "webpack-synchronizable-shell-plugin": "^0.0.7", 58 | "xml2js": "^0.4.19" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /r0d3nt-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/r0d3nt-icon.png -------------------------------------------------------------------------------- /scripts/generateAssetsClass.js: -------------------------------------------------------------------------------- 1 | var shell = require('shelljs'); 2 | var fs = require('fs'); 3 | var xml2js = require('xml2js'); 4 | var commander = require('commander'); 5 | var webpack = require('webpack'); 6 | var webpackConfig; 7 | 8 | commander 9 | .option('--dev', 'Use webpack.dev.config.js for some values, excluding this will use webpack.dist.config.js (currently only GOOGLE_WEB_FONTS is being used).') 10 | .parse(process.argv); 11 | 12 | if (commander.dev) { 13 | webpackConfig = require('../webpack.dev.config.js'); 14 | } else { 15 | webpackConfig = require('../webpack.dist.config.js'); 16 | } 17 | 18 | function toCamelCase(string) { 19 | return string.replace(/[^A-Za-z0-9]/g, ' ').replace(/^\w|[A-Z]|\b\w|\s+/g, function (match, index) { 20 | if (+match === 0 || match === '-' || match === '.') { 21 | return ""; 22 | } 23 | return index === 0 ? match.toLowerCase() : match.toUpperCase(); 24 | }); 25 | } 26 | 27 | function toPascalCase(string) { 28 | var camelCase = toCamelCase(string); 29 | 30 | return camelCase[0].toUpperCase() + camelCase.substr(1); 31 | } 32 | 33 | function findExtension(haystack, arr) { 34 | return arr.some(function (v) { 35 | return haystack.lastIndexOf(v) >= 0; 36 | }); 37 | } 38 | 39 | var gameAssets = {}; 40 | 41 | var loaderTypes = { 42 | image: {}, 43 | spritesheet: {}, 44 | atlas: {}, 45 | audio: {}, 46 | audiosprite: {}, 47 | font: {}, 48 | bitmap_font: {}, 49 | json: {}, 50 | tilemap_json: {}, 51 | xml: {}, 52 | text: {}, 53 | script: {}, 54 | shader: {}, 55 | misc: {} 56 | }; 57 | 58 | var audioExtensions = ['aac', 'ac3', 'caf', 'flac', 'm4a', 'mp3', 'mp4', 'ogg', 'wav', 'webm']; 59 | var imageExtensions = ['bmp', 'gif', 'jpg', 'jpeg', 'png', 'webp']; 60 | var fontExtensions = ['eot', 'otf', 'svg', 'ttf', 'woff', 'woff2']; 61 | var bitmapFontExtensions = ['xml', 'fnt']; 62 | var jsonExtensions = ['json']; 63 | var xmlExtensions = ['xml']; 64 | var textExtensions = ['txt']; 65 | var scriptExtensions = ['js']; 66 | var shaderExtensions = ['frag']; 67 | 68 | shell.ls('-R', 'assets/').forEach(function (file) { 69 | var filePath = file; 70 | var fileName = filePath.substring(0, filePath.lastIndexOf('.')); 71 | 72 | var extensionIndex = filePath.lastIndexOf('.'); 73 | if (extensionIndex === -1) { 74 | return; 75 | } 76 | var extension = filePath.substr(extensionIndex + 1); 77 | 78 | gameAssets[fileName] = gameAssets[fileName] || []; 79 | gameAssets[fileName] = gameAssets[fileName].concat(extension); 80 | }); 81 | 82 | for (var i in gameAssets) { 83 | var imageType = findExtension(gameAssets[i], imageExtensions); 84 | var audioType = findExtension(gameAssets[i], audioExtensions); 85 | var fontType = findExtension(gameAssets[i], fontExtensions); 86 | var bitmapFontType = findExtension(gameAssets[i], bitmapFontExtensions); 87 | var jsonType = findExtension(gameAssets[i], jsonExtensions); 88 | var xmlType = findExtension(gameAssets[i], xmlExtensions); 89 | var textType = findExtension(gameAssets[i], textExtensions); 90 | var scriptType = findExtension(gameAssets[i], scriptExtensions); 91 | var shaderType = findExtension(gameAssets[i], shaderExtensions); 92 | 93 | if (bitmapFontType) { 94 | var isItActuallyAFont = false; 95 | 96 | for (var ext in gameAssets[i]) { 97 | if (((shell.grep(/^[\s\S]*?/g, ('assets/' + i + '.' + gameAssets[i][ext]))).length > 1)) { 98 | isItActuallyAFont = true; 99 | break; 100 | } 101 | } 102 | 103 | bitmapFontType = isItActuallyAFont; 104 | } 105 | 106 | if (bitmapFontType && imageType) { 107 | loaderTypes.bitmap_font[i] = gameAssets[i]; 108 | } else if (audioType) { 109 | if (jsonType) { 110 | loaderTypes.audiosprite[i] = gameAssets[i]; 111 | } else { 112 | loaderTypes.audio[i] = gameAssets[i]; 113 | } 114 | } else if (imageType) { 115 | if (jsonType || xmlType) { 116 | loaderTypes.atlas[i] = gameAssets[i]; 117 | } else { 118 | var spritesheetData = i.match(/\[(-?[0-9],?)*]/); 119 | if (spritesheetData && spritesheetData.length > 0) { 120 | loaderTypes.spritesheet[i] = gameAssets[i]; 121 | } else { 122 | loaderTypes.image[i] = gameAssets[i]; 123 | } 124 | } 125 | } else if (fontType) { 126 | loaderTypes.font[i] = gameAssets[i]; 127 | } else if (jsonType) { 128 | var isItATilemap = false; 129 | 130 | for (var ext in gameAssets[i]) { 131 | if (((shell.grep(/^[\s\S]*?"tiledversion"/g, ('assets/' + i + '.' + gameAssets[i][ext]))).length > 1) && ((shell.grep(/^[\s\S]*?"tilewidth"/g, ('assets/' + i + '.' + gameAssets[i][ext]))).length > 1) && ((shell.grep(/^[\s\S]*?"tileheight"/g, ('assets/' + i + '.' + gameAssets[i][ext]))).length > 1)) { 132 | loaderTypes.tilemap_json[i] = gameAssets[i]; 133 | 134 | isItATilemap = true; 135 | break; 136 | } 137 | } 138 | 139 | if (!isItATilemap) { 140 | loaderTypes.json[i] = gameAssets[i]; 141 | } 142 | } else if (xmlType) { 143 | loaderTypes.xml[i] = gameAssets[i]; 144 | } else if (textType) { 145 | loaderTypes.text[i] = gameAssets[i]; 146 | } else if (scriptType) { 147 | loaderTypes.script[i] = gameAssets[i]; 148 | } else if (shaderType) { 149 | loaderTypes.shader[i] = gameAssets[i]; 150 | } else { 151 | loaderTypes.misc[i] = gameAssets[i]; 152 | } 153 | } 154 | 155 | var assetsClassFile = 'src/assets.ts'; 156 | shell.rm('-f', assetsClassFile); 157 | 158 | shell.ShellString('/* AUTO GENERATED FILE. DO NOT MODIFY. YOU WILL LOSE YOUR CHANGES ON BUILD. */\n\n').to(assetsClassFile); 159 | 160 | shell.ShellString('export namespace Images {').toEnd(assetsClassFile); 161 | if (!Object.keys(loaderTypes.image).length) { 162 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 163 | } else { 164 | for (var i in loaderTypes.image) { 165 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 166 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 167 | 168 | for (var t in loaderTypes.image[i]) { 169 | shell.ShellString('\n static get' + loaderTypes.image[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.image[i][t] + '\'); }').toEnd(assetsClassFile); 170 | } 171 | 172 | shell.ShellString('\n }').toEnd(assetsClassFile); 173 | } 174 | } 175 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 176 | 177 | shell.ShellString('export namespace Spritesheets {').toEnd(assetsClassFile); 178 | if (!Object.keys(loaderTypes.spritesheet).length) { 179 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 180 | } else { 181 | for (var i in loaderTypes.spritesheet) { 182 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 183 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 184 | 185 | shell.ShellString('\n static get' + loaderTypes.spritesheet[i][0].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.spritesheet[i][0] + (loaderTypes.spritesheet[i][1] ? '.' + loaderTypes.spritesheet[i][1] : '') + '\'); }').toEnd(assetsClassFile); 186 | 187 | var spritesheetProperties = i.split('.')[1].replace('[', '').replace(']', '').split(','); 188 | if (spritesheetProperties.length < 2 || spritesheetProperties.length > 5) { 189 | console.log('Invalid number of Spritesheet properties provided for \'' + i + '\'. Must have between 2 and 5; [frameWidth, frameHeight, frameMax, margin, spacing] frameWidth and frameHeight are required'); 190 | } 191 | 192 | shell.ShellString('\n static getFrameWidth(): number { return ' + parseInt(spritesheetProperties[0] ? spritesheetProperties[0] : -1) + '; }').toEnd(assetsClassFile); 193 | shell.ShellString('\n static getFrameHeight(): number { return ' + parseInt(spritesheetProperties[1] ? spritesheetProperties[1] : -1) + '; }').toEnd(assetsClassFile); 194 | shell.ShellString('\n static getFrameMax(): number { return ' + parseInt(spritesheetProperties[2] ? spritesheetProperties[2] : -1) + '; }').toEnd(assetsClassFile); 195 | shell.ShellString('\n static getMargin(): number { return ' + parseInt(spritesheetProperties[3] ? spritesheetProperties[3] : 0) + '; }').toEnd(assetsClassFile); 196 | shell.ShellString('\n static getSpacing(): number { return ' + parseInt(spritesheetProperties[4] ? spritesheetProperties[4] : 0) + '; }').toEnd(assetsClassFile); 197 | 198 | shell.ShellString('\n }').toEnd(assetsClassFile); 199 | } 200 | } 201 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 202 | 203 | shell.ShellString('export namespace Atlases {').toEnd(assetsClassFile); 204 | if (!Object.keys(loaderTypes.atlas).length) { 205 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 206 | } else { 207 | for (var i in loaderTypes.atlas) { 208 | var dataExtensions = []; 209 | var dataTypes = []; 210 | var dataJSON = []; 211 | 212 | for (var t in loaderTypes.atlas[i]) { 213 | var dataFile = ('assets/' + i + '.' + loaderTypes.atlas[i][t]); 214 | var fileData = null; 215 | var json = null; 216 | 217 | dataExtensions.push(loaderTypes.atlas[i][t]); 218 | 219 | if (jsonExtensions.indexOf(loaderTypes.atlas[i][t]) !== -1) { 220 | try { 221 | fileData = fs.readFileSync(dataFile, 'ascii'); 222 | json = JSON.parse(fileData); 223 | 224 | dataJSON.push(json); 225 | 226 | if (Array.isArray(json['frames'])) { 227 | dataTypes.push('Array'); 228 | } else { 229 | dataTypes.push('Hash'); 230 | } 231 | } catch (e) { 232 | console.log('Atlas Data File Error: ', e); 233 | } 234 | } else if (xmlExtensions.indexOf(loaderTypes.atlas[i][t]) !== -1) { 235 | dataTypes.push(''); 236 | dataJSON.push(''); 237 | } else { 238 | dataTypes.push(''); 239 | dataJSON.push(''); 240 | } 241 | } 242 | 243 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 244 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }').toEnd(assetsClassFile); 245 | for (var e in dataExtensions) { 246 | shell.ShellString('\n\n static get' + dataExtensions[e].toUpperCase() + dataTypes[e] + '(): string { return require(\'assets/' + i + '.' + dataExtensions[e] + '\'); }').toEnd(assetsClassFile); 247 | } 248 | shell.ShellString('\n }').toEnd(assetsClassFile); 249 | 250 | for (var e in dataExtensions) { 251 | var json = null; 252 | var parser = null; 253 | var frameFull = ''; 254 | var frame = ''; 255 | var indexOfExtension = -1; 256 | 257 | if (dataExtensions[e].toUpperCase() === 'JSON') { 258 | json = dataJSON[e]; 259 | 260 | shell.ShellString('\n export namespace ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 261 | shell.ShellString('\n export enum Frames {').toEnd(assetsClassFile); 262 | 263 | if (dataTypes[e] === 'Array') { 264 | for (var a in json['frames']) { 265 | frameFull = (json['frames'][a]['filename']); 266 | indexOfExtension = frameFull.lastIndexOf('.'); 267 | if (indexOfExtension === -1) { 268 | frame = frameFull; 269 | } else { 270 | frame = frameFull.substring(0, indexOfExtension); 271 | } 272 | shell.ShellString('\n ' + toPascalCase(frame) + ' = \'' + frameFull + '\',').toEnd(assetsClassFile); 273 | } 274 | } else { 275 | for (var h in json['frames']) { 276 | frameFull = (h); 277 | indexOfExtension = frameFull.lastIndexOf('.'); 278 | if (indexOfExtension === -1) { 279 | frame = frameFull; 280 | } else { 281 | frame = frameFull.substring(0, indexOfExtension); 282 | } 283 | shell.ShellString('\n ' + toPascalCase(frame) + ' = \'' + frameFull + '\',').toEnd(assetsClassFile); 284 | } 285 | } 286 | 287 | shell.ShellString('\n }').toEnd(assetsClassFile); 288 | shell.ShellString('\n }').toEnd(assetsClassFile); 289 | } else if (dataExtensions[e].toUpperCase() === 'XML') { 290 | shell.ShellString('\n export namespace ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 291 | shell.ShellString('\n export enum Frames {').toEnd(assetsClassFile); 292 | 293 | fileData = fs.readFileSync(dataFile, 'ascii'); 294 | parser = new xml2js.Parser(); 295 | 296 | parser.parseString(fileData.substring(0, fileData.length), function (err, result) { 297 | for (var x in result['TextureAtlas']['SubTexture']) { 298 | frameFull = (result['TextureAtlas']['SubTexture'][x]['$']['name']); 299 | indexOfExtension = frameFull.lastIndexOf('.'); 300 | if (indexOfExtension === -1) { 301 | frame = frameFull; 302 | } else { 303 | frame = frameFull.substring(0, indexOfExtension); 304 | } 305 | shell.ShellString('\n ' + toPascalCase(frame) + ' = \'' + frameFull + '\',').toEnd(assetsClassFile); 306 | } 307 | }); 308 | 309 | shell.ShellString('\n }').toEnd(assetsClassFile); 310 | shell.ShellString('\n }').toEnd(assetsClassFile); 311 | } 312 | } 313 | } 314 | } 315 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 316 | 317 | shell.ShellString('export namespace Audio {').toEnd(assetsClassFile); 318 | if (!Object.keys(loaderTypes.audio).length) { 319 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 320 | } else { 321 | for (var i in loaderTypes.audio) { 322 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 323 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 324 | 325 | for (var t in loaderTypes.audio[i]) { 326 | shell.ShellString('\n static get' + loaderTypes.audio[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.audio[i][t] + '\'); }').toEnd(assetsClassFile); 327 | } 328 | 329 | shell.ShellString('\n }').toEnd(assetsClassFile); 330 | } 331 | } 332 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 333 | 334 | shell.ShellString('export namespace Audiosprites {').toEnd(assetsClassFile); 335 | if (!Object.keys(loaderTypes.audiosprite).length) { 336 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 337 | } else { 338 | for (var i in loaderTypes.audiosprite) { 339 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 340 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 341 | for (var t in loaderTypes.audiosprite[i]) { 342 | shell.ShellString('\n static get' + loaderTypes.audiosprite[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.audiosprite[i][t] + '\'); }').toEnd(assetsClassFile); 343 | } 344 | shell.ShellString('\n }\n').toEnd(assetsClassFile); 345 | 346 | shell.ShellString(' export namespace ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 347 | for (var t in loaderTypes.audiosprite[i]) { 348 | var dataFile = ('assets/' + i + '.' + loaderTypes.audiosprite[i][t]); 349 | var fileData = null; 350 | var json = null; 351 | var sprite = null; 352 | 353 | if (jsonExtensions.indexOf(loaderTypes.audiosprite[i][t]) !== -1) { 354 | shell.ShellString('\n export enum Sprites {').toEnd(assetsClassFile); 355 | 356 | try { 357 | fileData = fs.readFileSync(dataFile, 'ascii'); 358 | json = JSON.parse(fileData); 359 | 360 | for (var h in json['spritemap']) { 361 | sprite = (h); 362 | shell.ShellString('\n ' + toPascalCase(sprite) + ' = \'' + sprite + '\',').toEnd(assetsClassFile); 363 | } 364 | } catch (e) { 365 | console.log('Audiosprite Data File Error: ', e); 366 | } 367 | 368 | shell.ShellString('\n }').toEnd(assetsClassFile); 369 | } 370 | } 371 | shell.ShellString('\n }').toEnd(assetsClassFile); 372 | } 373 | } 374 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 375 | 376 | shell.ShellString('export namespace GoogleWebFonts {').toEnd(assetsClassFile); 377 | var webFontsToUse = JSON.parse(webpackConfig.plugins[webpackConfig.plugins.findIndex(function(element) { return (element instanceof webpack.DefinePlugin); })].definitions.GOOGLE_WEB_FONTS); 378 | if (!webFontsToUse.length) { 379 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 380 | } else { 381 | for (var i in webFontsToUse) { 382 | shell.ShellString('\n export const ' + toPascalCase(webFontsToUse[i]) + ': string = \'' + webFontsToUse[i] + '\';').toEnd(assetsClassFile); 383 | } 384 | } 385 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 386 | 387 | 388 | shell.ShellString('export namespace CustomWebFonts {').toEnd(assetsClassFile); 389 | if (!Object.keys(loaderTypes.font).length) { 390 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 391 | } else { 392 | for (var i in loaderTypes.font) { 393 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 394 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 395 | 396 | var cssFileData = fs.readFileSync(('assets/' + i + '.css'), 'ascii'); 397 | var family = /font-family:(\s)*('|")(\w*\W*)('|")/g.exec(cssFileData)[3]; 398 | shell.ShellString('\n static getFamily(): string { return \'' + family + '\'; }\n').toEnd(assetsClassFile); 399 | 400 | for (var t in loaderTypes.font[i]) { 401 | shell.ShellString('\n static get' + loaderTypes.font[i][t].toUpperCase() + '(): string { return require(\'!file-loader?name=assets/fonts/[name].[ext]!assets/' + i + '.' + loaderTypes.font[i][t] + '\'); }').toEnd(assetsClassFile); 402 | } 403 | 404 | shell.ShellString('\n }').toEnd(assetsClassFile); 405 | } 406 | } 407 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 408 | 409 | shell.ShellString('export namespace BitmapFonts {').toEnd(assetsClassFile); 410 | if (!Object.keys(loaderTypes.bitmap_font).length) { 411 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 412 | } else { 413 | for (var i in loaderTypes.bitmap_font) { 414 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 415 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 416 | 417 | for (var t in loaderTypes.bitmap_font[i]) { 418 | shell.ShellString('\n static get' + loaderTypes.bitmap_font[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.bitmap_font[i][t] + '\'); }').toEnd(assetsClassFile); 419 | } 420 | 421 | shell.ShellString('\n }').toEnd(assetsClassFile); 422 | } 423 | } 424 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 425 | 426 | shell.ShellString('export namespace JSON {').toEnd(assetsClassFile); 427 | if (!Object.keys(loaderTypes.json).length) { 428 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 429 | } else { 430 | for (var i in loaderTypes.json) { 431 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 432 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 433 | 434 | for (var t in loaderTypes.json[i]) { 435 | shell.ShellString('\n static get' + loaderTypes.json[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.json[i][t] + '\'); }').toEnd(assetsClassFile); 436 | } 437 | 438 | shell.ShellString('\n }').toEnd(assetsClassFile); 439 | } 440 | } 441 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 442 | 443 | shell.ShellString('export namespace TilemapJSON {').toEnd(assetsClassFile); 444 | if (!Object.keys(loaderTypes.tilemap_json).length) { 445 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 446 | } else { 447 | for (var i in loaderTypes.tilemap_json) { 448 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 449 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 450 | 451 | for (var t in loaderTypes.tilemap_json[i]) { 452 | shell.ShellString('\n static get' + loaderTypes.tilemap_json[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.tilemap_json[i][t] + '\'); }').toEnd(assetsClassFile); 453 | } 454 | 455 | shell.ShellString('\n }').toEnd(assetsClassFile); 456 | } 457 | } 458 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 459 | 460 | shell.ShellString('export namespace XML {').toEnd(assetsClassFile); 461 | if (!Object.keys(loaderTypes.xml).length) { 462 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 463 | } else { 464 | for (var i in loaderTypes.xml) { 465 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 466 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 467 | 468 | for (var t in loaderTypes.xml[i]) { 469 | shell.ShellString('\n static get' + loaderTypes.xml[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.xml[i][t] + '\'); }').toEnd(assetsClassFile); 470 | } 471 | 472 | shell.ShellString('\n }').toEnd(assetsClassFile); 473 | } 474 | } 475 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 476 | 477 | shell.ShellString('export namespace Text {').toEnd(assetsClassFile); 478 | if (!Object.keys(loaderTypes.text).length) { 479 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 480 | } else { 481 | for (var i in loaderTypes.text) { 482 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 483 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 484 | 485 | for (var t in loaderTypes.text[i]) { 486 | shell.ShellString('\n static get' + loaderTypes.text[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.text[i][t] + '\'); }').toEnd(assetsClassFile); 487 | } 488 | 489 | shell.ShellString('\n }').toEnd(assetsClassFile); 490 | } 491 | } 492 | shell.ShellString('\n}\n\n').toEnd(assetsClassFile); 493 | 494 | shell.ShellString('export namespace Scripts {').toEnd(assetsClassFile); 495 | if (!Object.keys(loaderTypes.script).length) { 496 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 497 | } else { 498 | for (var i in loaderTypes.script) { 499 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 500 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 501 | 502 | for (var t in loaderTypes.script[i]) { 503 | shell.ShellString('\n static get' + loaderTypes.script[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.script[i][t] + '\'); }').toEnd(assetsClassFile); 504 | } 505 | 506 | shell.ShellString('\n }').toEnd(assetsClassFile); 507 | } 508 | } 509 | shell.ShellString('\n}\n').toEnd(assetsClassFile); 510 | 511 | shell.ShellString('export namespace Shaders {').toEnd(assetsClassFile); 512 | if (!Object.keys(loaderTypes.shader).length) { 513 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 514 | } else { 515 | for (var i in loaderTypes.shader) { 516 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 517 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 518 | 519 | for (var t in loaderTypes.shader[i]) { 520 | shell.ShellString('\n static get' + loaderTypes.shader[i][t].toUpperCase() + '(): string { return require(\'assets/' + i + '.' + loaderTypes.shader[i][t] + '\'); }').toEnd(assetsClassFile); 521 | } 522 | 523 | shell.ShellString('\n }').toEnd(assetsClassFile); 524 | } 525 | } 526 | shell.ShellString('\n}\n').toEnd(assetsClassFile); 527 | 528 | shell.ShellString('export namespace Misc {').toEnd(assetsClassFile); 529 | if (!Object.keys(loaderTypes.misc).length) { 530 | shell.ShellString('\n class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {}').toEnd(assetsClassFile); 531 | } else { 532 | for (var i in loaderTypes.misc) { 533 | shell.ShellString('\n export class ' + toPascalCase(i) + ' {').toEnd(assetsClassFile); 534 | shell.ShellString('\n static getName(): string { return \'' + i.split('/').pop() + '\'; }\n').toEnd(assetsClassFile); 535 | 536 | for (var t in loaderTypes.misc[i]) { 537 | shell.ShellString('\n static getFile(): string { return require(\'assets/' + i + '.' + loaderTypes.misc[i][t] + '\'); }').toEnd(assetsClassFile); 538 | } 539 | 540 | shell.ShellString('\n }').toEnd(assetsClassFile); 541 | } 542 | } 543 | shell.ShellString('\n}\n').toEnd(assetsClassFile); 544 | -------------------------------------------------------------------------------- /scripts/packageElectronApp.js: -------------------------------------------------------------------------------- 1 | var packager = require('electron-packager'); 2 | 3 | var args = process.argv.slice(2); 4 | var options = {}; 5 | 6 | for (var a = 0, argsCount = args.length; a < argsCount; a++) { 7 | var currentArg = args[a]; 8 | var nextArg = args[a + 1]; 9 | 10 | if (currentArg.indexOf('--') === 0 && (nextArg && nextArg.indexOf('--') === -1)) { 11 | currentArg = currentArg.slice(2); 12 | 13 | options[currentArg] = nextArg; 14 | 15 | a++; 16 | } else { 17 | currentArg = currentArg.slice(2).split('='); 18 | 19 | if (currentArg.length > 1) { 20 | options[currentArg.shift()] = currentArg.shift(); 21 | } else { 22 | options[currentArg.shift()] = true; 23 | } 24 | } 25 | } 26 | 27 | options.dir = (options.dir || '.'); 28 | options.out = (options.out || 'builds'); 29 | options.overwrite = (options.overwrite || true); 30 | options.ignore = (options.ignore || function (fileName) { 31 | return !(fileName === '' || 32 | fileName.indexOf('/dist') === 0 || 33 | fileName.indexOf('/package.json') === 0 || 34 | fileName.indexOf('/electron-main.js') === 0 35 | ); 36 | }); 37 | 38 | packager(options, function done_callback (err) { 39 | if (err) { 40 | console.log(err); 41 | return; 42 | } 43 | 44 | console.log('Packaging Complete ✔'); 45 | }); 46 | -------------------------------------------------------------------------------- /scripts/setupGameSize.js: -------------------------------------------------------------------------------- 1 | var PNGImage = require('pngjs-image'); 2 | var ImageJS = require('imagejs'); 3 | var commander = require('commander'); 4 | var shell = require('shelljs'); 5 | 6 | var filename = './assets/images/background_template.png'; 7 | var defaultWidth = 800; 8 | var defaultHeight = 500; 9 | var defaultAspectRatio = 1.6; 10 | var defaultScaleMode = 'USER_SCALE'; 11 | var possibleScaleModes = ['USER_SCALE', 'SHOW_ALL', 'EXACT_FIT', 'NO_SCALE', 'RESIZE']; 12 | 13 | commander 14 | .option('--width ', 'Base width of your game (Default: ' + defaultWidth + ')', function (val) { 15 | return parseInt(val, 10); 16 | }, -1) 17 | .option('--height ', 'Base height of your game (Default: ' + defaultHeight + ')', function (val) { 18 | return parseInt(val, 10); 19 | }, -1) 20 | .option('--aspect-ratio ', 'Aspect ratio of your game (Default: ' + defaultAspectRatio + ')', function (val) { 21 | return parseFloat(val); 22 | }, -1) 23 | .option('--scale-mode ', 'Scale mode of your game (Default: ' + defaultHeight + ') [USER_SCALE, SHOW_ALL, EXACT_FIT, NO_SCALE, RESIZE]', function (val) { 24 | return (possibleScaleModes.indexOf(val) !== -1 ? val : null); 25 | }, defaultScaleMode) 26 | .option('--no-png', 'Do not create the template background png') 27 | .parse(process.argv); 28 | 29 | var aspectRatio = commander.aspectRatio; 30 | var baseWidth = Math.round(commander.width); 31 | var baseHeight = Math.round(commander.height); 32 | var maxWidth = baseWidth; 33 | var maxHeight = baseHeight; 34 | var midHeight = -1; 35 | var isPortrait = false; 36 | var scaleMode = commander.scaleMode; 37 | 38 | if (scaleMode === null) { 39 | console.log('--scale-mode must be one of the available options; [USER_SCALE, SHOW_ALL, EXACT_FIT, NO_SCALE, RESIZE]'); 40 | return; 41 | } 42 | 43 | var image = null; 44 | 45 | var greenColor = {red: 0, green: 255, blue: 0, alpha: 100}; 46 | var redColor = {red: 255, green: 0, blue: 0, alpha: 100}; 47 | var blueColor = {red: 0, green: 0, blue: 255, alpha: 100}; 48 | var yellowColor = {red: 255, green: 255, blue: 0, alpha: 100}; 49 | var greyColor = {red: 128, green: 128, blue: 128, alpha: 100}; 50 | 51 | if (scaleMode === 'USER_SCALE') { 52 | // The game Display is scaled according to the user-specified scale set by setUserScale. 53 | 54 | if (aspectRatio === -1) { 55 | aspectRatio = defaultAspectRatio; 56 | } 57 | 58 | if (baseWidth === -1 && baseHeight === -1) { 59 | baseWidth = defaultWidth; 60 | baseHeight = defaultHeight; 61 | } else if (baseWidth === -1) { 62 | baseWidth = Math.round(baseHeight * aspectRatio); 63 | } else if (baseHeight === -1) { 64 | baseHeight = Math.round(baseWidth / aspectRatio); 65 | } 66 | 67 | if (baseWidth < baseHeight) { 68 | var temp = baseHeight; 69 | baseHeight = baseWidth; 70 | baseWidth = temp; 71 | 72 | isPortrait = true; 73 | } 74 | 75 | var providedAspectRatio = parseFloat((baseWidth / baseHeight).toFixed(3)); 76 | if (aspectRatio < 1) { 77 | aspectRatio = parseFloat((baseWidth / baseHeight).toFixed(3)); 78 | } 79 | if (providedAspectRatio !== aspectRatio) { 80 | throw 'Base width and height must result in a ' + aspectRatio + ' aspect ratio (width / height).\nYou only need to provide either width or height and the other will be calculated for you.'; 81 | } 82 | 83 | var iPhone4Resolution = { 84 | width: 960, 85 | height: 640 86 | }; 87 | 88 | var iPhone5Resolution = { 89 | width: 1136, 90 | height: 640 91 | } 92 | 93 | var iPadResolution = { 94 | width: 1024, 95 | height: 768 96 | } 97 | 98 | // Calculated based of the long and narrow (aspect ratio wise) iOS device, the iPhone 5 (1136 x 640) 99 | maxWidth = Math.round(iPhone5Resolution.width * (baseHeight / iPhone5Resolution.height)); 100 | // Calculated based of the short and wide (aspect ratio wise) iOS device, the iPad (1024 x 768) 101 | maxHeight = Math.round(iPadResolution.height * (baseWidth / iPadResolution.width)); 102 | // Calculated based of the middle (aspect ratio wise) iOS device, the iPhone 4 (960 x 640) 103 | midHeight = Math.round(iPhone4Resolution.height * (baseWidth / iPhone4Resolution.width)); 104 | 105 | image = PNGImage.createImage(maxWidth, maxHeight); 106 | 107 | var maxWidthDifferenceHalf = Math.round((maxWidth - baseWidth) * 0.5); 108 | var maxHeightDifferenceHalf = Math.round((maxHeight - baseHeight) * 0.5); 109 | var midHeightDifferenceHalf = Math.round((midHeight - baseHeight) * 0.5); 110 | 111 | // Draw green 16:10 (safe) area 112 | image.fillRect(maxWidthDifferenceHalf, maxHeightDifferenceHalf, baseWidth, baseHeight, greenColor); 113 | 114 | // Draw blue 16:9 area 115 | image.fillRect(0, maxHeightDifferenceHalf, maxWidthDifferenceHalf, baseHeight, blueColor); 116 | image.fillRect(maxWidthDifferenceHalf + baseWidth, maxHeightDifferenceHalf, maxWidthDifferenceHalf, baseHeight, blueColor); 117 | 118 | // Draw yellow 3:2 area 119 | image.fillRect(maxWidthDifferenceHalf, maxHeightDifferenceHalf - midHeightDifferenceHalf, baseWidth, midHeightDifferenceHalf, yellowColor); 120 | image.fillRect(maxWidthDifferenceHalf, maxHeightDifferenceHalf + baseHeight, baseWidth, midHeightDifferenceHalf, yellowColor); 121 | 122 | // Draw red 4:3 area 123 | image.fillRect(maxWidthDifferenceHalf, 0, baseWidth, maxHeightDifferenceHalf - midHeightDifferenceHalf, redColor); 124 | image.fillRect(maxWidthDifferenceHalf, maxHeightDifferenceHalf + baseHeight + midHeightDifferenceHalf, baseWidth, maxHeightDifferenceHalf, redColor); 125 | 126 | // Draw grey unseen corners 127 | image.fillRect(0, 0, maxWidthDifferenceHalf, maxHeightDifferenceHalf, greyColor); 128 | image.fillRect(0, maxHeightDifferenceHalf + baseHeight, maxWidthDifferenceHalf, maxHeightDifferenceHalf, greyColor); 129 | image.fillRect(maxWidthDifferenceHalf + baseWidth, 0, maxWidthDifferenceHalf, maxHeightDifferenceHalf, greyColor); 130 | image.fillRect(maxWidthDifferenceHalf + baseWidth, maxHeightDifferenceHalf + baseHeight, maxWidthDifferenceHalf, maxHeightDifferenceHalf, greyColor); 131 | } else { 132 | if (baseWidth === -1) { 133 | baseWidth = defaultWidth; 134 | maxWidth = baseWidth; 135 | } 136 | 137 | if (baseHeight === -1) { 138 | baseHeight = defaultHeight; 139 | maxHeight = baseHeight; 140 | } 141 | 142 | if (baseWidth < baseHeight) { 143 | var temp = baseHeight; 144 | baseHeight = baseWidth; 145 | baseWidth = temp; 146 | 147 | maxWidth = baseWidth; 148 | maxHeight = baseHeight; 149 | 150 | isPortrait = true; 151 | } 152 | 153 | image = PNGImage.createImage(maxWidth, maxHeight); 154 | image.fillRect(0, 0, maxWidth, maxHeight, greenColor); 155 | } 156 | 157 | if (isPortrait) { 158 | var temp = baseHeight; 159 | baseHeight = baseWidth; 160 | baseWidth = temp; 161 | 162 | var temp = maxHeight; 163 | maxHeight = maxWidth; 164 | maxWidth = temp; 165 | } 166 | 167 | shell.ls('webpack.*.config.js').forEach(function (file) { 168 | shell.sed('-i', /\/\*\[\[DEFAULT_GAME_WIDTH\*\/\S*\/\*DEFAULT_GAME_WIDTH\]\]\*\//, '/*[[DEFAULT_GAME_WIDTH*/' + baseWidth + '/*DEFAULT_GAME_WIDTH]]*/', file); 169 | shell.sed('-i', /\/\*\[\[DEFAULT_GAME_HEIGHT\*\/\S*\/\*DEFAULT_GAME_HEIGHT\]\]\*\//, '/*[[DEFAULT_GAME_HEIGHT*/' + baseHeight + '/*DEFAULT_GAME_HEIGHT]]*/', file); 170 | shell.sed('-i', /\/\*\[\[MAX_GAME_WIDTH\*\/\S*\/\*MAX_GAME_WIDTH\]\]\*\//, '/*[[MAX_GAME_WIDTH*/' + maxWidth + '/*MAX_GAME_WIDTH]]*/', file); 171 | shell.sed('-i', /\/\*\[\[MAX_GAME_HEIGHT\*\/\S*\/\*MAX_GAME_HEIGHT\]\]\*\//, '/*[[MAX_GAME_HEIGHT*/' + maxHeight + '/*MAX_GAME_HEIGHT]]*/', file); 172 | shell.sed('-i', /\/\*\[\[SCALE_MODE\*\/\S*\/\*SCALE_MODE\]\]\*\//, '/*[[SCALE_MODE*/\'' + scaleMode + '\'/*SCALE_MODE]]*/', file); 173 | }); 174 | 175 | shell.ls('electron-main.js').forEach(function (file) { 176 | shell.sed('-i', /\/\*\[\[DEFAULT_GAME_WIDTH\*\/\S*\/\*DEFAULT_GAME_WIDTH\]\]\*\//, '/*[[DEFAULT_GAME_WIDTH*/' + baseWidth + '/*DEFAULT_GAME_WIDTH]]*/', file); 177 | shell.sed('-i', /\/\*\[\[DEFAULT_GAME_HEIGHT\*\/\S*\/\*DEFAULT_GAME_HEIGHT\]\]\*\//, '/*[[DEFAULT_GAME_HEIGHT*/' + baseHeight + '/*DEFAULT_GAME_HEIGHT]]*/', file); 178 | }); 179 | 180 | if (!commander.noPng) { 181 | shell.mkdir('-p', 'assets/images/'); 182 | 183 | image.writeImage(filename, function (error) { 184 | if (!error && isPortrait) { 185 | var bitmap = new ImageJS.Bitmap(); 186 | bitmap.readFile(filename).then(function() { 187 | var rotatedBitmap = bitmap.rotate({ degrees: 90, fit: "custom", width: maxWidth, height: maxHeight }); 188 | rotatedBitmap.writeFile(filename); 189 | }); 190 | } 191 | }); 192 | } 193 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import 'p2'; 2 | import 'pixi'; 3 | import 'phaser'; 4 | 5 | import * as WebFontLoader from 'webfontloader'; 6 | 7 | import Boot from './states/boot'; 8 | import Preloader from './states/preloader'; 9 | import Title from './states/title'; 10 | import * as Utils from './utils/utils'; 11 | import * as Assets from './assets'; 12 | 13 | class App extends Phaser.Game { 14 | constructor(config: Phaser.IGameConfig) { 15 | super (config); 16 | 17 | this.state.add('boot', Boot); 18 | this.state.add('preloader', Preloader); 19 | this.state.add('title', Title); 20 | 21 | this.state.start('boot'); 22 | } 23 | } 24 | 25 | function startApp(): void { 26 | let gameWidth: number = DEFAULT_GAME_WIDTH; 27 | let gameHeight: number = DEFAULT_GAME_HEIGHT; 28 | 29 | if (SCALE_MODE === 'USER_SCALE') { 30 | let screenMetrics: Utils.ScreenMetrics = Utils.ScreenUtils.calculateScreenMetrics(gameWidth, gameHeight, MAX_GAME_WIDTH, MAX_GAME_HEIGHT); 31 | 32 | gameWidth = screenMetrics.gameWidth; 33 | gameHeight = screenMetrics.gameHeight; 34 | } 35 | 36 | // There are a few more options you can set if needed, just take a look at Phaser.IGameConfig 37 | let gameConfig: Phaser.IGameConfig = { 38 | width: gameWidth, 39 | height: gameHeight, 40 | renderer: Phaser.AUTO, 41 | parent: '', 42 | resolution: 1 43 | }; 44 | 45 | let app = new App(gameConfig); 46 | } 47 | 48 | window.onload = () => { 49 | let webFontLoaderOptions: any = null; 50 | let webFontsToLoad: string[] = GOOGLE_WEB_FONTS; 51 | 52 | if (webFontsToLoad.length > 0) { 53 | webFontLoaderOptions = (webFontLoaderOptions || {}); 54 | 55 | webFontLoaderOptions.google = { 56 | families: webFontsToLoad 57 | }; 58 | } 59 | 60 | if (Object.keys(Assets.CustomWebFonts).length > 0) { 61 | webFontLoaderOptions = (webFontLoaderOptions || {}); 62 | 63 | webFontLoaderOptions.custom = { 64 | families: [], 65 | urls: [] 66 | }; 67 | 68 | let allCustomWebFonts = (Assets.CustomWebFonts as any); 69 | 70 | for (let font in allCustomWebFonts) { 71 | webFontLoaderOptions.custom.families.push(allCustomWebFonts[font].getFamily()); 72 | webFontLoaderOptions.custom.urls.push(allCustomWebFonts[font].getCSS()); 73 | } 74 | } 75 | 76 | if (webFontLoaderOptions === null) { 77 | // Just start the game, we don't need any additional fonts 78 | startApp(); 79 | } else { 80 | // Load the fonts defined in webFontsToLoad from Google Web Fonts, and/or any Local Fonts then start the game knowing the fonts are available 81 | webFontLoaderOptions.active = startApp; 82 | 83 | WebFontLoader.load(webFontLoaderOptions); 84 | } 85 | }; 86 | -------------------------------------------------------------------------------- /src/assets.ts: -------------------------------------------------------------------------------- 1 | /* AUTO GENERATED FILE. DO NOT MODIFY. YOU WILL LOSE YOUR CHANGES ON BUILD. */ 2 | 3 | export namespace Images { 4 | export class ImagesBackgroundTemplate { 5 | static getName(): string { return 'background_template'; } 6 | 7 | static getPNG(): string { return require('assets/images/background_template.png'); } 8 | } 9 | } 10 | 11 | export namespace Spritesheets { 12 | export class SpritesheetsMetalslugMummy374518 { 13 | static getName(): string { return 'metalslug_mummy.[37,45,18,0,0]'; } 14 | 15 | static getPNG(): string { return require('assets/spritesheets/metalslug_mummy.[37,45,18,0,0].png'); } 16 | static getFrameWidth(): number { return 37; } 17 | static getFrameHeight(): number { return 45; } 18 | static getFrameMax(): number { return 18; } 19 | static getMargin(): number { return 0; } 20 | static getSpacing(): number { return 0; } 21 | } 22 | } 23 | 24 | export namespace Atlases { 25 | export class AtlasesPreloadSpritesArray { 26 | static getName(): string { return 'preload_sprites_array'; } 27 | 28 | static getJSONArray(): string { return require('assets/atlases/preload_sprites_array.json'); } 29 | 30 | static getPNG(): string { return require('assets/atlases/preload_sprites_array.png'); } 31 | } 32 | export namespace AtlasesPreloadSpritesArray { 33 | export enum Frames { 34 | PreloadBar = 'preload_bar.png', 35 | PreloadFrame = 'preload_frame.png', 36 | } 37 | } 38 | export class AtlasesPreloadSpritesHash { 39 | static getName(): string { return 'preload_sprites_hash'; } 40 | 41 | static getJSONHash(): string { return require('assets/atlases/preload_sprites_hash.json'); } 42 | 43 | static getPNG(): string { return require('assets/atlases/preload_sprites_hash.png'); } 44 | } 45 | export namespace AtlasesPreloadSpritesHash { 46 | export enum Frames { 47 | PreloadBar = 'preload_bar.png', 48 | PreloadFrame = 'preload_frame.png', 49 | } 50 | } 51 | export class AtlasesPreloadSpritesXml { 52 | static getName(): string { return 'preload_sprites_xml'; } 53 | 54 | static getPNG(): string { return require('assets/atlases/preload_sprites_xml.png'); } 55 | 56 | static getXML(): string { return require('assets/atlases/preload_sprites_xml.xml'); } 57 | } 58 | export namespace AtlasesPreloadSpritesXml { 59 | export enum Frames { 60 | PreloadBar = 'preload_bar.png', 61 | PreloadFrame = 'preload_frame.png', 62 | } 63 | } 64 | } 65 | 66 | export namespace Audio { 67 | export class AudioMusic { 68 | static getName(): string { return 'music'; } 69 | 70 | static getAC3(): string { return require('assets/audio/music.ac3'); } 71 | static getM4A(): string { return require('assets/audio/music.m4a'); } 72 | static getMP3(): string { return require('assets/audio/music.mp3'); } 73 | static getOGG(): string { return require('assets/audio/music.ogg'); } 74 | } 75 | } 76 | 77 | export namespace Audiosprites { 78 | export class AudiospritesSfx { 79 | static getName(): string { return 'sfx'; } 80 | 81 | static getAC3(): string { return require('assets/audiosprites/sfx.ac3'); } 82 | static getJSON(): string { return require('assets/audiosprites/sfx.json'); } 83 | static getM4A(): string { return require('assets/audiosprites/sfx.m4a'); } 84 | static getMP3(): string { return require('assets/audiosprites/sfx.mp3'); } 85 | static getOGG(): string { return require('assets/audiosprites/sfx.ogg'); } 86 | } 87 | export namespace AudiospritesSfx { 88 | export enum Sprites { 89 | Laser1 = 'laser1', 90 | Laser2 = 'laser2', 91 | Laser3 = 'laser3', 92 | Laser4 = 'laser4', 93 | Laser5 = 'laser5', 94 | Laser6 = 'laser6', 95 | Laser7 = 'laser7', 96 | Laser8 = 'laser8', 97 | Laser9 = 'laser9', 98 | } 99 | } 100 | } 101 | 102 | export namespace GoogleWebFonts { 103 | export const Barrio: string = 'Barrio'; 104 | } 105 | 106 | export namespace CustomWebFonts { 107 | export class Fonts2DumbWebfont { 108 | static getName(): string { return '2Dumb-webfont'; } 109 | 110 | static getFamily(): string { return '2dumbregular'; } 111 | 112 | static getCSS(): string { return require('!file-loader?name=assets/fonts/[name].[ext]!assets/fonts/2Dumb-webfont.css'); } 113 | static getEOT(): string { return require('!file-loader?name=assets/fonts/[name].[ext]!assets/fonts/2Dumb-webfont.eot'); } 114 | static getSVG(): string { return require('!file-loader?name=assets/fonts/[name].[ext]!assets/fonts/2Dumb-webfont.svg'); } 115 | static getTTF(): string { return require('!file-loader?name=assets/fonts/[name].[ext]!assets/fonts/2Dumb-webfont.ttf'); } 116 | static getWOFF(): string { return require('!file-loader?name=assets/fonts/[name].[ext]!assets/fonts/2Dumb-webfont.woff'); } 117 | } 118 | } 119 | 120 | export namespace BitmapFonts { 121 | export class FontsFontFnt { 122 | static getName(): string { return 'font_fnt'; } 123 | 124 | static getFNT(): string { return require('assets/fonts/font_fnt.fnt'); } 125 | static getPNG(): string { return require('assets/fonts/font_fnt.png'); } 126 | } 127 | export class FontsFontXml { 128 | static getName(): string { return 'font_xml'; } 129 | 130 | static getPNG(): string { return require('assets/fonts/font_xml.png'); } 131 | static getXML(): string { return require('assets/fonts/font_xml.xml'); } 132 | } 133 | } 134 | 135 | export namespace JSON { 136 | class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {} 137 | } 138 | 139 | export namespace TilemapJSON { 140 | export class TilemapsTilemap { 141 | static getName(): string { return 'tilemap'; } 142 | 143 | static getJSON(): string { return require('assets/tilemaps/tilemap.json'); } 144 | } 145 | } 146 | 147 | export namespace XML { 148 | class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {} 149 | } 150 | 151 | export namespace Text { 152 | class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {} 153 | } 154 | 155 | export namespace Scripts { 156 | export class ScriptsBlurX { 157 | static getName(): string { return 'BlurX'; } 158 | 159 | static getJS(): string { return require('assets/scripts/BlurX.js'); } 160 | } 161 | export class ScriptsBlurY { 162 | static getName(): string { return 'BlurY'; } 163 | 164 | static getJS(): string { return require('assets/scripts/BlurY.js'); } 165 | } 166 | } 167 | export namespace Shaders { 168 | export class ShadersPixelate { 169 | static getName(): string { return 'pixelate'; } 170 | 171 | static getFRAG(): string { return require('assets/shaders/pixelate.frag'); } 172 | } 173 | } 174 | export namespace Misc { 175 | class IExistSoTypeScriptWillNotComplainAboutAnEmptyNamespace {} 176 | } 177 | -------------------------------------------------------------------------------- /src/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare let DEBUG: boolean; 2 | declare let DEFAULT_GAME_WIDTH: number; 3 | declare let DEFAULT_GAME_HEIGHT: number; 4 | declare let MAX_GAME_WIDTH: number; 5 | declare let MAX_GAME_HEIGHT: number; 6 | declare let SCALE_MODE: string; 7 | declare let GOOGLE_WEB_FONTS: string[]; 8 | declare let SOUND_EXTENSIONS_PREFERENCE: string[]; 9 | -------------------------------------------------------------------------------- /src/states/boot.ts: -------------------------------------------------------------------------------- 1 | import * as Utils from '../utils/utils'; 2 | import * as Assets from '../assets'; 3 | 4 | export default class Boot extends Phaser.State { 5 | public preload(): void { 6 | // Load any assets you need for your preloader state here. 7 | this.game.load.atlasJSONArray(Assets.Atlases.AtlasesPreloadSpritesArray.getName(), Assets.Atlases.AtlasesPreloadSpritesArray.getPNG(), Assets.Atlases.AtlasesPreloadSpritesArray.getJSONArray()); 8 | // this.game.load.atlasJSONHash(Assets.Atlases.AtlasesPreloadSpritesHash.getName(), Assets.Atlases.AtlasesPreloadSpritesHash.getPNG(), Assets.Atlases.AtlasesPreloadSpritesHash.getJSONHash()); 9 | // this.game.load.atlasXML(Assets.Atlases.AtlasesPreloadSpritesXml.getName(), Assets.Atlases.AtlasesPreloadSpritesXml.getPNG(), Assets.Atlases.AtlasesPreloadSpritesXml.getXML()); 10 | } 11 | 12 | public create(): void { 13 | // Do anything here that you need to be setup immediately, before the game actually starts doing anything. 14 | 15 | // Uncomment the following to disable multitouch 16 | // this.input.maxPointers = 1; 17 | 18 | this.game.scale.scaleMode = (Phaser.ScaleManager as any)[SCALE_MODE]; 19 | 20 | if (SCALE_MODE === 'USER_SCALE') { 21 | let screenMetrics: Utils.ScreenMetrics = Utils.ScreenUtils.screenMetrics; 22 | 23 | this.game.scale.setUserScale(screenMetrics.scaleX, screenMetrics.scaleY); 24 | } 25 | 26 | this.game.scale.pageAlignHorizontally = true; 27 | this.game.scale.pageAlignVertically = true; 28 | 29 | if (this.game.device.desktop) { 30 | // Any desktop specific stuff here 31 | } else { 32 | // Any mobile specific stuff here 33 | } 34 | 35 | // Use DEBUG to wrap code that should only be included in a DEBUG build of the game 36 | // DEFAULT_GAME_WIDTH is the safe area width of the game 37 | // DEFAULT_GAME_HEIGHT is the safe area height of the game 38 | // MAX_GAME_WIDTH is the max width of the game 39 | // MAX_GAME_HEIGHT is the max height of the game 40 | // game.width is the actual width of the game 41 | // game.height is the actual height of the game 42 | // GOOGLE_WEB_FONTS are the fonts to be loaded from Google Web Fonts 43 | // SOUND_EXTENSIONS_PREFERENCE is the most preferred to least preferred order to look for audio sources 44 | console.log( 45 | `DEBUG....................... ${DEBUG} 46 | \nSCALE_MODE.................. ${SCALE_MODE} 47 | \nDEFAULT_GAME_WIDTH.......... ${DEFAULT_GAME_WIDTH} 48 | \nDEFAULT_GAME_HEIGHT......... ${DEFAULT_GAME_HEIGHT} 49 | \nMAX_GAME_WIDTH.............. ${MAX_GAME_WIDTH} 50 | \nMAX_GAME_HEIGHT............. ${MAX_GAME_HEIGHT} 51 | \ngame.width.................. ${this.game.width} 52 | \ngame.height................. ${this.game.height} 53 | \nGOOGLE_WEB_FONTS............ ${GOOGLE_WEB_FONTS} 54 | \nSOUND_EXTENSIONS_PREFERENCE. ${SOUND_EXTENSIONS_PREFERENCE}` 55 | ); 56 | 57 | this.game.state.start('preloader'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/states/preloader.ts: -------------------------------------------------------------------------------- 1 | import * as Assets from '../assets'; 2 | import * as AssetUtils from '../utils/assetUtils'; 3 | 4 | export default class Preloader extends Phaser.State { 5 | private preloadBarSprite!: Phaser.Sprite; 6 | private preloadFrameSprite!: Phaser.Sprite; 7 | 8 | public preload(): void { 9 | // Setup your loading screen and preload sprite (if you want a loading progress indicator) here 10 | 11 | this.preloadBarSprite = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, Assets.Atlases.AtlasesPreloadSpritesArray.getName(), Assets.Atlases.AtlasesPreloadSpritesArray.Frames.PreloadBar); 12 | // this.preloadBarSprite = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, Assets.Atlases.AtlasesPreloadSpritesHash.getName(), Assets.Atlases.AtlasesPreloadSpritesHash.Frames.PreloadBar); 13 | // this.preloadBarSprite = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, Assets.Atlases.AtlasesPreloadSpritesXml.getName(), Assets.Atlases.AtlasesPreloadSpritesXml.Frames.PreloadBar); 14 | this.preloadBarSprite.anchor.setTo(0, 0.5); 15 | this.preloadBarSprite.x -= this.preloadBarSprite.width * 0.5; 16 | 17 | this.preloadFrameSprite = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, Assets.Atlases.AtlasesPreloadSpritesArray.getName(), Assets.Atlases.AtlasesPreloadSpritesArray.Frames.PreloadFrame); 18 | // this.preloadFrameSprite = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, Assets.Atlases.AtlasesPreloadSpritesHash.getName(), Assets.Atlases.AtlasesPreloadSpritesHash.Frames.PreloadFrame); 19 | // this.preloadFrameSprite = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, Assets.Atlases.AtlasesPreloadSpritesXml.getName(), Assets.Atlases.AtlasesPreloadSpritesXml.Frames.PreloadFrame); 20 | this.preloadFrameSprite.anchor.setTo(0.5); 21 | 22 | this.game.load.setPreloadSprite(this.preloadBarSprite); 23 | 24 | AssetUtils.Loader.loadAllAssets(this.game, this.waitForSoundDecoding, this); 25 | } 26 | 27 | private waitForSoundDecoding(): void { 28 | AssetUtils.Loader.waitForSoundDecoding(this.startGame, this); 29 | } 30 | 31 | private startGame(): void { 32 | this.game.camera.onFadeComplete.addOnce(this.loadTitle, this); 33 | this.game.camera.fade(0x000000, 1000); 34 | } 35 | 36 | private loadTitle(): void { 37 | this.game.state.start('title'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/states/title.ts: -------------------------------------------------------------------------------- 1 | import * as Assets from '../assets'; 2 | 3 | export default class Title extends Phaser.State { 4 | private backgroundTemplateSprite!: Phaser.Sprite; 5 | private googleFontText!: Phaser.Text; 6 | private localFontText!: Phaser.Text; 7 | private pixelateShader!: Phaser.Filter; 8 | private bitmapFontText!: Phaser.BitmapText; 9 | private blurXFilter!: Phaser.Filter.BlurX; 10 | private blurYFilter!: Phaser.Filter.BlurY; 11 | private sfxAudiosprite!: Phaser.AudioSprite; 12 | private mummySpritesheet!: Phaser.Sprite; 13 | private sfxLaserSounds!: Assets.Audiosprites.AudiospritesSfx.Sprites[]; 14 | 15 | public create(): void { 16 | this.backgroundTemplateSprite = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, Assets.Images.ImagesBackgroundTemplate.getName()); 17 | this.backgroundTemplateSprite.anchor.setTo(0.5); 18 | 19 | this.googleFontText = this.game.add.text(this.game.world.centerX, this.game.world.centerY - 100, 'Google Web Fonts', { 20 | font: '50px ' + Assets.GoogleWebFonts.Barrio 21 | }); 22 | this.googleFontText.anchor.setTo(0.5); 23 | 24 | this.localFontText = this.game.add.text(this.game.world.centerX, this.game.world.centerY, 'Local Fonts + Shaders .frag (Pixelate here)!', { 25 | font: '30px ' + Assets.CustomWebFonts.Fonts2DumbWebfont.getFamily() 26 | }); 27 | this.localFontText.anchor.setTo(0.5); 28 | 29 | this.pixelateShader = new Phaser.Filter(this.game, null, this.game.cache.getShader(Assets.Shaders.ShadersPixelate.getName())); 30 | this.localFontText.filters = [this.pixelateShader]; 31 | 32 | this.bitmapFontText = this.game.add.bitmapText(this.game.world.centerX, this.game.world.centerY + 100, Assets.BitmapFonts.FontsFontFnt.getName(), 'Bitmap Fonts + Filters .js (Blur here)!', 40); 33 | this.bitmapFontText.anchor.setTo(0.5); 34 | 35 | this.blurXFilter = this.game.add.filter(Assets.Scripts.ScriptsBlurX.getName()) as Phaser.Filter.BlurX; 36 | this.blurXFilter.blur = 8; 37 | this.blurYFilter = this.game.add.filter(Assets.Scripts.ScriptsBlurY.getName()) as Phaser.Filter.BlurY; 38 | this.blurYFilter.blur = 2; 39 | 40 | this.bitmapFontText.filters = [this.blurXFilter, this.blurYFilter]; 41 | 42 | this.mummySpritesheet = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY + 175, Assets.Spritesheets.SpritesheetsMetalslugMummy374518.getName()); 43 | this.mummySpritesheet.animations.add('walk'); 44 | this.mummySpritesheet.animations.play('walk', 30, true); 45 | 46 | this.sfxAudiosprite = this.game.add.audioSprite(Assets.Audiosprites.AudiospritesSfx.getName()); 47 | 48 | // This is an example of how you can lessen the verbosity 49 | let availableSFX = Assets.Audiosprites.AudiospritesSfx.Sprites; 50 | this.sfxLaserSounds = [ 51 | availableSFX.Laser1, 52 | availableSFX.Laser2, 53 | availableSFX.Laser3, 54 | availableSFX.Laser4, 55 | availableSFX.Laser5, 56 | availableSFX.Laser6, 57 | availableSFX.Laser7, 58 | availableSFX.Laser8, 59 | availableSFX.Laser9 60 | ]; 61 | 62 | this.game.sound.play(Assets.Audio.AudioMusic.getName(), 0.2, true); 63 | 64 | this.backgroundTemplateSprite.inputEnabled = true; 65 | this.backgroundTemplateSprite.events.onInputDown.add(() => { 66 | this.sfxAudiosprite.play(Phaser.ArrayUtils.getRandomItem(this.sfxLaserSounds)); 67 | }); 68 | 69 | this.game.camera.flash(0x000000, 1000); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/utils/assetUtils.ts: -------------------------------------------------------------------------------- 1 | import * as Assets from '../assets'; 2 | 3 | export class Loader { 4 | private static game: Phaser.Game; 5 | private static soundKeys: string[] = []; 6 | private static soundExtensionsPreference: string[] = SOUND_EXTENSIONS_PREFERENCE; 7 | 8 | private static loadImages() { 9 | let allImages = (Assets.Images as any); 10 | 11 | for (let image in allImages) { 12 | if (!this.game.cache.checkImageKey(allImages[image].getName())) { 13 | for (let option of Object.getOwnPropertyNames(allImages[image])) { 14 | if (option !== 'getName' && option.includes('get')) { 15 | this.game.load.image(allImages[image].getName(), allImages[image][option]()); 16 | } 17 | } 18 | } 19 | } 20 | } 21 | 22 | private static loadSpritesheets() { 23 | let allSpritesheets = (Assets.Spritesheets as any); 24 | 25 | for (let spritesheet in allSpritesheets) { 26 | if (!this.game.cache.checkImageKey(allSpritesheets[spritesheet].getName())) { 27 | let imageOption: string = ''; 28 | 29 | for (let option of Object.getOwnPropertyNames(allSpritesheets[spritesheet])) { 30 | if (option !== 'getName' && option !== 'getFrameWidth' && option !== 'getFrameHeight' && option !== 'getFrameMax' && option !== 'getMargin' && option !== 'getSpacing' && option.includes('get')) { 31 | imageOption = option; 32 | } 33 | } 34 | 35 | this.game.load.spritesheet(allSpritesheets[spritesheet].getName(), allSpritesheets[spritesheet][imageOption](), allSpritesheets[spritesheet].getFrameWidth(), allSpritesheets[spritesheet].getFrameHeight(), allSpritesheets[spritesheet].getFrameMax(), allSpritesheets[spritesheet].getMargin(), allSpritesheets[spritesheet].getSpacing()); 36 | } 37 | } 38 | } 39 | 40 | private static loadAtlases() { 41 | let allAtlases = (Assets.Atlases as any); 42 | 43 | for (let atlas in allAtlases) { 44 | if (!this.game.cache.checkImageKey(allAtlases[atlas].getName())) { 45 | let imageOption: string = ''; 46 | let dataOption: string = ''; 47 | 48 | for (let option of Object.getOwnPropertyNames(allAtlases[atlas])) { 49 | if ((option === 'getXML' || option === 'getJSONArray' || option === 'getJSONHash') && option.includes('get')) { 50 | dataOption = option; 51 | } else if (option !== 'getName' && option !== 'Frames' && option.includes('get')) { 52 | imageOption = option; 53 | } 54 | } 55 | 56 | if (dataOption === 'getXML') { 57 | this.game.load.atlasXML(allAtlases[atlas].getName(), allAtlases[atlas][imageOption](), allAtlases[atlas].getXML()); 58 | } else if (dataOption === 'getJSONArray') { 59 | this.game.load.atlasJSONArray(allAtlases[atlas].getName(), allAtlases[atlas][imageOption](), allAtlases[atlas].getJSONArray()); 60 | } else if (dataOption === 'getJSONHash') { 61 | this.game.load.atlasJSONHash(allAtlases[atlas].getName(), allAtlases[atlas][imageOption](), allAtlases[atlas].getJSONHash()); 62 | } 63 | } 64 | } 65 | } 66 | 67 | private static orderAudioSourceArrayBasedOnSoundExtensionPreference(soundSourceArray: string[]): string[] { 68 | let orderedSoundSourceArray: string[] = []; 69 | 70 | for (let e in this.soundExtensionsPreference) { 71 | let sourcesWithExtension: string[] = soundSourceArray.filter((el) => { 72 | return (el.substring(el.lastIndexOf('.') + 1, el.length) === this.soundExtensionsPreference[e]); 73 | }); 74 | 75 | orderedSoundSourceArray = orderedSoundSourceArray.concat(sourcesWithExtension); 76 | } 77 | 78 | return orderedSoundSourceArray; 79 | } 80 | 81 | private static loadAudio() { 82 | let allAudio = (Assets.Audio as any); 83 | 84 | for (let audio in allAudio) { 85 | let soundName = allAudio[audio].getName(); 86 | this.soundKeys.push(soundName); 87 | 88 | if (!this.game.cache.checkSoundKey(soundName)) { 89 | let audioTypeArray = []; 90 | 91 | for (let option of Object.getOwnPropertyNames(allAudio[audio])) { 92 | if (option !== 'getName' && option.includes('get')) { 93 | audioTypeArray.push(allAudio[audio][option]()); 94 | } 95 | } 96 | 97 | audioTypeArray = this.orderAudioSourceArrayBasedOnSoundExtensionPreference(audioTypeArray); 98 | 99 | this.game.load.audio(soundName, audioTypeArray, true); 100 | } 101 | } 102 | } 103 | 104 | private static loadAudiosprites() { 105 | let allAudiosprites = (Assets.Audiosprites as any); 106 | 107 | for (let audio in allAudiosprites) { 108 | let soundName = allAudiosprites[audio].getName(); 109 | this.soundKeys.push(soundName); 110 | 111 | if (!this.game.cache.checkSoundKey(soundName)) { 112 | let audioTypeArray = []; 113 | 114 | for (let option of Object.getOwnPropertyNames(allAudiosprites[audio])) { 115 | if (option !== 'getName' && option !== 'getJSON' && option !== 'Sprites' && option.includes('get')) { 116 | audioTypeArray.push(allAudiosprites[audio][option]()); 117 | } 118 | } 119 | 120 | audioTypeArray = this.orderAudioSourceArrayBasedOnSoundExtensionPreference(audioTypeArray); 121 | 122 | this.game.load.audiosprite(soundName, audioTypeArray, allAudiosprites[audio].getJSON(), null, true); 123 | } 124 | } 125 | } 126 | 127 | private static loadBitmapFonts() { 128 | let allBitmapFonts = (Assets.BitmapFonts as any); 129 | 130 | for (let font in allBitmapFonts) { 131 | if (!this.game.cache.checkBitmapFontKey(allBitmapFonts[font].getName())) { 132 | let imageOption: string = ''; 133 | let dataOption: string = ''; 134 | 135 | for (let option of Object.getOwnPropertyNames(allBitmapFonts[font])) { 136 | if ((option === 'getXML' || option === 'getFNT') && option.includes('get')) { 137 | dataOption = option; 138 | } else if (option !== 'getName' && option.includes('get')) { 139 | imageOption = option; 140 | } 141 | } 142 | 143 | this.game.load.bitmapFont(allBitmapFonts[font].getName(), allBitmapFonts[font][imageOption](), allBitmapFonts[font][dataOption]()); 144 | } 145 | } 146 | } 147 | 148 | private static loadJSON() { 149 | let allJSON = (Assets.JSON as any); 150 | 151 | for (let json in allJSON) { 152 | if (!this.game.cache.checkJSONKey(allJSON[json].getName())) { 153 | this.game.load.json(allJSON[json].getName(), allJSON[json].getJSON(), true); 154 | } 155 | } 156 | } 157 | 158 | private static loadTilemapJSON() { 159 | let allTilemapJSON = (Assets.TilemapJSON as any); 160 | 161 | for (let json in allTilemapJSON) { 162 | if (!this.game.cache.checkTilemapKey(allTilemapJSON[json].getName())) { 163 | this.game.load.tilemap(allTilemapJSON[json].getName(), allTilemapJSON[json].getJSON(), null, Phaser.Tilemap.TILED_JSON); 164 | } 165 | } 166 | } 167 | 168 | private static loadXML() { 169 | let allXML = (Assets.XML as any); 170 | 171 | for (let xml in allXML) { 172 | if (!this.game.cache.checkXMLKey(allXML[xml].getName())) { 173 | this.game.load.xml(allXML[xml].getName(), allXML[xml].getXML(), true); 174 | } 175 | } 176 | } 177 | 178 | private static loadText() { 179 | let allText = (Assets.Text as any); 180 | 181 | for (let text in allText) { 182 | if (!this.game.cache.checkTextKey(allText[text].getName())) { 183 | this.game.load.text(allText[text].getName(), allText[text].getTXT(), true); 184 | } 185 | } 186 | } 187 | 188 | private static loadScripts() { 189 | let allScripts = (Assets.Scripts as any); 190 | 191 | for (let script in Assets.Scripts) { 192 | this.game.load.script(allScripts[script].getName(), allScripts[script].getJS()); 193 | } 194 | } 195 | 196 | private static loadShaders() { 197 | let allShaders = (Assets.Shaders as any); 198 | 199 | for (let shader in allShaders) { 200 | if (!this.game.cache.checkShaderKey(allShaders[shader].getName())) { 201 | this.game.load.shader(allShaders[shader].getName(), allShaders[shader].getFRAG(), true); 202 | } 203 | } 204 | } 205 | 206 | private static loadMisc() { 207 | let allMisc = (Assets.Misc as any); 208 | 209 | for (let misc in allMisc) { 210 | if (!this.game.cache.checkBinaryKey(allMisc[misc].getName())) { 211 | this.game.load.binary(allMisc[misc].getName(), allMisc[misc].getFile()); 212 | } 213 | } 214 | } 215 | 216 | public static loadAllAssets(game: Phaser.Game, onComplete?: Function, onCompleteContext?: any) { 217 | this.game = game; 218 | 219 | if (onComplete) { 220 | this.game.load.onLoadComplete.addOnce(onComplete, onCompleteContext); 221 | } 222 | 223 | this.loadImages(); 224 | this.loadSpritesheets(); 225 | this.loadAtlases(); 226 | this.loadAudio(); 227 | this.loadAudiosprites(); 228 | this.loadBitmapFonts(); 229 | this.loadJSON(); 230 | this.loadTilemapJSON(); 231 | this.loadXML(); 232 | this.loadText(); 233 | this.loadScripts(); 234 | this.loadShaders(); 235 | this.loadMisc(); 236 | 237 | if ((this.game.load as any)._fileList.length === 0) { 238 | this.game.load.onLoadComplete.dispatch(); 239 | } 240 | } 241 | 242 | public static waitForSoundDecoding(onComplete: Function, onCompleteContext?: any) { 243 | if (this.soundKeys.length > 0) { 244 | this.game.sound.setDecodedCallback(this.soundKeys, onComplete, onCompleteContext); 245 | } else { 246 | onComplete.call(onCompleteContext); 247 | } 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | The Screen[...] classes are modified versions of the classes in an article I found. 3 | 4 | Author: Tomáš Rychnovský 5 | Article: http://sbcgamesdev.blogspot.ca/2015/04/phaser-tutorial-manage-different-screen.html 6 | Date: Thursday, April 9, 2015 7 | 8 | Big thanks to Tomáš! 9 | */ 10 | 11 | export enum ScreenOrientation { 12 | PORTRAIT, 13 | LANDSCAPE 14 | } 15 | 16 | export class ScreenMetrics { 17 | windowWidth!: number; 18 | windowHeight!: number; 19 | 20 | defaultGameWidth!: number; 21 | defaultGameHeight!: number; 22 | 23 | maxGameWidth!: number; 24 | maxGameHeight!: number; 25 | 26 | gameWidth!: number; 27 | gameHeight!: number; 28 | 29 | scaleX!: number; 30 | scaleY!: number; 31 | 32 | offsetX!: number; 33 | offsetY!: number; 34 | } 35 | 36 | export class ScreenUtils { 37 | public static screenMetrics: ScreenMetrics; 38 | 39 | public static calculateScreenMetrics(defaultWidth: number, defaultHeight: number, maxGameWidth?: number, maxGameHeight?: number): ScreenMetrics { 40 | // Just to give some explanation as to the numbers and colors in the included background; 41 | // The GREEN is the safe area and will be displayed fully on any device and is based on 16:10 aspect ratio, build your actual gameplay here 42 | // The YELLOW is the extra area that will be visible on devices with a 3:2 aspect ratio (iPhone 4S and below) 43 | // The RED is the extra area that will be visible on devices with a 4:3 aspect ratio (iPads) 44 | // The BLUE is the extra area that will be visible on devices with a 16:9 aspect ratio (iPhone 5 and above) (this is probably the most common ratio overall...) 45 | // The GREY area will most likely never be seen, unless some device has a really odd aspect ratio (and with Android, I wouldn't be surprised if there is a few out there) 46 | 47 | this.screenMetrics = new ScreenMetrics(); 48 | 49 | this.screenMetrics.windowWidth = window.innerWidth; 50 | this.screenMetrics.windowHeight = window.innerHeight; 51 | 52 | this.screenMetrics.defaultGameWidth = defaultWidth; 53 | this.screenMetrics.defaultGameHeight = defaultHeight; 54 | 55 | let orientation: ScreenOrientation = ((this.screenMetrics.defaultGameWidth < this.screenMetrics.defaultGameHeight) ? ScreenOrientation.PORTRAIT : ScreenOrientation.LANDSCAPE); 56 | 57 | // Swap width and height if necessary to match the specified orientation 58 | let dimensionsOppositeForLandscape: boolean = ((this.screenMetrics.windowWidth < this.screenMetrics.windowHeight) && orientation === ScreenOrientation.LANDSCAPE); 59 | let dimensionsOppositeForPortrait: boolean = ((this.screenMetrics.windowHeight < this.screenMetrics.windowWidth) && orientation === ScreenOrientation.PORTRAIT); 60 | 61 | if (dimensionsOppositeForLandscape || dimensionsOppositeForPortrait) { 62 | [this.screenMetrics.windowWidth, this.screenMetrics.windowHeight] = [this.screenMetrics.windowHeight, this.screenMetrics.windowWidth]; 63 | } 64 | 65 | // Calculate the max width and max height if not provided; ratios are based off iPad (4:3) and iPhone 5+ (16:9) as the extremes in both width and height 66 | if (!maxGameWidth || !maxGameHeight) { 67 | this.screenMetrics.maxGameWidth = Math.round(this.screenMetrics.defaultGameWidth * (MAX_GAME_WIDTH / DEFAULT_GAME_WIDTH)); 68 | this.screenMetrics.maxGameHeight = Math.round(this.screenMetrics.defaultGameHeight * (MAX_GAME_HEIGHT / DEFAULT_GAME_HEIGHT)); 69 | } else { 70 | this.screenMetrics.maxGameWidth = maxGameWidth; 71 | this.screenMetrics.maxGameHeight = maxGameHeight; 72 | } 73 | 74 | let defaultAspectRatio: number = (DEFAULT_GAME_WIDTH / DEFAULT_GAME_HEIGHT); 75 | let windowAspectRatio: number = (this.screenMetrics.windowWidth / this.screenMetrics.windowHeight); 76 | 77 | if (windowAspectRatio > defaultAspectRatio) { 78 | this.screenMetrics.gameHeight = this.screenMetrics.defaultGameHeight; 79 | this.screenMetrics.gameWidth = (Math.ceil((this.screenMetrics.gameHeight * windowAspectRatio) * 0.5) * 2); 80 | this.screenMetrics.gameWidth = Math.min(this.screenMetrics.gameWidth, this.screenMetrics.maxGameWidth); 81 | 82 | this.screenMetrics.offsetX = ((this.screenMetrics.gameWidth - this.screenMetrics.defaultGameWidth) * 0.5); 83 | this.screenMetrics.offsetY = 0; 84 | } else { 85 | this.screenMetrics.gameWidth = this.screenMetrics.defaultGameWidth; 86 | this.screenMetrics.gameHeight = (Math.ceil((this.screenMetrics.gameWidth / windowAspectRatio) * 0.5) * 2); 87 | this.screenMetrics.gameHeight = Math.min(this.screenMetrics.gameHeight, this.screenMetrics.maxGameHeight); 88 | 89 | this.screenMetrics.offsetX = 0; 90 | this.screenMetrics.offsetY = ((this.screenMetrics.gameHeight - this.screenMetrics.defaultGameHeight) * 0.5); 91 | } 92 | 93 | // Calculate scaling 94 | this.screenMetrics.scaleX = (this.screenMetrics.windowWidth / this.screenMetrics.gameWidth); 95 | this.screenMetrics.scaleY = (this.screenMetrics.windowHeight / this.screenMetrics.gameHeight); 96 | 97 | return this.screenMetrics; 98 | } 99 | } 100 | 101 | export class StringUtils { 102 | public static toCamelCase(str: string) { 103 | return str.replace(/[^A-Za-z0-9]/g, ' ').replace(/^\w|[A-Z]|\b\w|\s+/g, function (match, index) { 104 | if (+match === 0 || match === '-' || match === '.') { 105 | return ''; 106 | } 107 | return (index === 0 ? match.toLowerCase() : match.toUpperCase()); 108 | }); 109 | } 110 | 111 | public static toPascalCase(str: string) { 112 | let camelCase: string = StringUtils.toCamelCase(str); 113 | 114 | return (camelCase[0].toUpperCase() + camelCase.substr(1)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /take-back-the-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rroylance/phaser-ce-npm-webpack-typescript-starter-project/7fde95c77b936c6ec113fb6e623a580c4d3bf155/take-back-the-colors.png -------------------------------------------------------------------------------- /templates/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= htmlWebpackPlugin.options.title %> 6 | 7 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "sourceMap": true, 6 | "types": [ 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "./src/globals.d.ts", 12 | "./src/app.ts", 13 | "./node_modules/phaser-ce/typescript/phaser.comments.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/phaser-ce/typescript/tslint.json", 3 | "rules": { 4 | "quotemark": [ 5 | true, 6 | "single" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | var CleanWebpackPlugin = require('clean-webpack-plugin'); 5 | var WebpackSynchronizableShellPlugin = require('webpack-synchronizable-shell-plugin'); 6 | 7 | module.exports = { 8 | mode: 'development', 9 | devtool: 'source-map', 10 | entry: path.join(__dirname, 'src/app.ts'), 11 | output: { 12 | path: path.join(__dirname, 'dist'), 13 | filename: 'game.js' 14 | }, 15 | resolve: { 16 | extensions: ['.ts', '.js'], 17 | alias: { 18 | pixi: path.join(__dirname, 'node_modules/phaser-ce/build/custom/pixi.js'), 19 | phaser: path.join(__dirname, 'node_modules/phaser-ce/build/custom/phaser-split.js'), 20 | p2: path.join(__dirname, 'node_modules/phaser-ce/build/custom/p2.js'), 21 | assets: path.join(__dirname, 'assets/') 22 | } 23 | }, 24 | plugins: [ 25 | new WebpackSynchronizableShellPlugin({ 26 | onBuildStart: { 27 | scripts: ['npm run assets:dev'], 28 | blocking: true, 29 | parallel: false 30 | } 31 | }), 32 | new webpack.DefinePlugin({ 33 | 'DEBUG': true, 34 | 35 | // Do not modify these manually, you may break things... 36 | 'DEFAULT_GAME_WIDTH': /*[[DEFAULT_GAME_WIDTH*/800/*DEFAULT_GAME_WIDTH]]*/, 37 | 'DEFAULT_GAME_HEIGHT': /*[[DEFAULT_GAME_HEIGHT*/500/*DEFAULT_GAME_HEIGHT]]*/, 38 | 'MAX_GAME_WIDTH': /*[[MAX_GAME_WIDTH*/888/*MAX_GAME_WIDTH]]*/, 39 | 'MAX_GAME_HEIGHT': /*[[MAX_GAME_HEIGHT*/600/*MAX_GAME_HEIGHT]]*/, 40 | 'SCALE_MODE': JSON.stringify(/*[[SCALE_MODE*/'USER_SCALE'/*SCALE_MODE]]*/), 41 | 42 | // The items below most likely the ones you should be modifying 43 | 'GOOGLE_WEB_FONTS': JSON.stringify([ // Add or remove entries in this array to change which fonts are loaded 44 | 'Barrio' 45 | ]), 46 | 'SOUND_EXTENSIONS_PREFERENCE': JSON.stringify([ // Re-order the items in this array to change the desired order of checking your audio sources (do not add/remove/modify the entries themselves) 47 | 'webm', 'ogg', 'm4a', 'mp3', 'aac', 'ac3', 'caf', 'flac', 'mp4', 'wav' 48 | ]) 49 | }), 50 | new CleanWebpackPlugin([ 51 | path.join(__dirname, 'dist') 52 | ]), 53 | new HtmlWebpackPlugin({ 54 | title: 'DEV MODE: Phaser NPM Webpack TypeScript Starter Project!', 55 | template: path.join(__dirname, 'templates/index.ejs') 56 | }) 57 | ], 58 | devServer: { 59 | contentBase: path.join(__dirname, 'dist'), 60 | compress: true, 61 | port: 9000, 62 | inline: true, 63 | watchOptions: { 64 | aggregateTimeout: 300, 65 | poll: true, 66 | ignored: /node_modules/ 67 | } 68 | }, 69 | module: { 70 | rules: [ 71 | { test: /\.ts$/, enforce: 'pre', loader: 'tslint-loader' }, 72 | { test: /assets(\/|\\)/, type: 'javascript/auto', loader: 'file-loader?name=assets/[hash].[ext]' }, 73 | { test: /pixi\.js$/, loader: 'expose-loader?PIXI' }, 74 | { test: /phaser-split\.js$/, loader: 'expose-loader?Phaser' }, 75 | { test: /p2\.js$/, loader: 'expose-loader?p2' }, 76 | { test: /\.ts$/, loader: 'ts-loader', exclude: '/node_modules/' } 77 | ] 78 | }, 79 | performance: { 80 | hints: false 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /webpack.dist.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | var CleanWebpackPlugin = require('clean-webpack-plugin'); 5 | var WebpackSynchronizableShellPlugin = require('webpack-synchronizable-shell-plugin'); 6 | 7 | module.exports = { 8 | mode: 'production', 9 | entry: path.join(__dirname, 'src/app.ts'), 10 | output: { 11 | path: path.join(__dirname, 'dist'), 12 | filename: 'game.min.js' 13 | }, 14 | resolve: { 15 | extensions: ['.ts', '.js'], 16 | alias: { 17 | pixi: path.join(__dirname, 'node_modules/phaser-ce/build/custom/pixi.js'), 18 | phaser: path.join(__dirname, 'node_modules/phaser-ce/build/custom/phaser-split.js'), 19 | p2: path.join(__dirname, 'node_modules/phaser-ce/build/custom/p2.js'), 20 | assets: path.join(__dirname, 'assets/') 21 | } 22 | }, 23 | plugins: [ 24 | new WebpackSynchronizableShellPlugin({ 25 | onBuildStart: { 26 | scripts: ['npm run assets'], 27 | blocking: true, 28 | parallel: false 29 | } 30 | }), 31 | new webpack.DefinePlugin({ 32 | 'DEBUG': false, 33 | 34 | // Do not modify these manually, you may break things... 35 | 'DEFAULT_GAME_WIDTH': /*[[DEFAULT_GAME_WIDTH*/800/*DEFAULT_GAME_WIDTH]]*/, 36 | 'DEFAULT_GAME_HEIGHT': /*[[DEFAULT_GAME_HEIGHT*/500/*DEFAULT_GAME_HEIGHT]]*/, 37 | 'MAX_GAME_WIDTH': /*[[MAX_GAME_WIDTH*/888/*MAX_GAME_WIDTH]]*/, 38 | 'MAX_GAME_HEIGHT': /*[[MAX_GAME_HEIGHT*/600/*MAX_GAME_HEIGHT]]*/, 39 | 'SCALE_MODE': JSON.stringify(/*[[SCALE_MODE*/'USER_SCALE'/*SCALE_MODE]]*/), 40 | 41 | // The items below most likely the ones you should be modifying 42 | 'GOOGLE_WEB_FONTS': JSON.stringify([ // Add or remove entries in this array to change which fonts are loaded 43 | 'Barrio' 44 | ]), 45 | 'SOUND_EXTENSIONS_PREFERENCE': JSON.stringify([ // Re-order the items in this array to change the desired order of checking your audio sources (do not add/remove/modify the entries themselves) 46 | 'webm', 'ogg', 'm4a', 'mp3', 'aac', 'ac3', 'caf', 'flac', 'mp4', 'wav' 47 | ]) 48 | }), 49 | new CleanWebpackPlugin([ 50 | path.join(__dirname, 'dist') 51 | ]), 52 | new HtmlWebpackPlugin({ 53 | title: 'Phaser NPM Webpack TypeScript Starter Project!', 54 | template: path.join(__dirname, 'templates/index.ejs') 55 | }) 56 | ], 57 | devServer: { 58 | contentBase: path.join(__dirname, 'dist'), 59 | compress: true, 60 | port: 9000, 61 | inline: true, 62 | watchOptions: { 63 | aggregateTimeout: 300, 64 | poll: true, 65 | ignored: /node_modules/ 66 | } 67 | }, 68 | module: { 69 | rules: [ 70 | { test: /\.ts$/, enforce: 'pre', loader: 'tslint-loader' }, 71 | { test: /assets(\/|\\)/, type: 'javascript/auto', loader: 'file-loader?name=assets/[hash].[ext]' }, 72 | { test: /pixi\.js$/, loader: 'expose-loader?PIXI' }, 73 | { test: /phaser-split\.js$/, loader: 'expose-loader?Phaser' }, 74 | { test: /p2\.js$/, loader: 'expose-loader?p2' }, 75 | { test: /\.ts$/, loader: 'ts-loader', exclude: '/node_modules/' } 76 | ] 77 | }, 78 | performance: { 79 | hints: false 80 | } 81 | }; 82 | 83 | --------------------------------------------------------------------------------