├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .istanbul.yml ├── .npmignore ├── .prettierrc ├── .publishrc ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── nnp.gif ├── np.gif └── what-is-nine-patch.png ├── example └── index.ts ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── com │ └── koreez │ │ └── plugin │ │ ├── NinePatchPlugin.ts │ │ └── ninepatch │ │ ├── INinePatchConfig.ts │ │ ├── INinePatchCreator.ts │ │ ├── INinePatchFactory.ts │ │ ├── IPatchesConfig.ts │ │ └── NinePatch.ts └── index.ts ├── static ├── assets │ ├── metalPanel_blueCorner.png │ ├── player.png │ ├── squareGreen.png │ └── squareYellow.png ├── index.html ├── manifest.json ├── scripts │ └── cache-polyfill.js ├── service-worker.js └── styles │ └── main.css ├── test ├── README.md ├── com │ └── koreez │ │ └── plugin │ │ └── plugin.test.ts └── entry.ts ├── tsconfig.json ├── tsconfig.test.json ├── tslint.json ├── tslint.test.json ├── types └── phaser.d.ts ├── webpack.config.js └── webpack.example.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [*.{ts}] 11 | indent_style = space 12 | indent_size = 4 13 | 14 | [*.{js,json}] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [{.codeclimate.yml,.eslintignore,.eslintrc,.istanbul.yml,.publishrc,.travis.yml}] 19 | indent_style = space 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Expected Behavior 4 | 5 | 6 | 7 | ## Current Behavior 8 | 9 | 10 | 11 | ## Possible Solution 12 | 13 | 14 | 15 | ## Steps to Reproduce (for bugs) 16 | 17 | 18 | 1. 19 | 2. 20 | 3. 21 | 4. 22 | 23 | ## Context 24 | 25 | 26 | 27 | ## Your Environment 28 | 29 | * Version used: 30 | * Environment name and version (e.g. Chrome 39, node.js 5.4): 31 | * Operating System and version (desktop or mobile): 32 | * Link to your project: 33 | 34 | # Stack trace 35 | 36 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | 7 | ## Related Issue 8 | 9 | 10 | 11 | 12 | 13 | 14 | ## Motivation and Context 15 | 16 | 17 | 18 | ## How Has This Been Tested? 19 | 20 | 21 | 22 | 23 | 24 | ## Types of changes 25 | 26 | 27 | 28 | - [ ] Updated docs / Refactor code / Added a tests case (non-breaking change) 29 | - [ ] Bug fix (non-breaking change which fixes an issue) 30 | - [ ] New feature (non-breaking change which adds functionality) 31 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 32 | 33 | ## Checklist: 34 | 35 | 36 | 37 | 38 | - [ ] My code follows the code style of this project. 39 | - [ ] My change requires a change to the documentation. 40 | - [ ] I have updated the documentation accordingly. 41 | - [ ] I have read the **CONTRIBUTING** document. 42 | - [ ] I have added tests to cover my changes. 43 | - [ ] All new and existing tests passed. 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .nyc_output 3 | coverage 4 | dist/lib 5 | dist-test 6 | lib 7 | lib-test 8 | node_modules 9 | *.as 10 | *.iml 11 | src/**/*.js 12 | src/**/*.js.map 13 | test/**/*.js 14 | test/**/*.js.map 15 | npm-debug.log 16 | yarn-error.log 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | verbose: false 2 | instrumentation: 3 | root: lib 4 | extensions: 5 | - .ts 6 | # default-excludes: true 7 | # excludes: [] 8 | # variable: __coverage__ 9 | # compact: true 10 | # preserve-comments: false 11 | # complete-copy: false 12 | # save-baseline: false 13 | # baseline-file: ./coverage/coverage-baseline.raw.json 14 | # include-all-sources: false 15 | # include-pid: false 16 | # es-modules: true 17 | # auto-wrap: true 18 | # reporting: 19 | # print: summary 20 | # reports: 21 | # - lcov 22 | # dir: ./coverage 23 | # summarizer: pkg 24 | # report-config: {} 25 | # watermarks: 26 | # statements: [50, 80] 27 | # functions: [50, 80] 28 | # branches: [50, 80] 29 | # lines: [50, 80] 30 | # hooks: 31 | # hook-run-in-context: false 32 | # post-require-hook: null 33 | # handle-sigint: false 34 | # check: 35 | # global: 36 | # statements: 0 37 | # lines: 0 38 | # branches: 0 39 | # functions: 0 40 | # excludes: [] 41 | # each: 42 | # statements: 0 43 | # lines: 0 44 | # branches: 0 45 | # functions: 0 46 | # excludes: [] 47 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.nyc_output 3 | /.vscode 4 | /coverage 5 | /dist 6 | /dist-test 7 | /docs 8 | /lib-test 9 | /not-ported-extensions 10 | /src 11 | /test 12 | /transpiled-code 13 | /typings 14 | CODE_OF_CONDUCT.md 15 | CONTRIBUTING.md 16 | ISSUE_TEMPLATE.md 17 | PULL_REQUEST_TEMPLATE.md 18 | karma.conf.js 19 | tsconfig.json 20 | tsconfig.test.json 21 | tslint.json 22 | tslint.test.json 23 | webpack.config.js 24 | yarn.lock 25 | .editorconfig 26 | .gitignore 27 | .istanbul.yml 28 | .npmignore 29 | .prettierrc 30 | .publishrc 31 | .travis.yml -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "tabWidth": 4, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": false, 7 | "trailingComma": "none", 8 | "bracketSpacing": true 9 | } 10 | -------------------------------------------------------------------------------- /.publishrc: -------------------------------------------------------------------------------- 1 | { 2 | "validations": { 3 | "vulnerableDependencies": false, 4 | "uncommittedChanges": true, 5 | "untrackedFiles": true, 6 | "sensitiveData": false, 7 | "branch": "master", 8 | "gitTag": true 9 | }, 10 | "confirm": true, 11 | "publishTag": "latest --access public", 12 | "prePublishScript": false, 13 | "postPublishScript": false 14 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | env: 5 | global: 6 | - CC_TEST_REPORTER_ID=71e455189fdcb3925ba38e620c090f72e7734ab75219b2a39fbaaad0021daab7 7 | before_install: 8 | - export CHROME_BIN=chromium-browser 9 | - export DISPLAY=:99.0 10 | - sh -e /etc/init.d/xvfb start 11 | - yarn config set registry "https://registry.npmjs.org" 12 | - yarn global add greenkeeper-lockfile@1 13 | - yarn global add codeclimate-test-reporter 14 | before_script: 15 | - greenkeeper-lockfile-update 16 | script: 17 | - yarn test 18 | after_script: 19 | - greenkeeper-lockfile-upload 20 | after_success: 21 | - codeclimate-test-reporter < coverage/lcov.info 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Phaser3 Nine Patch Changelog: 2 | 3 | ## Phaser3 Nine Patch 2.0.0 4 | 5 | ### v2.0.1 6 | 7 | #### Added 8 | 9 | - Add `setTint`, `setTintFill` and `clearTint` methods to be able to change tint. 10 | 11 | ### v2.0.0 12 | 13 | #### Added 14 | 15 | - Now NinePatch class extends Phaser.GameObjects.Container instead of Phaser.GameObjects.RenderTexture to not add additional draws and calls. 16 | 17 | ### v1.1.5 18 | 19 | #### Fixed 20 | 21 | - Minor fixings. 22 | 23 | ### v1.1.4 24 | 25 | #### Fixed 26 | 27 | - Minor fixings. 28 | 29 | ### v1.1.3 30 | 31 | #### Fixed 32 | 33 | - Fix canvas rendering issue. 34 | 35 | ### v1.1.2 36 | 37 | #### Added 38 | 39 | - Add `setTexture` and `setFrame` methods to be able to change texture and/or frame dynamically (see #18). 40 | 41 | #### Fixed 42 | 43 | - Fix `addToScene` to be optional for nine patch creator. 44 | 45 | ### v1.1.1 - Planned stable version 46 | 47 | - fixed resize method to be chainable 48 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@koreez.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to phaser3-ninepatch-plugin 2 | 3 | ## Setup 4 | 5 | 1 - Clone your fork of the repository: 6 | 7 | ``` 8 | $ git clone https://github.com/koreezgames/phaser3-ninepatch-plugin.git 9 | ``` 10 | 11 | 2 - Install npm dependencies using yarn: 12 | 13 | ``` 14 | $ yarn install 15 | ``` 16 | 17 | 3 - Run start process 18 | 19 | ``` 20 | $ yarn run start 21 | ``` 22 | 23 | 4 - Run test process 24 | 25 | ``` 26 | $ yarn test 27 | ``` 28 | 29 | ## Guidelines 30 | 31 | - Please try to [combine multiple commits before 32 | pushing](http://stackoverflow.com/questions/6934752/combining-multiple-commits-before-pushing-in-git). 33 | 34 | - Please use `TDD` when fixing bugs. This means that you should write a unit 35 | test that fails because it reproduces the issue, then fix the issue and finally run 36 | the test to ensure that the issue has been resolved. This helps us to prevent 37 | fixed bugs from happening again in the future. 38 | 39 | - Always format your code using `yarn run autoformat`. 40 | 41 | - Please keep the test coverage at 100%. Write additional unit test if 42 | necessary. 43 | 44 | - Please create an issue before sending a PR if your commit is going to change the 45 | public interface of the package or it includes significant architecture 46 | changes. 47 | 48 | - Feel free to ask for help from other members of the Koreez team via the 49 | [github issues](https://github.com/koreezgames/phaser3-ninepatch-plugin/issues). 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Koreez LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phaser3 Nine Patch 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/koreezgames/phaser3-ninepatch-plugin/blob/master/LICENSE) 4 | [![Build Status](https://secure.travis-ci.org/koreezgames/phaser3-ninepatch-plugin.svg?branch=master)](https://travis-ci.org/koreezgames/phaser3-ninepatch-plugin) 5 | [![codebeat badge](https://codebeat.co/badges/e3792494-1875-4826-be00-2124148b9287)](https://codebeat.co/projects/github-com-koreezgames-phaser3-ninepatch-plugin-master) 6 | [![Test Coverage](https://api.codeclimate.com/v1/badges/63e0c72189fa97ca55db/test_coverage)](https://codeclimate.com/github/koreezgames/phaser3-ninepatch-plugin/test_coverage) 7 | [![npm version](https://badge.fury.io/js/%40koreez%2Fphaser3-ninepatch.svg)](https://badge.fury.io/js/%40koreez%2Fphaser3-ninepatch) 8 | [![Greenkeeper badge](https://badges.greenkeeper.io/koreezgames/phaser3-ninepatch-plugin.svg)](https://greenkeeper.io/) 9 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 10 | 11 | Phaser3 Nine Patch plugin adds 9-slice scaling support to Phaser3! 12 | 13 | #### Resizing with nine patch 14 | 15 | ![Nine Patch ](https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/master/assets/np.gif) 16 | 17 | #### Resizing without nine patch 18 | 19 | ![Not Nine Patch](https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/master/assets/nnp.gif) 20 | 21 | Key features: 22 | 23 | - Blazing fast 24 | - Low memory usage 25 | - Easy to use API 26 | 27 | ## What is Nine-Patch? 28 | 29 | A **Nine-Patch** is an image format that uses extra information, defining what parts of it should be scaled (and which are not) when the image is rendered in a larger or smaller size. This technique is very useful for creating UI components like buttons, panels, containers, etc. 30 | 31 | Using this technique, you can define the background of multiples UI components like panels or buttons with the same image asset. You can also create large panels with a reduced image asset so is very useful for optimizing your project resources. 32 | 33 | ![Phaser3 Nine Patch](https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/master/assets/what-is-nine-patch.png) 34 | 35 | ## Getting Started 36 | 37 | First you want to get a fresh copy of the plugin. You can get it from this repo or from npm, ain't that handy. 38 | 39 | [![npm](https://img.shields.io/npm/dt/@koreez/phaser3-ninepatch.svg)](https://www.npmjs.com/package/@koreez/phaser3-ninepatch) 40 | 41 | ``` 42 | npm install @koreez/phaser3-ninepatch --save 43 | ``` 44 | 45 | ## Usage 46 | 47 | ### Load the plugin 48 | 49 | You still need to load the plugin in your game. This is done just like any other global plugin in Phaser3. 50 | So, to load the plugin, include it in phaser game config. 51 | 52 | ```javascript 53 | { 54 | type: Phaser.WEBGL, 55 | width: 540, 56 | height: 960, 57 | backgroundColor: '#626262', 58 | parent: 'gameContainer', 59 | scene: [GameScene], 60 | plugins: { 61 | global: [{ key: 'NinePatchPlugin', plugin: NinePatchPlugin, start: true }], 62 | }, 63 | } 64 | ``` 65 | 66 | The plugin will patch your Phaser game with additional add/make methods so the NinePatch game object fits up in Phaser like any normal object! 67 | 68 | ### Adding a container 69 | 70 | Also adding and creating of 9-patch images is exposed through Phaser's GameObjectCreator and GameObjectFactory. 71 | So you can add a 9-patch image to your game just like any other image once it's preloaded! 72 | 73 | ```javascript 74 | //We specify the x and y position, the width and height and the key for the image of the image. It will be automaticly scaled! 75 | this.add.ninePatch(100, 100, 200, 400, "my-image", null, { 76 | top: 10, // Amount of pixels for top 77 | bottom: 20, // Amount of pixels for bottom 78 | left: 30, // Amount of pixels for left 79 | right: 40 // Amount of pixels for right 80 | }); 81 | ``` 82 | 83 | Or if you'd want to do something with it first: 84 | 85 | ```javascript 86 | //Two options here, first we use Phaser 87 | var ninePatch = this.make.ninePatch({ 88 | key: "my-image", 89 | width: 200, 90 | height: 200, 91 | patchesConfig: { 92 | top: 10, // Amount of pixels for top 93 | bottom: 20, // Amount of pixels for bottom 94 | left: 30, // Amount of pixels for left 95 | right: 40 // Amount of pixels for right 96 | } 97 | }); 98 | ninePatch.x = 20; 99 | ninePatch.y = 20; 100 | this.add.existing(ninePatch); 101 | 102 | //Or we use the Constructor 103 | var ninePatch = new NinePatch(this, 0, 0, 200, 50, "my-image", null, { 104 | top: 10, // Amount of pixels for top 105 | bottom: 20, // Amount of pixels for bottom 106 | left: 30, // Amount of pixels for left 107 | right: 40 // Amount of pixels for right 108 | }); 109 | ninePatch.x = 50; 110 | ninePatch.y = 50; 111 | this.add.existing(nineSlice); 112 | ``` 113 | 114 | ### Using a Texture Atlas 115 | 116 | It's also possible to use an image that's located inside a Texture atlas. The only difference then is that you don't have to preload the image, instead you use the object's constructor and pass the framedata directly on creation: 117 | 118 | ```javascript 119 | //Add an nineslice image with an texture atlas 120 | var ninePatch = new NinePatch( 121 | this, // Phaser.Scene 122 | 150, // x position 123 | 100, // y position 124 | 200, // expected width 125 | 100, // expected height 126 | "buttons", // atlas key 127 | "btn_clean.png", // Image frame 128 | { 129 | top: 20, // Amount of pixels for top 130 | bottom: 23, // Amount of pixels for bottom 131 | left: 27, // Amount of pixels for left 132 | right: 28 // Amount of pixels for right 133 | } 134 | ); 135 | this.add.existing(ninePatch); 136 | ``` 137 | 138 | ### Resize method 139 | 140 | 9-patch image has a resize method which lets you expand/shorten the width or length of the image. 141 | When using resize method, make sure values are not lower than the width of the image corners 142 | 143 | ```javascript 144 | var ninePatch = this.add.ninePatch(5, 5, 48, 48, "image", null, { 145 | top: 5 // Amount of pixels for top, bottom, left, right 146 | }); 147 | ninePatch.resize(100, 200); 148 | ``` 149 | 150 | ## Credits 151 | 152 | Big thanks to this great repo: 153 | 154 | https://github.com/orange-games/phaser-nineslice 155 | 156 | ## License 157 | 158 | [MIT](LICENSE) 159 | -------------------------------------------------------------------------------- /assets/nnp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/11cfc39edb972db092aff0652172610df98756fc/assets/nnp.gif -------------------------------------------------------------------------------- /assets/np.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/11cfc39edb972db092aff0652172610df98756fc/assets/np.gif -------------------------------------------------------------------------------- /assets/what-is-nine-patch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/11cfc39edb972db092aff0652172610df98756fc/assets/what-is-nine-patch.png -------------------------------------------------------------------------------- /example/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import "phaser"; 4 | import { NinePatch } from "../src/com/koreez/plugin/ninepatch/NinePatch"; 5 | import { NinePatchPlugin } from "../src/com/koreez/plugin/NinePatchPlugin"; 6 | 7 | export default class Game extends Phaser.Game { 8 | public centerX: number; 9 | public centerY: number; 10 | constructor(config: GameConfig) { 11 | super(config); 12 | } 13 | } 14 | 15 | function preload() { 16 | console.log("preload"); 17 | this.load.image("metalPanel_blueCorner", "static/assets/metalPanel_blueCorner.png"); 18 | this.load.image("squareGreen", "static/assets/squareGreen.png"); 19 | this.load.image("squareYellow", "static/assets/squareYellow.png"); 20 | } 21 | 22 | function create() { 23 | console.log("create"); 24 | // We specify the x and y position, the width and height and the key for the image of the image. 25 | // It will be automatically scaled! 26 | const metalPanel = this.add.ninePatch(200, 200, 300, 300, "metalPanel_blueCorner", null, { 27 | bottom: 11, // Amount of pixels for bottom 28 | left: 11, // Amount of pixels for left 29 | right: 53, // Amount of pixels for right 30 | top: 32 // Amount of pixels for top 31 | }); 32 | let metalPanelDir = 0.6; 33 | metalPanel.setTint(0xff0000); 34 | this.tweens.add({ 35 | /* tslint:disable */ 36 | targets: metalPanel, 37 | duration: 3000, 38 | y: 190, 39 | onUpdate: (tween: any) => { 40 | metalPanel.resize(metalPanel.width + metalPanelDir, metalPanel.height + metalPanelDir); 41 | }, 42 | onYoyo: (tween: any) => { 43 | metalPanelDir = -1 * metalPanelDir; 44 | }, 45 | onRepeat: (tween: any) => { 46 | metalPanelDir = -1 * metalPanelDir; 47 | }, 48 | repeat: -1, 49 | yoyo: true 50 | /* tslint:enable */ 51 | }); 52 | setTimeout(() => { 53 | metalPanel.setTintFill(0xff0000); 54 | setTimeout(() => { 55 | metalPanel.clearTint(0xff0000); 56 | }, 3000); 57 | }, 3000); 58 | let squareGreen = this.make.ninePatch({ 59 | /* tslint:disable */ 60 | key: "squareGreen", 61 | width: 100, 62 | height: 50, 63 | patchesConfig: { 64 | bottom: 14, // Amount of pixels for bottom 65 | left: 6, // Amount of pixels for left 66 | right: 6, // Amount of pixels for right 67 | top: 10 // Amount of pixels for top 68 | } 69 | /* tslint:enable */ 70 | }); 71 | 72 | squareGreen.x = 450; 73 | squareGreen.y = 50; 74 | this.add.existing(squareGreen); 75 | let squareGreenDir = 1; 76 | this.tweens.add({ 77 | /* tslint:disable */ 78 | targets: squareGreen, 79 | duration: 3000, 80 | x: 550, 81 | onUpdate: (tween: any) => { 82 | squareGreen.resize(squareGreen.width + squareGreenDir, squareGreen.height); 83 | }, 84 | onYoyo: (tween: any) => { 85 | squareGreenDir = -1 * squareGreenDir; 86 | }, 87 | onRepeat: (tween: any) => { 88 | squareGreenDir = -1 * squareGreenDir; 89 | }, 90 | repeat: -1, 91 | yoyo: true 92 | /* tslint:enable */ 93 | }); 94 | 95 | this.cache.custom.ninePatch.add("squareYellow", { 96 | /* tslint:disable */ 97 | bottom: 14, // Amount of pixels for bottom 98 | left: 6, // Amount of pixels for left 99 | right: 6, // Amount of pixels for right 100 | top: 10 // Amount of pixels for top 101 | /* tslint:enable */ 102 | }); 103 | 104 | const squareYellow = this.add.ninePatch(550, 500, 300, 100, "squareYellow"); 105 | let squareYellowDir = 0.6; 106 | this.tweens.add({ 107 | /* tslint:disable */ 108 | targets: squareYellow, 109 | duration: 3000, 110 | x: 150, 111 | repeat: -1, 112 | yoyo: true, 113 | onUpdate: (tween: any) => { 114 | squareYellow.resize(squareYellow.width + squareYellowDir, squareYellow.height); 115 | }, 116 | onYoyo: (tween: any) => { 117 | squareYellowDir = -1 * squareYellowDir; 118 | }, 119 | onRepeat: (tween: any) => { 120 | squareYellowDir = -1 * squareYellowDir; 121 | } 122 | /* tslint:enable */ 123 | }); 124 | 125 | // Or we use the Constructor 126 | const newNinePatch = new NinePatch(this, 630, 300, 200, 50, "squareGreen", null, { 127 | /* tslint:disable */ 128 | bottom: 14, // Amount of pixels for bottom 129 | left: 6, // Amount of pixels for left 130 | right: 6, // Amount of pixels for right 131 | top: 10 // Amount of pixels for top 132 | /* tslint:enable */ 133 | }); 134 | this.add.existing(newNinePatch); 135 | let newNinePatchDir = 0.6; 136 | this.tweens.add({ 137 | /* tslint:disable */ 138 | targets: newNinePatch, 139 | duration: 3000, 140 | rotation: Math.PI, 141 | repeat: -1, 142 | yoyo: true, 143 | onUpdate: (tween: any) => { 144 | newNinePatch.resize(newNinePatch.width + newNinePatchDir, newNinePatch.height); 145 | }, 146 | onYoyo: (tween: any) => { 147 | newNinePatchDir = -1 * newNinePatchDir; 148 | }, 149 | onRepeat: (tween: any) => { 150 | newNinePatchDir = -1 * newNinePatchDir; 151 | } 152 | /* tslint:enable */ 153 | }); 154 | setTimeout(() => { 155 | newNinePatch.setTexture("squareYellow"); 156 | }, 4000); 157 | } 158 | 159 | document.onreadystatechange = () => { 160 | if (document.readyState === "complete") { 161 | const config = { 162 | banner: true, 163 | height: 600, 164 | plugins: { 165 | global: [{ key: "NinePatchPlugin", plugin: NinePatchPlugin, start: true }] 166 | }, 167 | scene: { 168 | create, 169 | preload 170 | }, 171 | type: Phaser.AUTO, 172 | width: 800 173 | }; 174 | (window as any).game = new Game(config); 175 | } 176 | }; 177 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | process.env.TEST = true; 2 | process.env.NODE_ENV = "test"; 3 | 4 | const webpackConfig = require("./webpack.config.js")({ production: false, karma: true }); 5 | 6 | delete webpackConfig.entry; 7 | 8 | module.exports = config => { 9 | var configuration = { 10 | basePath: "", 11 | frameworks: ["mocha", "chai", "sinon", "es6-shim"], 12 | files: [ 13 | { pattern: "node_modules/bluebird/js/browser/bluebird.js", include: true }, 14 | { pattern: "node_modules/phaser/dist/phaser.js", include: true }, 15 | { pattern: "./test/**/**/**.test.ts", include: true }, 16 | { pattern: "**/*.map", served: true, included: false, watched: true } 17 | ], 18 | preprocessors: { 19 | "./**/**/**/**.ts": ["sourcemap"], 20 | "./test/**/**/**.test.ts": ["webpack"] 21 | }, 22 | webpack: webpackConfig, 23 | webpackMiddleware: { 24 | noInfo: true 25 | }, 26 | plugins: [ 27 | "karma-webpack", 28 | "karma-sourcemap-writer", 29 | "karma-sourcemap-loader", 30 | "karma-remap-istanbul", 31 | "karma-mocha-reporter", 32 | "karma-mocha", 33 | "karma-chai", 34 | "karma-sinon", 35 | "karma-es6-shim", 36 | "karma-coverage-istanbul-reporter" 37 | ], 38 | reporters: config.singleRun ? ["dots", "mocha", "coverage-istanbul"] : ["dots", "mocha"], 39 | coverageIstanbulReporter: { 40 | reports: ["html", "lcov", "lcovonly", "text-summary"], 41 | dir: "coverage", 42 | fixWebpackSourcePaths: true, 43 | "report-config": { 44 | html: { 45 | subdir: "html-report" 46 | } 47 | } 48 | }, 49 | port: 9876, 50 | colors: true, 51 | logLevel: config.LOG_INFO, 52 | autoWatch: true, 53 | browsers: [] 54 | }; 55 | 56 | if (process.env.TRAVIS) { 57 | configuration.browsers.push("PhantomJS"); 58 | configuration.plugins.push("karma-phantomjs-launcher"); 59 | } else { 60 | configuration.browsers.push("PhantomJS"); 61 | configuration.plugins.push("karma-phantomjs-launcher"); 62 | } 63 | 64 | config.set(configuration); 65 | }; 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@koreez/phaser3-ninepatch", 3 | "version": "2.0.2", 4 | "description": "Phaser3 Nine Patch", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "start": "webpack-dev-server --config ./webpack.example.config.js", 9 | "test": "karma start --single-run", 10 | "mocha": "mocha test/**/*.test.ts --require ts-node/register", 11 | "autoformat": "prettier --config .prettierrc --write {src,test}{/,/**/}{*,*.test}.ts", 12 | "tslint-check:src": "tslint-config-prettier-check ./tslint.json", 13 | "tslint-check:test": "tslint-config-prettier-check ./tslint.test.json", 14 | "tslint:src": "tslint --project tsconfig.json", 15 | "tslint:test": "tslint --config tslint.test.json --project tsconfig.test.json", 16 | "tslint": "npm run tslint-check:src && npm run tslint-check:test && npm run tslint:src && npm run tslint:test", 17 | "clean-up": "rimraf .nyc_output && rimraf coverage && rimraf lib && rimraf lib-test && rimraf dist-test", 18 | "compile:src": "tsc -d", 19 | "compile:test": "tsc -p tsconfig.test.json -d", 20 | "dev": "webpack", 21 | "build": "webpack --env.production", 22 | "prepare": "npm run clean-up && npm run compile:src", 23 | "prepublishOnly": "publish-please guard", 24 | "publish-please": "npm run tslint && npm run autoformat && npm run clean-up && npm run test && publish-please" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git@github.com:koreezgames/phaser3-ninepatch-plugin.git" 29 | }, 30 | "publishConfig": { 31 | "registry": "https://registry.npmjs.org" 32 | }, 33 | "keywords": [ 34 | "Phaser3", 35 | "Phaser", 36 | "ninepatch", 37 | "nine patch", 38 | "nine slice" 39 | ], 40 | "author": "Koreez LLC", 41 | "contributors": [ 42 | "Sargis Sargsyan ", 43 | "Gnun Ulikhanyan " 44 | ], 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/koreezgames/phaser3-ninepatch-plugin/issues" 48 | }, 49 | "homepage": "https://github.com/koreezgames/phaser3-ninepatch-plugin#readme", 50 | "dependencies": {}, 51 | "peerDependencies": { 52 | "phaser": "^3.12.0" 53 | }, 54 | "devDependencies": { 55 | "@types/bluebird": "^3.5.26", 56 | "@types/chai": "^4.1.7", 57 | "@types/mocha": "^5.2.6", 58 | "@types/sinon": "^7.0.11", 59 | "bluebird": "^3.5.4", 60 | "browserify-versionify": "^1.0.6", 61 | "chai": "^4.2.0", 62 | "es6-map": "^0.1.5", 63 | "es6-symbol": "^3.1.1", 64 | "glslify": "^7.0.0", 65 | "html-webpack-plugin": "^3.2.0", 66 | "imports-loader": "^0.8.0", 67 | "istanbul": "^0.4.5", 68 | "istanbul-instrumenter-loader": "^3.0.1", 69 | "karma": "^4.0.1", 70 | "karma-chai": "^0.1.0", 71 | "karma-chai-sinon": "^0.1.5", 72 | "karma-chrome-launcher": "^2.2.0", 73 | "karma-coverage-istanbul-reporter": "^2.0.5", 74 | "karma-es6-shim": "^1.0.0", 75 | "karma-mocha": "^1.3.0", 76 | "karma-mocha-reporter": "^2.2.5", 77 | "karma-phantomjs-launcher": "^1.0.4", 78 | "karma-remap-istanbul": "^0.6.0", 79 | "karma-sinon": "^1.0.5", 80 | "karma-sourcemap-loader": "^0.3.7", 81 | "karma-sourcemap-writer": "^0.1.2", 82 | "karma-webpack": "^3.0.5", 83 | "mocha": "^5.2.0", 84 | "phaser": "^3.16.2", 85 | "prettier": "^1.16.4", 86 | "publish-please": "^5.4.3", 87 | "remap-istanbul": "^0.13.0", 88 | "rimraf": "^2.6.3", 89 | "sinon": "^7.3.1", 90 | "sinon-chai": "^3.3.0", 91 | "ts-loader": "^5.3.3", 92 | "ts-node": "^8.0.3", 93 | "tslint": "^5.15.0", 94 | "tslint-config-prettier": "^1.18.0", 95 | "typescript": "^3.4.2", 96 | "uglifyjs-webpack-plugin": "^2.1.2", 97 | "webpack": "^4.29.6", 98 | "webpack-cli": "^3.3.0", 99 | "webpack-concat-plugin": "^3.0.0", 100 | "webpack-dev-server": "^3.3.0" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/NinePatchPlugin.ts: -------------------------------------------------------------------------------- 1 | import { NinePatch } from "./ninepatch/NinePatch"; 2 | 3 | export class NinePatchPlugin extends Phaser.Plugins.BasePlugin { 4 | constructor(pluginManager: Phaser.Plugins.PluginManager) { 5 | super(pluginManager); 6 | 7 | pluginManager.game.cache.addCustom("ninePatch"); 8 | 9 | // Register our new Game Object type 10 | pluginManager.registerGameObject("ninePatch", this.ninePatchFactory, this.ninePatchCreator); 11 | } 12 | 13 | private ninePatchFactory( 14 | x: number, 15 | y: number, 16 | width: number, 17 | height: number, 18 | key: string, 19 | frame?: string | number, 20 | patchesConfig?: any 21 | ): NinePatch { 22 | return this.scene.add.existing(new NinePatch(this.scene, x, y, width, height, key, frame, patchesConfig)) as NinePatch; 23 | } 24 | 25 | private ninePatchCreator(config: any, addToScene?: boolean): NinePatch { 26 | if (config === undefined) { 27 | config = {}; 28 | } 29 | 30 | const key: any = (Phaser.Utils as any).Objects.GetAdvancedValue(config, "key", null); 31 | const frame: any = (Phaser.Utils as any).Objects.GetAdvancedValue(config, "frame", null); 32 | const width: any = (Phaser.Utils as any).Objects.GetAdvancedValue(config, "width", null); 33 | const height: any = (Phaser.Utils as any).Objects.GetAdvancedValue(config, "height", null); 34 | const patchesConfig: any = (Phaser.Utils as any).Objects.GetAdvancedValue(config, "patchesConfig", null); 35 | 36 | const ninePatch: NinePatch = new NinePatch(this.scene, 0, 0, width, height, key, frame, patchesConfig); 37 | 38 | if (addToScene !== undefined) { 39 | config.add = addToScene; 40 | } 41 | 42 | Phaser.GameObjects.BuildGameObject(this.scene, ninePatch, config); 43 | 44 | return ninePatch; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/ninepatch/INinePatchConfig.ts: -------------------------------------------------------------------------------- 1 | export interface INinePatchConfig { 2 | x: number; 3 | y: number; 4 | width: number; 5 | height: number; 6 | key: string; 7 | frame?: string | number; 8 | patchesConfig?: any; 9 | } 10 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/ninepatch/INinePatchCreator.ts: -------------------------------------------------------------------------------- 1 | import { NinePatch } from "./NinePatch"; 2 | 3 | export interface INinePatchCreator extends Phaser.GameObjects.GameObjectCreator { 4 | ninePatch: (config: any, addToScene?: boolean) => NinePatch; 5 | } 6 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/ninepatch/INinePatchFactory.ts: -------------------------------------------------------------------------------- 1 | import { NinePatch } from "./NinePatch"; 2 | 3 | export interface INinePatchFactory extends Phaser.GameObjects.GameObjectFactory { 4 | ninePatch: ( 5 | x: number, 6 | y: number, 7 | width: number, 8 | height: number, 9 | key: string, 10 | frame?: string | number, 11 | patchesConfig?: any 12 | ) => NinePatch; 13 | } 14 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/ninepatch/IPatchesConfig.ts: -------------------------------------------------------------------------------- 1 | export interface IPatchesConfig { 2 | top: number; 3 | left?: number; 4 | right?: number; 5 | bottom?: number; 6 | } 7 | 8 | const normalizePatchesConfig: (config: IPatchesConfig) => IPatchesConfig = (config: IPatchesConfig) => { 9 | config.bottom = config.bottom || config.top; 10 | config.left = config.left || config.top; 11 | config.right = config.right || config.left; 12 | return config; 13 | }; 14 | 15 | export { normalizePatchesConfig }; 16 | -------------------------------------------------------------------------------- /src/com/koreez/plugin/ninepatch/NinePatch.ts: -------------------------------------------------------------------------------- 1 | import { IPatchesConfig, normalizePatchesConfig } from "./IPatchesConfig"; 2 | 3 | export class NinePatch extends Phaser.GameObjects.Container { 4 | private static readonly __BASE: string = "__BASE"; 5 | private static readonly patches: string[] = ["[0][0]", "[1][0]", "[2][0]", "[0][1]", "[1][1]", "[2][1]", "[0][2]", "[1][2]", "[2][2]"]; 6 | 7 | private originTexture: Phaser.Textures.Texture; 8 | private originFrame: Phaser.Textures.Frame; 9 | private config: IPatchesConfig; 10 | private finalXs: number[]; 11 | private finalYs: number[]; 12 | private internalTint: number; 13 | 14 | constructor( 15 | scene: Phaser.Scene, 16 | x: number, 17 | y: number, 18 | width: number, 19 | height: number, 20 | key: string, 21 | frame?: string | number, 22 | config?: IPatchesConfig 23 | ) { 24 | super(scene, x, y); 25 | this.config = config || this.scene.cache.custom.ninePatch.get(frame ? `${frame}` : key); 26 | normalizePatchesConfig(this.config); 27 | this.setSize(width, height); 28 | this.setTexture(key, frame); 29 | } 30 | 31 | public resize(width: number, height: number): this { 32 | width = Math.round(width); 33 | height = Math.round(height); 34 | if (!this.config) { 35 | return this; 36 | } 37 | if (this.width === width && this.height === height) { 38 | return this; 39 | } 40 | width = Math.max(width, this.config.left + this.config.right); 41 | height = Math.max(height, this.config.top + this.config.bottom); 42 | this.setSize(width, height); 43 | this.drawPatches(); 44 | return this; 45 | } 46 | 47 | public setTexture(key: string, frame?: string | integer): this { 48 | this.originTexture = this.scene.textures.get(key); 49 | this.setFrame(frame); 50 | return this; 51 | } 52 | 53 | public setFrame(frame: string | integer): this { 54 | this.originFrame = (this.originTexture.frames as any)[frame] || (this.originTexture.frames as any)[NinePatch.__BASE]; 55 | this.createPatches(); 56 | this.drawPatches(); 57 | return this; 58 | } 59 | 60 | public setSize(width: number, height: number): this { 61 | super.setSize(width, height); 62 | // These are the positions we need the eventual texture to have 63 | this.finalXs = [0, this.config.left, this.width - this.config.right, this.width]; 64 | this.finalYs = [0, this.config.top, this.height - this.config.bottom, this.height]; 65 | return this; 66 | } 67 | 68 | public setTint(tint: number): this { 69 | this.tint = tint; 70 | return this; 71 | } 72 | 73 | public setTintFill(tint: number): this { 74 | this.tint = tint; 75 | this.tintFill = true; 76 | return this; 77 | } 78 | 79 | public clearTint(): this { 80 | this.each((patch: Phaser.GameObjects.Image) => patch.clearTint()); 81 | this.internalTint = undefined; 82 | return this; 83 | } 84 | 85 | public get tintFill(): boolean { 86 | return this.first && (this.first as Phaser.GameObjects.Image).tintFill; 87 | } 88 | 89 | public set tintFill(value: boolean) { 90 | this.each((patch: Phaser.GameObjects.Image) => (patch.tintFill = value)); 91 | } 92 | 93 | public set tint(value: number) { 94 | this.each((patch: Phaser.GameObjects.Image) => patch.setTint(value)); 95 | this.internalTint = value; 96 | } 97 | 98 | get isTinted(): boolean { 99 | return this.first && (this.first as Phaser.GameObjects.Image).isTinted; 100 | } 101 | 102 | private createPatches(): void { 103 | // The positions we want from the base texture 104 | const textureXs: number[] = [0, this.config.left, this.originFrame.width - this.config.right, this.originFrame.width]; 105 | const textureYs: number[] = [0, this.config.top, this.originFrame.height - this.config.bottom, this.originFrame.height]; 106 | let patchIndex: number = 0; 107 | for (let yi: number = 0; yi < 3; yi++) { 108 | for (let xi: number = 0; xi < 3; xi++) { 109 | this.createPatchFrame( 110 | this.getPatchNameByIndex(patchIndex), 111 | textureXs[xi], // x 112 | textureYs[yi], // y 113 | textureXs[xi + 1] - textureXs[xi], // width 114 | textureYs[yi + 1] - textureYs[yi] // height 115 | ); 116 | ++patchIndex; 117 | } 118 | } 119 | } 120 | 121 | private drawPatches(): void { 122 | const tintFill = this.tintFill; 123 | this.removeAll(true); 124 | let patchIndex: number = 0; 125 | for (let yi: number = 0; yi < 3; yi++) { 126 | for (let xi: number = 0; xi < 3; xi++) { 127 | // @ts-ignore 128 | const patch: Phaser.Textures.Frame = this.originTexture.frames[this.getPatchNameByIndex(patchIndex)]; 129 | const patchImg = new Phaser.GameObjects.Image(this.scene, 0, 0, patch.texture.key, patch.name); 130 | patchImg.setOrigin(0); 131 | patchImg.setPosition(this.finalXs[xi] - this.width * this.originX, this.finalYs[yi] - this.height * this.originY); 132 | patchImg.setScale( 133 | (this.finalXs[xi + 1] - this.finalXs[xi]) / patch.width, 134 | (this.finalYs[yi + 1] - this.finalYs[yi]) / patch.height 135 | ); 136 | this.add(patchImg); 137 | patchImg.setTint(this.internalTint); 138 | patchImg.tintFill = tintFill; 139 | ++patchIndex; 140 | } 141 | } 142 | } 143 | 144 | private createPatchFrame(patch: string, x: number, y: number, width: number, height: number): void { 145 | if (this.originTexture.frames.hasOwnProperty(patch)) { 146 | return; 147 | } 148 | this.originTexture.add(patch, this.originFrame.sourceIndex, this.originFrame.cutX + x, this.originFrame.cutY + y, width, height); 149 | } 150 | 151 | private getPatchNameByIndex(index: number): string { 152 | return `${this.originFrame.name}|${NinePatch.patches[index]}`; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // Copyright (c) 2018 Koreez LLC. All Rights Reserved. 3 | // 4 | // NOTICE: You are permitted to use, modify, and distribute this file 5 | // in accordance with the terms of the license agreement accompanying it. 6 | // ------------------------------------------------------------------------------ 7 | 8 | /** 9 | * NinePatchPlugin 10 | */ 11 | export { NinePatchPlugin } from "./com/koreez/plugin/NinePatchPlugin"; 12 | 13 | export { INinePatchCreator } from "./com/koreez/plugin/ninepatch/INinePatchCreator"; 14 | 15 | export { INinePatchFactory } from "./com/koreez/plugin/ninepatch/INinePatchFactory"; 16 | 17 | export { INinePatchConfig } from "./com/koreez/plugin/ninepatch/INinePatchConfig"; 18 | 19 | export { IPatchesConfig } from "./com/koreez/plugin/ninepatch/IPatchesConfig"; 20 | 21 | export { NinePatch } from "./com/koreez/plugin/ninepatch/NinePatch"; 22 | -------------------------------------------------------------------------------- /static/assets/metalPanel_blueCorner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/11cfc39edb972db092aff0652172610df98756fc/static/assets/metalPanel_blueCorner.png -------------------------------------------------------------------------------- /static/assets/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/11cfc39edb972db092aff0652172610df98756fc/static/assets/player.png -------------------------------------------------------------------------------- /static/assets/squareGreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/11cfc39edb972db092aff0652172610df98756fc/static/assets/squareGreen.png -------------------------------------------------------------------------------- /static/assets/squareYellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koreezgames/phaser3-ninepatch-plugin/11cfc39edb972db092aff0652172610df98756fc/static/assets/squareYellow.png -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Phaser Boilerplate 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Your Game", 3 | "short_name": "Your Game", 4 | "start_url": "index.html", 5 | "display": "standalone", 6 | "orientation": "landscape", 7 | "icons": [ 8 | { 9 | "src": "icon.png", 10 | "sizes": "512x512", 11 | "type": "image/png" 12 | }, 13 | { 14 | "src": "launcher-icon-2x.png", 15 | "sizes": "96x96", 16 | "type": "image/png" 17 | }, 18 | { 19 | "src": "launcher-icon-3x.png", 20 | "sizes": "144x144", 21 | "type": "image/png" 22 | }, 23 | { 24 | "src": "launcher-icon-4x.png", 25 | "sizes": "192x192", 26 | "type": "image/png" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /static/scripts/cache-polyfill.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | (function() { 19 | var nativeAddAll = Cache.prototype.addAll; 20 | var userAgent = navigator.userAgent.match(/(Firefox|Chrome)\/(\d+\.)/); 21 | 22 | // Has nice behavior of `var` which everyone hates 23 | if (userAgent) { 24 | var agent = userAgent[1]; 25 | var version = parseInt(userAgent[2]); 26 | } 27 | 28 | if ( 29 | nativeAddAll && (!userAgent || 30 | (agent === 'Firefox' && version >= 46) || 31 | (agent === 'Chrome' && version >= 50) 32 | ) 33 | ) { 34 | return; 35 | } 36 | 37 | Cache.prototype.addAll = function addAll(requests) { 38 | var cache = this; 39 | 40 | // Since DOMExceptions are not constructable: 41 | function NetworkError(message) { 42 | this.name = 'NetworkError'; 43 | this.code = 19; 44 | this.message = message; 45 | } 46 | 47 | NetworkError.prototype = Object.create(Error.prototype); 48 | 49 | return Promise.resolve().then(function() { 50 | if (arguments.length < 1) throw new TypeError(); 51 | 52 | // Simulate sequence<(Request or USVString)> binding: 53 | var sequence = []; 54 | 55 | requests = requests.map(function(request) { 56 | if (request instanceof Request) { 57 | return request; 58 | } 59 | else { 60 | return String(request); // may throw TypeError 61 | } 62 | }); 63 | 64 | return Promise.all( 65 | requests.map(function(request) { 66 | if (typeof request === 'string') { 67 | request = new Request(request); 68 | } 69 | 70 | var scheme = new URL(request.url).protocol; 71 | 72 | if (scheme !== 'http:' && scheme !== 'https:') { 73 | throw new NetworkError("Invalid scheme"); 74 | } 75 | 76 | return fetch(request.clone()); 77 | }) 78 | ); 79 | }).then(function(responses) { 80 | // If some of the responses has not OK-eish status, 81 | // then whole operation should reject 82 | if (responses.some(function(response) { 83 | return !response.ok; 84 | })) { 85 | throw new NetworkError('Incorrect response status'); 86 | } 87 | 88 | // TODO: check that requests don't overwrite one another 89 | // (don't think this is possible to polyfill due to opaque responses) 90 | return Promise.all( 91 | responses.map(function(response, i) { 92 | return cache.put(requests[i], response); 93 | }) 94 | ); 95 | }).then(function() { 96 | return undefined; 97 | }); 98 | }; 99 | 100 | Cache.prototype.add = function add(request) { 101 | return this.addAll([request]); 102 | }; 103 | }()); -------------------------------------------------------------------------------- /static/service-worker.js: -------------------------------------------------------------------------------- 1 | importScripts('scripts/cache-polyfill.js'); 2 | 3 | var CACHE_VERSION = 'app-v1'; 4 | var CACHE_FILES = [ 5 | 'index.html' 6 | ]; 7 | 8 | self.addEventListener('install', function (event) { 9 | event.waitUntil( 10 | caches.open(CACHE_VERSION) 11 | .then(function (cache) { 12 | console.log('Opened cache'); 13 | return cache.addAll(CACHE_FILES); 14 | }) 15 | ); 16 | }); 17 | 18 | self.addEventListener('activate', function (event) { 19 | event.waitUntil( 20 | caches.keys().then(function(keys){ 21 | return Promise.all(keys.map(function(key, i){ 22 | if(key !== CACHE_VERSION){ 23 | return caches.delete(keys[i]); 24 | } 25 | })) 26 | }) 27 | ) 28 | }); 29 | 30 | self.addEventListener('fetch', function (event) { 31 | event.respondWith( 32 | caches.match(event.request).then(function(res){ 33 | if(res){ 34 | return res; 35 | } 36 | requestBackend(event); 37 | }) 38 | ) 39 | }); 40 | 41 | function requestBackend(event){ 42 | var url = event.request.clone(); 43 | return fetch(url).then(function(res){ 44 | //if not a valid response send the error 45 | if(!res || res.status !== 200 || res.type !== 'basic'){ 46 | return res; 47 | } 48 | 49 | var response = res.clone(); 50 | 51 | caches.open(CACHE_VERSION).then(function(cache){ 52 | cache.put(event.request, response); 53 | }); 54 | 55 | return res; 56 | }) 57 | } -------------------------------------------------------------------------------- /static/styles/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | width: 100%; 3 | height: 100%; 4 | margin: 0px; 5 | padding: 0px; 6 | } 7 | 8 | body { 9 | height: inherit; 10 | width: inherit; 11 | margin: 0px; 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | } 16 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Plugin Testing 2 | 3 | Unit tests are written using Mocha, Chai and Sinon. -------------------------------------------------------------------------------- /test/com/koreez/plugin/plugin.test.ts: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // Copyright (c) 2018 Koreez LLC. All Rights Reserved. 3 | // 4 | // NOTICE: You are permitted to use, modify, and distribute this file 5 | // in accordance with the terms of the license agreement accompanying it. 6 | // ------------------------------------------------------------------------------ 7 | 8 | import "../../../entry"; 9 | 10 | import { expect } from "chai"; 11 | 12 | import { NinePatchPlugin } from "../../../../src/com/koreez/plugin/NinePatchPlugin"; 13 | 14 | describe("Ninepatch Plugin", () => { 15 | it("plugin_is_properly_installed", done => { 16 | const config = { 17 | banner: false, 18 | height: 10, 19 | plugins: { 20 | global: [{ key: "NinePatchPlugin", plugin: NinePatchPlugin, start: true }] 21 | }, 22 | scene: { 23 | create 24 | }, 25 | type: Phaser.AUTO, 26 | width: 10 27 | }; 28 | let plugin: NinePatchPlugin; 29 | function create() { 30 | plugin = this.plugins.get("NinePatchPlugin"); 31 | expect(plugin).instanceOf(NinePatchPlugin); 32 | done(); 33 | } 34 | (window as any).game = new Phaser.Game(config); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/entry.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | import "bluebird/js/browser/bluebird"; 4 | import "es6-map/implement"; 5 | import "es6-symbol/implement"; 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "target": "es5", 5 | "lib": ["es7", "dom"], 6 | "sourceMap": true, 7 | "inlineSources": true, 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "emitDecoratorMetadata": true, 12 | "removeComments": true, 13 | "strict": false, 14 | "noImplicitAny": true, 15 | "strictNullChecks": false, 16 | "noImplicitThis": false, 17 | "alwaysStrict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": false, 20 | "noImplicitReturns": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["./src/**/*.ts"], 24 | "files": ["./types/phaser.d.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "lib-test" 5 | }, 6 | "include": ["./src/**/*.ts", "./test/**/*.ts"], 7 | "files": ["./types/phaser.d.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-config-prettier"], 3 | "rules": { 4 | "ban-types": [false], 5 | "class-name": true, 6 | "comment-format": [true, "check-space"], 7 | "curly": true, 8 | "forin": true, 9 | "label-position": true, 10 | "member-access": true, 11 | "member-ordering": [ 12 | true, 13 | { 14 | "order": [ 15 | "static-field", 16 | "instance-field", 17 | "constructor", 18 | "public-instance-method", 19 | "protected-instance-method", 20 | "private-instance-method" 21 | ] 22 | } 23 | ], 24 | "no-angle-bracket-type-assertion": false, 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 28 | "no-construct": true, 29 | "no-debugger": true, 30 | "no-duplicate-variable": true, 31 | "no-empty": false, 32 | "no-eval": true, 33 | "no-inferrable-types": false, 34 | "no-shadowed-variable": true, 35 | "no-string-literal": true, 36 | "no-switch-case-fall-through": false, 37 | "no-this-assignment": false, 38 | "no-unused-expression": true, 39 | "no-use-before-declare": true, 40 | "no-var-keyword": true, 41 | "object-literal-sort-keys": true, 42 | "ordered-imports": false, 43 | "only-arrow-functions": [false], 44 | "prefer-const": false, 45 | "radix": true, 46 | "trailing-comma": [false], 47 | "triple-equals": [true, "allow-null-check"], 48 | "variable-name": false 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tslint.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "./tslint.json" 4 | ], 5 | "rules": { 6 | "no-implicit-dependencies": false, 7 | "no-reference": false, 8 | "no-submodule-imports": false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const path = require("path"); 3 | const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); 4 | 5 | module.exports = env => { 6 | if (!env) env = { production: false, karma: false }; 7 | 8 | let mode = env.production ? "production" : "development"; 9 | let tsconfig = !env.karma ? "tsconfig.json" : "tsconfig.test.json"; 10 | let output = env.production ? "dist" : "dist-test"; 11 | let filename = env.production ? "ninepatch.min.js" : "ninepatch.js"; 12 | 13 | return { 14 | mode: mode, 15 | 16 | entry: { 17 | main: path.join(__dirname, "src/index.ts") 18 | }, 19 | 20 | output: { 21 | path: path.join(__dirname, output), 22 | filename: filename, 23 | 24 | libraryTarget: "var", 25 | library: "Phaser3Ninepatch" 26 | }, 27 | 28 | devtool: env.production ? undefined : "inline-source-map", 29 | 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.ts$/, 34 | loader: "ts-loader?configFile=" + tsconfig 35 | }, 36 | { 37 | test: env.production /* disable this loader for production builds */ ? /^$/ : /^(.(?!\.test))*\.ts$/, 38 | loader: "istanbul-instrumenter-loader", 39 | query: { 40 | embedSource: true 41 | }, 42 | enforce: "post" 43 | } 44 | ] 45 | }, 46 | 47 | plugins: env.production ? [] : [new webpack.SourceMapDevToolPlugin({ test: /\.ts$/i })], 48 | 49 | optimization: env.production 50 | ? { 51 | concatenateModules: true, 52 | minimize: true, 53 | minimizer: [ 54 | new UglifyJsPlugin({ 55 | cache: true, 56 | parallel: 4, 57 | uglifyOptions: { 58 | output: { 59 | comments: false 60 | } 61 | } 62 | }) 63 | ] 64 | } 65 | : {}, 66 | resolve: { 67 | extensions: [".ts", ".js", ".json"] 68 | } 69 | }; 70 | }; 71 | -------------------------------------------------------------------------------- /webpack.example.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const path = require("path"); 3 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 4 | const ConcatPlugin = require("webpack-concat-plugin"); 5 | 6 | const concatPluginConfigGenerator = (name, files) => { 7 | return { 8 | uglify: false, 9 | sourceMap: false, 10 | name: name, 11 | fileName: "[name].js", 12 | filesToConcat: files, 13 | injectType: "none" 14 | }; 15 | }; 16 | 17 | module.exports = (function(options) { 18 | return { 19 | mode: "development", 20 | 21 | entry: { 22 | main: path.resolve("example/index.ts") 23 | }, 24 | 25 | output: { 26 | path: __dirname + "/dist", 27 | filename: "bundle.js" 28 | }, 29 | 30 | devtool: "source-map", 31 | 32 | module: { 33 | rules: [{ test: /\.ts$/, loader: "ts-loader" }] 34 | }, 35 | 36 | plugins: [ 37 | new HtmlWebpackPlugin({ 38 | template: path.resolve("static/index.html"), 39 | inject: false 40 | }), 41 | 42 | new ConcatPlugin(concatPluginConfigGenerator("phaser", [path.resolve(__dirname, "./node_modules/phaser/dist/phaser.js")])) 43 | ], 44 | 45 | resolve: { 46 | extensions: [".ts", ".js", ".json"] 47 | } 48 | }; 49 | })(); 50 | --------------------------------------------------------------------------------