├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .snyk ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets ├── paths │ └── data.json └── sprites │ └── phaser-dude.png ├── dist ├── PathBuilder.js ├── PathBuilder.min.js └── vendor.js ├── examples └── test.js ├── index.html ├── lib └── dat.gui.js ├── package-lock.json ├── package.json ├── phaser.min.js ├── src ├── Scene.js ├── UI │ ├── Button.js │ ├── Element.js │ ├── Label.js │ ├── Menu.js │ ├── Point │ │ ├── ControlPoint.js │ │ ├── EndPoint.js │ │ └── Point.js │ ├── Pointer.js │ ├── Toggle.js │ └── UI.js └── main.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/phaser-arcade-physics.js 2 | src/animations/config.json 3 | src/physics/matter-js/lib/ 4 | src/physics/matter-js/poly-decomp/ 5 | src/polyfills/ 6 | src/renderer/webgl/shaders/ 7 | src/geom/polygon/Earcut.js 8 | src/utils/array/StableSort.js 9 | src/utils/object/Extend.js 10 | src/structs/RTree.js 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "es6": true, 6 | "commonjs": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "globals": { 10 | "WEBGL_RENDERER": true, 11 | "CANVAS_RENDERER": true, 12 | "Phaser": true, 13 | "p2": true, 14 | "process": true, 15 | "ActiveXObject": true 16 | }, 17 | "rules": { 18 | 19 | "no-cond-assign": [ "error", "except-parens" ], 20 | "no-duplicate-case": [ "error" ], 21 | 22 | "accessor-pairs": "error", 23 | "curly": "error", 24 | "eqeqeq": [ "error", "smart" ], 25 | "no-alert": "error", 26 | "no-caller": "error", 27 | "no-console": [ "error", { "allow": ["warn", "log"] } ], 28 | "no-floating-decimal": "error", 29 | "no-invalid-this": "error", 30 | "no-multi-spaces": "error", 31 | "no-multi-str": "error", 32 | "no-new-func": "error", 33 | "no-new-wrappers": "error", 34 | "no-redeclare": "error", 35 | "no-self-assign": "error", 36 | "no-self-compare": "error", 37 | "yoda": [ "error", "never" ], 38 | 39 | "array-bracket-spacing": [ "error", "always" ], 40 | "block-spacing": [ "error", "always" ], 41 | "brace-style": [ "error", "allman", { "allowSingleLine": true } ], 42 | "camelcase": "error", 43 | "comma-dangle": [ "error", "never" ], 44 | "comma-style": [ "error", "last" ], 45 | "computed-property-spacing": [ "error", "never" ], 46 | "consistent-this": [ "error", "_this" ], 47 | "eol-last": [ "error" ], 48 | "func-call-spacing": [ "error", "never" ], 49 | "indent": [ "error", 4, { "SwitchCase": 1 } ], 50 | "key-spacing": [ "error", { "beforeColon": false, "afterColon": true }], 51 | "linebreak-style": [ "off" ], 52 | "lines-around-comment": [ "error", { "beforeBlockComment": true, "afterBlockComment": false, "beforeLineComment": true, "afterLineComment": false, "allowBlockStart": true, "allowBlockEnd": false, "allowObjectStart": true, "allowArrayStart": true }], 53 | "new-parens": "error", 54 | "no-array-constructor": "error", 55 | "no-lonely-if": "error", 56 | "no-mixed-spaces-and-tabs": "error", 57 | "no-plusplus": "off", 58 | "no-trailing-spaces": [ "error", { "skipBlankLines": true, "ignoreComments": true } ], 59 | "no-underscore-dangle": "off", 60 | "no-whitespace-before-property": "error", 61 | "object-curly-newline": [ "error", { "multiline": true, "minProperties": 0 } ], 62 | "one-var-declaration-per-line": [ "error", "initializations" ], 63 | "quote-props": [ "error", "as-needed" ], 64 | "quotes": [ "error", "single" ], 65 | "semi-spacing": [ "error", { "before": false, "after": true } ], 66 | "semi": [ "error", "always" ], 67 | "space-before-blocks": "error", 68 | "space-before-function-paren": "error", 69 | "space-in-parens": [ "error", "never" ], 70 | "space-infix-ops": [ "error", { "int32Hint": true } ], 71 | "wrap-regex": "error", 72 | "spaced-comment": [ "error", "always", { "block": { "balanced": true, "exceptions": ["*", "!"] }} ] 73 | 74 | }, 75 | "parserOptions": { 76 | "sourceType": "module" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | This Issue is about (delete as applicable) 2 | 3 | * A bug in the API (always say which Plugin & Phaser version you're using!) 4 | * A problem with my own code 5 | * A feature request 6 | 7 | Feel free to use A [CodePen](https://codepen.io/Samid737/pen/pLVXag) template to demo the problem. 8 | 9 | Describe the Issue below: 10 | 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ** >> Make sure you describe your PR to the [CHANGELOG](https://github.com/samid737/phaser3-plugin-pathbuilder/blob/master/CHANGELOG.md)! << ** 2 | 3 | This PR changes (delete as applicable) 4 | 5 | * The public-facing API 6 | * Nothing, it's a bug fix 7 | 8 | Describe the changes below: 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System and IDE files 2 | Thumbs.db 3 | .DS_Store 4 | .idea 5 | *.suo 6 | *.sublime-project 7 | *.sublime-workspace 8 | 9 | # Vendors 10 | node_modules/ 11 | 12 | # Build 13 | build/ 14 | /npm-debug.log 15 | 16 | /gifs 17 | phaser.js 18 | 19 | UNRELEASED.MD 20 | .vscode/settings.json 21 | phaser.min_old.js 22 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.13.5 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | SNYK-JS-LODASH-450202: 7 | - webpack-dev-server > http-proxy-middleware > lodash: 8 | patched: '2019-07-04T05:21:19.332Z' 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Version 1.8.0 - March 24th 2019 4 | 5 | ### Features 6 | 7 | - 8 | 9 | ### Updates 10 | 11 | - Upgrade to Phaser 3.16.2. 12 | 13 | ### Bug Fixes 14 | 15 | - Fixed pointer problems due to API changes since 3.16.2, related to the dragState property. described in [3.16.1| https://github.com/photonstorm/phaser/releases/tag/v3.16.1] release notes. 16 | 17 | - 18 | 19 | ### Thanks 20 | 21 | - 22 | 23 | ## Version 1.7.5 -November 10th 2018 24 | 25 | ### Features 26 | 27 | - 28 | 29 | ### Updates 30 | 31 | - Upgrade to Phaser 3.15. 32 | 33 | ### Bug Fixes 34 | 35 | - Fixed global reference to game object causing undefined errors. 36 | 37 | ### Thanks 38 | 39 | - 40 | 41 | ## Version 1.7.3 - September 8th 2018 42 | 43 | ### Features 44 | 45 | - 46 | 47 | ### Updates 48 | 49 | - Upgrade to Phaser 3.1.2. 50 | 51 | ### Bug Fixes 52 | 53 | - Fixed import and export buttons by correcting callback function reference. Fix #1 (thanks @FeH9). 54 | 55 | ### Thanks 56 | 57 | - @FeH9 58 | 59 | ## Version 1.7.1 - August 4th 2018 60 | 61 | ### Features 62 | 63 | - 64 | 65 | ### Updates 66 | 67 | - Upgrade to Phaser 3.1.1. 68 | - Integrate Phaser 3.1.1 Camera API. 69 | 70 | ### Bug Fixes 71 | 72 | - 73 | 74 | ### Thanks 75 | 76 | - 77 | 78 | ## Version 1.7.0 - Juli 8th 2018 79 | 80 | ### Features 81 | 82 | - Added snap button to toggle grid snapping. 83 | - Added toggle button class. 84 | 85 | ### Updates 86 | 87 | - Switch to ES6 classes. 88 | - refactor place() function. 89 | - Integrate ES6 plugin API. 90 | 91 | ### Bug Fixes 92 | 93 | - 94 | 95 | ### Thanks 96 | 97 | - 98 | 99 | ## Version 1.6.1 - June 17th 2018 100 | 101 | ### Features 102 | 103 | - 104 | 105 | ### Updates 106 | 107 | - Update to Phaser 3.10.1 108 | - Integrate API changes. 109 | - Added a Menu class (container). 110 | - Remove obsolete pointer override code. 111 | 112 | ### Bug Fixes 113 | 114 | - 115 | 116 | ### Thanks 117 | 118 | - 119 | 120 | 121 | ## Version 1.6.0 - May 20th 2018 122 | 123 | ### Features 124 | 125 | - 126 | 127 | ### Updates 128 | 129 | - Update to Phaser 3.8.0. 130 | - Integrate API changes. 131 | - Added ES5 Branch. 132 | 133 | ### Bug Fixes 134 | 135 | - Fixed issue where middle mouse button press would place A point. 136 | - Fixed issue where right mouse button press would undo A placement. 137 | 138 | ### Thanks 139 | 140 | - 141 | 142 | ## Version 1.5.0 - May 11th 2018 143 | 144 | ### Features 145 | 146 | - Added Scene explorer. 147 | - Added reset view button. 148 | - Added camera zooming using mouse scroll. 149 | - Added mouse cursors for UX. 150 | 151 | ### Updates 152 | 153 | - Update to Phaser 3.6.0. 154 | - Added DOM mousewheel scroll. 155 | - Right mouse button for selection. 156 | - Middle mouse button for hand tool. 157 | 158 | ### Bug Fixes 159 | 160 | - Duplicate tween previews. 161 | 162 | ### Thanks 163 | 164 | - 165 | 166 | ## Version 1.1.3 - April 2th 2018 167 | 168 | ### Features 169 | 170 | - Added main scene pause resume buttons. 171 | 172 | ### Updates 173 | 174 | - Update to Phaser 3.3.0 175 | - UI aligned with canvas size. 176 | 177 | ### Bug Fixes 178 | 179 | - Fixed issue with preview button calling .start instead of .startFollow. 180 | 181 | ### Thanks 182 | 183 | - 184 | 185 | ## Version 1.1.0 - March 25th 2018 186 | 187 | ### Features 188 | 189 | - Added curve undo option. 190 | 191 | ### Updates 192 | 193 | - Pointer selection becomes inactive when moving away from menu. 194 | 195 | ### Bug Fixes 196 | 197 | - Fixed issue where preview crashed after clearing path. 198 | - Fixed issue where Spline was not drawn after clearing path. 199 | - Fixed issue where Ellipse was not drawn when placing. 200 | - Unwanted curve when entering select mode fixed. 201 | 202 | ### Thanks 203 | 204 | - 205 | 206 | ## Version 1.0.0 - March 19th 2018 207 | 208 | ### Features 209 | 210 | * Snap holding S key. 211 | * Clear path option. 212 | 213 | ### Updates 214 | 215 | ### Bug Fixes 216 | 217 | ### Thanks 218 | 219 | @samid737 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 samid737 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phaser 3 Plugin Path Builder 2 | 3 | NOTE: I notice there is not much activity on issues and can imagine issues exist. Should you encounter issues, please do submit them and I will look into it! Also any suggestions or feature requests that you would like to see, feel free to submit them via issues. Much appreciated. 4 | 5 | A tool to build paths for [Pathfollowers](https://labs.phaser.io/index.html?dir=paths/followers/) and path [tweens](https://labs.phaser.io/index.html?dir=paths/). Draw and edit Lines, Bezier Curves, Splines and Ellipses during runtime and export them to Phaser. [Demo](https://samid737.github.io/pathbuilder/). 6 | 7 | ![](https://media.giphy.com/media/p3AwdolG7NFjTGTE3P/giphy.gif) 8 | 9 | ## Description 10 | 11 | * Draw your path in-game. 12 | * Pause, resume and explore your scene while drawing paths. 13 | * Export created path to JSON. 14 | * Import existing paths from JSON. 15 | * Load path into Phaser and create awesome animations. 16 | * 90% Phaser API code. 17 | 18 | ## Usage 19 | 20 | 1. Grab the `PathBuilder.js` or `PathBuilder.min.js` file inside the `dist` folder. 21 | 22 | 2. Load it in Phaser. 23 | ``` 24 | function preload () 25 | { 26 | this.load.plugin({key:'PathBuilder', url:"/PathBuilder.js",mapping:'PathBuilder'}); 27 | //or if using minified: 28 | //this.load.plugin({key:'PathBuilder.min', url:"/PathBuilder.min.js",mapping:'PathBuilder'}); 29 | } 30 | ``` 31 | * UI: 32 | 33 | * Controls: 34 | * Use left mouse to draw. 35 | * Zoom and move trough scene using middle mouse and mousewheel. 36 | * Use right mouse to select different curves. 37 | 38 | * Buttons: 39 | * Undo : Undo the previous path draw. 40 | * Clear : Clear the entire path. 41 | * Draw : Enter draw mode. 42 | * Hide : Hide Plugin. 43 | * Show : Show Plugin. 44 | * Pause : Pause main scene. 45 | * Resume : Resume main scene. 46 | * Reset view : Reset the camera. 47 | * Snap : Snap mouse to a fixed grid. 48 | * Import : Import existing path (JSON). 49 | * Export : Save the current path as JSON file. 50 | 51 | ### Having questions or problems with usage, suggest a feature? Please submit an [Issue](https://github.com/samid737/phaser3-plugin-pathbuilder/issues/new). 52 | 53 | ## Requirements: 54 | 55 | * Phaser 3, latest recommended to guarantee matching API. 56 | * Mouse with middle mouse button if you want to explore A scene. 57 | * Chrome browser recommended. 58 | 59 | ## Dev notes 60 | 61 | Run `npm install` and then `npm run build` to build the plugin. Use `npm run dev` to run webpack-dev-server on port 8000. 62 | 63 | There is an existing ES5 branch, but it is no longer maintained since v.1.6.1: 64 | 65 | https://github.com/samid737/phaser3-plugin-pathbuilder/tree/ES5 66 | 67 | Contributions, optimizations, suggestions are very welcome. All changes are found in the [Changelog](https://github.com/samid737/phaser3-plugin-pathbuilder/blob/master/CHANGELOG.md); 68 | 69 | [Phaser 3](https://github.com/photonstorm/phaser). 70 | 71 | -------------------------------------------------------------------------------- /assets/paths/data.json: -------------------------------------------------------------------------------- 1 | {"type":"Path","x":0,"y":0,"autoClose":false,"curves":[{"type":"CubicBezierCurve","points":[477,189,457,210,445,236,408,247]},{"type":"CubicBezierCurve","points":[408,247,372,300,412,314,454,341]},{"type":"CubicBezierCurve","points":[454,341,453.25,359.25,399,364,460,420]},{"type":"CubicBezierCurve","points":[460,420,463.75,413,466,355,492,416]},{"type":"CubicBezierCurve","points":[492,416,548,382,505.75,359,507,342]},{"type":"CubicBezierCurve","points":[507,342,524,336,560,305,549,262]},{"type":"CubicBezierCurve","points":[549,262,553,242,500,232,480,189]}]} -------------------------------------------------------------------------------- /assets/sprites/phaser-dude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samid737/phaser3-plugin-pathbuilder/2e7a078ddbb258e3eccbe698555dba62349e7af7/assets/sprites/phaser-dude.png -------------------------------------------------------------------------------- /dist/PathBuilder.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.PathBuilder=t():e.PathBuilder=t()}(window,function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist/",n(n.s=5)}([ 2 | /*!******************!*\ 3 | !*** ./UI/UI.js ***! 4 | \******************/ 5 | /*! no static exports found */ 6 | /*! all exports used */ 7 | /*! ModuleConcatenation bailout: Module is not an ECMAScript module */function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n3?s-3:0),l=3;l50&&e.x150&&this.scene.switchmode("draw"),"select"!==this.scene.mode&&(this.x=Math.round(this.x/this.delta)*this.delta,this.y=Math.round(this.y/this.delta)*this.delta,this.lbl.setPosition(this.x+20,this.y+20),this.lbl.setText("x: "+this.x+" y: "+this.y))}}]),t}();t.default=a}, 32 | /*!*****************!*\ 33 | !*** ./main.js ***! 34 | \*****************/ 35 | /*! no static exports found */ 36 | /*! all exports used */ 37 | /*! ModuleConcatenation bailout: Module is not an ECMAScript module */function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathBuilder=void 0;var i=function(){function e(e,t){for(var n=0;n1){var e=this;this.path.curves[this.path.curves.length-1].controlpoints.forEach(function(t){t.destroy(),e.vectors.pop()}),this.path.curves.pop(),this.spline=null,this.graphics.clear(),this.draw()}}},{key:"place",value:function(e,t,n){if(0!=this.vectors.length){if("Line"==this.drawmode){this.spline=null;var i=this.vectors[this.vectors.length-1],o=e.add.endpoint(t,n);if(this.vectors.length>0){var r=new this.curves[this.drawmode](i,o.vec2);this.path.add(r),r.controlpoints=[],o.fuse(r)}}if("QuadraticBezier"==this.drawmode){this.spline=null;var s=this.vectors[this.vectors.length-1],a=e.add.controlpoint(s.x+.5*(t-s.x),s.y+.5*(n-s.y)),l=e.add.endpoint(t,n),u=new this.curves[this.drawmode](s,a.vec2,l.vec2);this.path.add(u),u.controlpoints=[],a.fuse(u),l.fuse(u)}if("CubicBezier"==this.drawmode){this.spline=null;var c=this.vectors[this.vectors.length-1],h=e.add.controlpoint(c.x+.25*(t-c.x),c.y+.25*(n-c.y)),f=e.add.controlpoint(c.x+.75*(t-c.x),c.y+.75*(n-c.y)),p=e.add.endpoint(t,n),d=new this.curves[this.drawmode](c,h.vec2,f.vec2,p.vec2);this.path.add(d),d.controlpoints=[],h.fuse(d),f.fuse(d),p.fuse(d)}if("Spline"==this.drawmode){var y=this.vectors[this.vectors.length-1],v=0;if(null==this.spline){v=e.add.endpoint(t,n);var b=new this.curves[this.drawmode]([y.x,y.y,v.vec2.x,v.vec2.y]);this.spline=b,this.path.add(b),b.controlpoints=[]}this.path.segments+=8,1==this.vectors.length?v=e.add.controlpoint(t,n):(v=e.add.controlpoint(t,n),this.spline.addPoints([t,n])),this.spline.points[this.spline.points.length-1]=v.vec2,this.spline.points[this.spline.points.length-2]=y,v.fuse(this.spline)}if("Ellipse"==this.drawmode){this.spline=null;this.vectors[this.vectors.length-1];var m=e.add.endpoint(t,n),w=e.add.controlpoint(t+100,n+100),g=new this.curves[this.drawmode](m.vec2.x,m.vec2.y,100,100);this.path.add(g),g.controlpoints=[],m.fuse(g),w.fuse(g),g.p0=m,w.map({src:g,data:{x:{property:"xRadius",operator:function(e,t){return e.p0.vec2.x-t}},y:{property:"yRadius",operator:function(e,t){return e.p0.vec2.y-t}}}})}this.draw(),this.setCameras()}else e.add.endpoint(t,n)}},{key:"move",value:function(e,t){}},{key:"draw",value:function(){this.graphics.clear(),this.graphics.lineStyle(2,16777215,1).fillStyle(16777215,1),this.path.draw(this.graphics,this.path.segments)}},{key:"look",value:function(e){e.scrollY=this.pointer.lastY-this.input.activePointer.y,e.scrollX=this.pointer.lastX-this.input.activePointer.x}},{key:"scroll",value:function(e){var t=0;return e.wheelDelta?t=e.wheelDelta/120:e.detail&&(t=-e.detail/3),t>0&&(this.gameCanvas.style.cursor="zoom-in"),t<0&&(this.gameCanvas.style.cursor="zoom-out"),this.time.delayedCall(250,this.pointer.switchCursor,[],this.pointer),this.drawpanel.camera.setZoom(this.drawpanel.camera.zoom+.1*t),this.drawpanel.camera.zoom+=.1*t,this.supercamera.zoom+=.1*t,t}},{key:"resetView",value:function(){this.drawpanel.camera.setZoom(1).setScroll(0,0),this.supercamera.setZoom(1).setScroll(0,0)}},{key:"freeze",value:function(){this.scene.manager.scenes[0].scene.pause()}},{key:"unfreeze",value:function(){this.scene.manager.scenes[0].scene.resume()}},{key:"importJSON",value:function(){var e=this.cache.json.get("data");this.path=this.path.fromJSON(e),this.draw()}},{key:"exportJSON",value:function(){var e=JSON.stringify(this.path.toJSON());console.log(e);var t=new Blob([e],["path.json"]),n=document.createElement("a");n.href=URL.createObjectURL(t),n.download="path.json",n.click()}},{key:"preview",value:function(){if(0!==this.path.curves.length){var e=this.add.follower(this.path,0,0,"dude");this.cameras.main.ignore(e),this.middle.camera.ignore(e),this.top.camera.ignore(e),e.startFollow({duration:2e3,rotateToPath:!0,yoyo:!0,onComplete:function(e,t,n){n.destroy()},onCompleteParams:[e],ease:"Cubic.easeInOut"})}}},{key:"destroy",value:function(){}}]),t}();t.default=r}, 44 | /*!********************!*\ 45 | !*** ./UI/Menu.js ***! 46 | \********************/ 47 | /*! no static exports found */ 48 | /*! all exports used */ 49 | /*! ModuleConcatenation bailout: Module is not an ECMAScript module */function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n3&&void 0!==arguments[3]?arguments[3]:"endpoint";!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var a=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,i,r));return a.lbl.setFontStyle(o.default.fonts.EndPoint),s(a,a)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,i.default),t}();t.default=a}, 62 | /*!**********************************!*\ 63 | !*** ./UI/Point/ControlPoint.js ***! 64 | \**********************************/ 65 | /*! no static exports found */ 66 | /*! all exports used */ 67 | /*! ModuleConcatenation bailout: Module is not an ECMAScript module */function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(/*! ./Point */2)),o=r(n(/*! ../UI */0));function r(e){return e&&e.__esModule?e:{default:e}}function s(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}var a=function(e){function t(e,n,i){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"controlpoint";!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var a=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,i,r));return a.setScale(.75,.75),a.lbl.setFontStyle(o.default.fonts.ControlPoint),a.lbl.visible=!1,a.on("pointermove",function(e,t){this.lbl.visible=!0}),a.on("pointerout",function(e,t){this.lbl.visible=!1}),a.on("dragend",function(e,t){this.lbl.visible=!1}),s(a,a)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,i.default),t}();t.default=a}, 68 | /*!*********************!*\ 69 | !*** ./UI/Label.js ***! 70 | \*********************/ 71 | /*! no static exports found */ 72 | /*! all exports used */ 73 | /*! ModuleConcatenation bailout: Module is not an ECMAScript module */function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(/*! ./Element */1)),o=r(n(/*! ./UI */0));function r(e){return e&&e.__esModule?e:{default:e}}function s(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}var a=function(e){function t(e,n,i,r,a,l,u,c){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var h=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,i,r,o.default.fonts.Label));return h.target=a,h.callbacks=l,h.callbackcontext=c,h.args=u,h.tween=null,h.setInteractive(),s(h,h)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,(0,i.default)(Phaser.GameObjects.Text)),t}();t.default=a}])}); -------------------------------------------------------------------------------- /dist/PathBuilder.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports["PathBuilder.min"]=t():e["PathBuilder.min"]=t()}(window,function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist/",n(n.s=5)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n3?s-3:0),l=3;l50&&e.x150&&this.scene.switchmode("draw"),"select"!==this.scene.mode&&(this.x=Math.round(this.x/this.delta)*this.delta,this.y=Math.round(this.y/this.delta)*this.delta,this.lbl.setPosition(this.x+20,this.y+20),this.lbl.setText("x: "+this.x+" y: "+this.y))}}]),t}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathBuilder=void 0;var i=function(){function e(e,t){for(var n=0;n1){var e=this;this.path.curves[this.path.curves.length-1].controlpoints.forEach(function(t){t.destroy(),e.vectors.pop()}),this.path.curves.pop(),this.spline=null,this.graphics.clear(),this.draw()}}},{key:"place",value:function(e,t,n){if(0!=this.vectors.length){if("Line"==this.drawmode){this.spline=null;var i=this.vectors[this.vectors.length-1],o=e.add.endpoint(t,n);if(this.vectors.length>0){var r=new this.curves[this.drawmode](i,o.vec2);this.path.add(r),r.controlpoints=[],o.fuse(r)}}if("QuadraticBezier"==this.drawmode){this.spline=null;var s=this.vectors[this.vectors.length-1],a=e.add.controlpoint(s.x+.5*(t-s.x),s.y+.5*(n-s.y)),l=e.add.endpoint(t,n),u=new this.curves[this.drawmode](s,a.vec2,l.vec2);this.path.add(u),u.controlpoints=[],a.fuse(u),l.fuse(u)}if("CubicBezier"==this.drawmode){this.spline=null;var c=this.vectors[this.vectors.length-1],h=e.add.controlpoint(c.x+.25*(t-c.x),c.y+.25*(n-c.y)),f=e.add.controlpoint(c.x+.75*(t-c.x),c.y+.75*(n-c.y)),p=e.add.endpoint(t,n),d=new this.curves[this.drawmode](c,h.vec2,f.vec2,p.vec2);this.path.add(d),d.controlpoints=[],h.fuse(d),f.fuse(d),p.fuse(d)}if("Spline"==this.drawmode){var y=this.vectors[this.vectors.length-1],v=0;if(null==this.spline){v=e.add.endpoint(t,n);var b=new this.curves[this.drawmode]([y.x,y.y,v.vec2.x,v.vec2.y]);this.spline=b,this.path.add(b),b.controlpoints=[]}this.path.segments+=8,1==this.vectors.length?v=e.add.controlpoint(t,n):(v=e.add.controlpoint(t,n),this.spline.addPoints([t,n])),this.spline.points[this.spline.points.length-1]=v.vec2,this.spline.points[this.spline.points.length-2]=y,v.fuse(this.spline)}if("Ellipse"==this.drawmode){this.spline=null,this.vectors[this.vectors.length-1];var m=e.add.endpoint(t,n),w=e.add.controlpoint(t+100,n+100),g=new this.curves[this.drawmode](m.vec2.x,m.vec2.y,100,100);this.path.add(g),g.controlpoints=[],m.fuse(g),w.fuse(g),g.p0=m,w.map({src:g,data:{x:{property:"xRadius",operator:function(e,t){return e.p0.vec2.x-t}},y:{property:"yRadius",operator:function(e,t){return e.p0.vec2.y-t}}}})}this.draw(),this.setCameras()}else e.add.endpoint(t,n)}},{key:"move",value:function(e,t){}},{key:"draw",value:function(){this.graphics.clear(),this.graphics.lineStyle(2,16777215,1).fillStyle(16777215,1),this.path.draw(this.graphics,this.path.segments)}},{key:"look",value:function(e){e.scrollY=this.pointer.lastY-this.input.activePointer.y,e.scrollX=this.pointer.lastX-this.input.activePointer.x}},{key:"scroll",value:function(e){var t=0;return e.wheelDelta?t=e.wheelDelta/120:e.detail&&(t=-e.detail/3),t>0&&(this.gameCanvas.style.cursor="zoom-in"),t<0&&(this.gameCanvas.style.cursor="zoom-out"),this.time.delayedCall(250,this.pointer.switchCursor,[],this.pointer),this.drawpanel.camera.setZoom(this.drawpanel.camera.zoom+.1*t),this.drawpanel.camera.zoom+=.1*t,this.supercamera.zoom+=.1*t,t}},{key:"resetView",value:function(){this.drawpanel.camera.setZoom(1).setScroll(0,0),this.supercamera.setZoom(1).setScroll(0,0)}},{key:"freeze",value:function(){this.scene.manager.scenes[0].scene.pause()}},{key:"unfreeze",value:function(){this.scene.manager.scenes[0].scene.resume()}},{key:"importJSON",value:function(){var e=this.cache.json.get("data");this.path=this.path.fromJSON(e),this.draw()}},{key:"exportJSON",value:function(){var e=JSON.stringify(this.path.toJSON());console.log(e);var t=new Blob([e],["path.json"]),n=document.createElement("a");n.href=URL.createObjectURL(t),n.download="path.json",n.click()}},{key:"preview",value:function(){if(0!==this.path.curves.length){var e=this.add.follower(this.path,0,0,"dude");this.cameras.main.ignore(e),this.middle.camera.ignore(e),this.top.camera.ignore(e),e.startFollow({duration:2e3,rotateToPath:!0,yoyo:!0,onComplete:function(e,t,n){n.destroy()},onCompleteParams:[e],ease:"Cubic.easeInOut"})}}},{key:"destroy",value:function(){}}]),t}();t.default=r},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n3&&void 0!==arguments[3]?arguments[3]:"endpoint";!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var a=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,i,r));return a.lbl.setFontStyle(o.default.fonts.EndPoint),s(a,a)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,i.default),t}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(2)),o=r(n(0));function r(e){return e&&e.__esModule?e:{default:e}}function s(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}var a=function(e){function t(e,n,i){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"controlpoint";!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var a=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,i,r));return a.setScale(.75,.75),a.lbl.setFontStyle(o.default.fonts.ControlPoint),a.lbl.visible=!1,a.on("pointermove",function(e,t){this.lbl.visible=!0}),a.on("pointerout",function(e,t){this.lbl.visible=!1}),a.on("dragend",function(e,t){this.lbl.visible=!1}),s(a,a)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,i.default),t}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(1)),o=r(n(0));function r(e){return e&&e.__esModule?e:{default:e}}function s(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}var a=function(e){function t(e,n,i,r,a,l,u,c){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var h=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,i,r,o.default.fonts.Label));return h.target=a,h.callbacks=l,h.callbackcontext=c,h.args=u,h.tween=null,h.setInteractive(),s(h,h)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,(0,i.default)(Phaser.GameObjects.Text)),t}();t.default=a}])}); -------------------------------------------------------------------------------- /dist/vendor.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define("PathBuilder", [], factory); 6 | else if(typeof exports === 'object') 7 | exports["PathBuilder"] = factory(); 8 | else 9 | root["PathBuilder"] = factory(); 10 | })(typeof self !== 'undefined' ? self : this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) { 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ } 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ i: moduleId, 25 | /******/ l: false, 26 | /******/ exports: {} 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.l = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // define getter function for harmony exports 47 | /******/ __webpack_require__.d = function(exports, name, getter) { 48 | /******/ if(!__webpack_require__.o(exports, name)) { 49 | /******/ Object.defineProperty(exports, name, { 50 | /******/ configurable: false, 51 | /******/ enumerable: true, 52 | /******/ get: getter 53 | /******/ }); 54 | /******/ } 55 | /******/ }; 56 | /******/ 57 | /******/ // getDefaultExport function for compatibility with non-harmony modules 58 | /******/ __webpack_require__.n = function(module) { 59 | /******/ var getter = module && module.__esModule ? 60 | /******/ function getDefault() { return module['default']; } : 61 | /******/ function getModuleExports() { return module; }; 62 | /******/ __webpack_require__.d(getter, 'a', getter); 63 | /******/ return getter; 64 | /******/ }; 65 | /******/ 66 | /******/ // Object.prototype.hasOwnProperty.call 67 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 68 | /******/ 69 | /******/ // __webpack_public_path__ 70 | /******/ __webpack_require__.p = ""; 71 | /******/ 72 | /******/ // Load entry module and return exports 73 | /******/ return __webpack_require__(__webpack_require__.s = 340); 74 | /******/ }) 75 | /************************************************************************/ 76 | /******/ ({ 77 | 78 | /***/ 340: 79 | /***/ (function(module, exports, __webpack_require__) { 80 | 81 | (function webpackMissingModule() { throw new Error("Cannot find module \"phaser\""); }()); 82 | 83 | 84 | /***/ }) 85 | 86 | /******/ }); 87 | }); -------------------------------------------------------------------------------- /examples/test.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | type: Phaser.CANVAS, 3 | parent: 'phaser-example', 4 | scene: { 5 | preload: preload, 6 | create: create, 7 | update: update 8 | }, 9 | 10 | width: 800, 11 | height: 600 12 | }; 13 | 14 | var game = new Phaser.Game(config); 15 | 16 | var player; 17 | 18 | function preload() 19 | { 20 | 21 | this.load.image('dude', 'assets/sprites/phaser-dude.png'); 22 | this.load.json('data', 'assets/paths/data.json'); 23 | 24 | // this.load.plugin({key:'PathBuilder.min', url:"dist/PathBuilder.min.js",mapping:'PathBuilder'}); 25 | this.load.plugin({key:'PathBuilder.min', url:"dist/PathBuilder.min.js",mapping:'PathBuilder'}); 26 | 27 | } 28 | 29 | function create() 30 | { 31 | 32 | this.cameras.main.setBackgroundColor(0x11155); 33 | 34 | player = this.add.image(400, 300, 'dude').setScale(6, 6); 35 | } 36 | 37 | function update() 38 | { 39 | player.x = Math.sin(this.time.now / 1000) * 200 + 400; 40 | } 41 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | Phaser 3 Plugin Path Builder 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phaser3-plugin-pathbuilder", 3 | "version": "1.8.0", 4 | "description": "Draw and edit Lines, Bezier Curves, Splines during runtime, explore your scene and export path tweens and PathFollowers", 5 | "main": "src/main.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "watch": "webpack --watch", 9 | "dist": "webpack --config webpack.dist.config.js", 10 | "lint": "eslint --config .eslintrc.json \"src/**/*.js\"", 11 | "lintfix": "eslint --config .eslintrc.json \"src/**/*.js\" --fix", 12 | "sloc": "node-sloc './src' --include-extensions \"js\"", 13 | "dev": "npm run build && webpack-dev-server --port=8000", 14 | "snyk-protect": "snyk protect", 15 | "prepare": "npm run snyk-protect" 16 | }, 17 | "keywords": [ 18 | "curves", 19 | "Curve", 20 | "Editor", 21 | "editor", 22 | "build", 23 | "generate", 24 | "JSON", 25 | "export", 26 | "Phaser 3", 27 | "Phaser", 28 | "plugin", 29 | "pathFollowers", 30 | "pahts", 31 | "tweens", 32 | "animation", 33 | "motion", 34 | "drawing", 35 | "scene", 36 | "explore", 37 | "editor" 38 | ], 39 | "repository": { 40 | "type": "git", 41 | "url": "git+https://github.com/samid737/phaser3-plugin-pathbuilder" 42 | }, 43 | "author": "samid737", 44 | "license": "MIT", 45 | "bugs": { 46 | "url": "https://github.com/samid737/phaser3-plugin-pathbuilder/issues" 47 | }, 48 | "homepage": "https://github.com/samid737/phaser3-plugin-pathbuilder#readme", 49 | "devDependencies": { 50 | "babel-preset-es2015": "^6.24.1", 51 | "clean-webpack-plugin": "^0.1.18", 52 | "eslint": "^4.19.1", 53 | "fs-extra": "^5.0.0", 54 | "node-sloc": "^0.1.11", 55 | "raw-loader": "^0.5.1", 56 | "uglifyjs-webpack-plugin": "^1.2.7", 57 | "uuid": "^3.3.2", 58 | "webpack": "^4.20.2", 59 | "webpack-cli": "^3.3.11" 60 | }, 61 | "dependencies": { 62 | "babel-core": "^6.26.3", 63 | "babel-eslint": "^9.0.0", 64 | "babel-loader": "^7.1.5", 65 | "babel-polyfill": "^6.26.0", 66 | "snyk": "^1.290.1", 67 | "webpack-dev-server": "^3.10.3" 68 | }, 69 | "snyk": true 70 | } 71 | -------------------------------------------------------------------------------- /src/Scene.js: -------------------------------------------------------------------------------- 1 | import UI from './UI/UI'; 2 | import Pointer from './UI/Pointer'; 3 | 4 | export default class Scene extends Phaser.Scene 5 | { 6 | 7 | constructor (config) 8 | { 9 | super(config); 10 | if (window.addEventListener) 11 | { 12 | window.addEventListener('DOMMouseScroll', this.scroll, false); 13 | window.onmousewheel = this.scroll.bind(this); 14 | } 15 | } 16 | 17 | preload () 18 | { 19 | 20 | } 21 | 22 | create () 23 | { 24 | this.gameCanvas = this.sys.canvas; 25 | 26 | // TODO: event driven modes, move 27 | this.tool = { normal: null, draw: this.place }; 28 | this.mode = 'normal'; 29 | this.cursors = { normal: 'default', draw: 'copy', select: 'default', hand: 'move' }; 30 | this.curves = { Line: Phaser.Curves.Line, Ellipse: Phaser.Curves.Ellipse, QuadraticBezier: Phaser.Curves.QuadraticBezier, CubicBezier: Phaser.Curves.CubicBezier, Spline: Phaser.Curves.Spline }; 31 | 32 | this.events.emit('switchmode', this.mode); 33 | 34 | this.input.mouse.disableContextMenu(); 35 | 36 | this.drawmode = 'CubicBezier'; 37 | 38 | // TODO: seperate class 39 | this.vectors = []; 40 | 41 | // reserved for spline building 42 | this.spline = null; 43 | this.path = this.add.path(); 44 | this.path.segments = 32; 45 | this.graphics = this.add.graphics(); 46 | 47 | this.drawpanel = this.add.ui(this); 48 | this.middle = this.add.ui(this); 49 | this.top = this.add.ui(this); 50 | 51 | // this.drawpanel.elements.push(this.graphics); 52 | this.graphics.fillStyle(0xff0000, 1).fillCircle(10, 10, 8).generateTexture('endpoint', 32, 32); 53 | this.graphics.fillStyle(0x00ff00, 1).fillCircle(10, 10, 8).generateTexture('controlpoint', 32, 32); 54 | this.graphics.clear(); 55 | 56 | this.pointer = this.add.superpointer(this.middle, 100, 100, 'controlpoint'); 57 | 58 | this.W = this.cameras.main.width; 59 | this.H = this.cameras.main.height; 60 | 61 | // TODO: rewrite callback implementation 62 | this.hidebutton = this.top.add.button(10, 300, 'hide', null, null, null, [ this.drawpanel.hide, this.middle.hide ], [], [ this.drawpanel, this.middle ]); 63 | this.showbutton = this.top.add.button(10, 350, 'show', null, null, null, [ this.drawpanel.show, this.middle.show ], [], [ this.drawpanel, this.middle ]); 64 | this.viewbutton = this.top.add.button(this.W - 100, this.H * 0.1, 'reset view', null, null, null, this.resetView, [], this); 65 | this.snapbutton = this.middle.add.toggle(this.W - 100, this.H * 0.2, 'snap', null, null, null, this.pointer.snap, [], this.pointer); 66 | 67 | this.drawbutton = this.middle.add.button(10, 200, 'draw', null, null, null, this.switchmode, [ 'draw' ], this); 68 | this.clearbutton = this.middle.add.button(10, 100, 'clear', null, null, null, this.clear, [], this); 69 | this.undobutton = this.middle.add.button(10, 50, 'undo', null, null, null, this.undo, [], this); 70 | 71 | this.importbutton = this.middle.add.button(this.W - 100, this.H - 200, 'import', null, null, null, this.importJSON, [], this); 72 | this.exportbutton = this.middle.add.button(this.W - 100, this.H - 100, 'export', null, null, null, this.exportJSON, [], this); 73 | 74 | this.pausebutton = this.middle.add.button(10, this.H - 200, 'pause', null, null, null, this.freeze, [], this); 75 | this.resumebutton = this.middle.add.button(10, this.H - 150, 'resume', null, null, null, this.unfreeze, [], this); 76 | 77 | this.previewbutton = this.middle.add.button(10, this.H - 100, 'preview', null, null, null, this.preview, [], this); 78 | 79 | this.modelabel = this.middle.add.label(100, 20, 'mode: ', null, null, null, null, this); 80 | this.drawmodelabel = this.middle.add.label(400, 20, 'curve: ' + this.drawmode, null, null, null, null, this); 81 | 82 | this.setCameras(); 83 | 84 | this.drawpanel.hide(); 85 | this.middle.hide(); 86 | 87 | } 88 | update () 89 | { 90 | this.drawpanel.update(); 91 | this.pointer.update(); 92 | 93 | // this.drawpanel.camera.setZoom(Math.sin(this.time.now/100000)+1); 94 | // this.cameras.main.setZoom(Math.sin(this.time.now/100000)+1); 95 | // this.middle.camera.setZoom(Math.sin(this.time.now/100000)+1); 96 | // this.top.camera.setZoom(Math.sin(this.time.now/100000)+1); 97 | } 98 | setCameras () 99 | { 100 | this.drawpanel.camera.ignore(this.middle.elements); 101 | this.drawpanel.camera.ignore(this.top.elements); 102 | 103 | this.middle.camera.ignore(this.graphics); 104 | this.middle.camera.ignore(this.drawpanel.elements); 105 | this.middle.camera.ignore(this.top.elements); 106 | 107 | this.top.camera.ignore(this.graphics); 108 | this.top.camera.ignore(this.middle.elements); 109 | this.top.camera.ignore(this.drawpanel.elements); 110 | 111 | this.cameras.main.ignore(this.children.list); 112 | 113 | this.supercamera = this.scene.manager.scenes[0].cameras.main; 114 | } 115 | switchmode (mode) 116 | { 117 | this.mode = mode; 118 | this.modelabel.setText('mode: ' + this.mode); 119 | this.pointer.switchCursor(); 120 | this.events.emit('switchmode', this.mode); 121 | } 122 | clear () 123 | { 124 | let _this = this; 125 | this.path.curves.forEach(function (curve) { _this.undo(); }); 126 | this.drawpanel.elements.forEach(function (element) { element.destroy(); }); 127 | this.vectors = []; 128 | this.drawpanel.elements = []; 129 | this.path.curves = []; 130 | this.spline = null; 131 | 132 | this.graphics.clear(); 133 | } 134 | undo () 135 | { 136 | // TODO: extend curve class 137 | if (this.vectors.length > 1) 138 | { 139 | let _this = this; 140 | let lastcurve = this.path.curves[this.path.curves.length - 1]; 141 | lastcurve.controlpoints.forEach(function (point) { point.destroy(); _this.vectors.pop(); }); 142 | 143 | this.path.curves.pop(); 144 | this.spline = null; 145 | 146 | this.graphics.clear(); 147 | this.draw(); 148 | } 149 | } 150 | place (ui, x, y) 151 | { 152 | // TODO: extend A curve class for each case, add A factory entry for curves. 153 | // TODO: add abstraction 154 | 155 | if (this.vectors.length == 0) 156 | { 157 | let p0 = ui.add.endpoint(x, y); 158 | return; 159 | } 160 | 161 | if (this.drawmode == 'Line') 162 | { 163 | 164 | this.spline = null; 165 | 166 | let p0 = this.vectors[this.vectors.length - 1]; 167 | 168 | let p1 = ui.add.endpoint(x, y); 169 | 170 | if (this.vectors.length > 0) 171 | { 172 | let c = new this.curves[this.drawmode](p0, p1.vec2); 173 | 174 | this.path.add(c); 175 | c.controlpoints = []; 176 | 177 | p1.fuse(c); 178 | 179 | } 180 | 181 | 182 | } 183 | 184 | if (this.drawmode == 'QuadraticBezier') 185 | { 186 | this.spline = null; 187 | 188 | let p0 = this.vectors[this.vectors.length - 1]; 189 | 190 | let p1 = ui.add.controlpoint(p0.x + (x - p0.x) * 0.5, p0.y + (y - p0.y) * 0.5); 191 | 192 | let p2 = ui.add.endpoint(x, y); 193 | 194 | let c = new this.curves[this.drawmode](p0, p1.vec2, p2.vec2); 195 | 196 | this.path.add(c); 197 | c.controlpoints = []; 198 | 199 | p1.fuse(c); 200 | p2.fuse(c); 201 | 202 | } 203 | 204 | if (this.drawmode == 'CubicBezier') 205 | { 206 | 207 | this.spline = null; 208 | 209 | let p0 = this.vectors[this.vectors.length - 1]; 210 | 211 | let p1 = ui.add.controlpoint(p0.x + (x - p0.x) * 0.25, p0.y + (y - p0.y) * 0.25); 212 | let p2 = ui.add.controlpoint(p0.x + (x - p0.x) * 0.75, p0.y + (y - p0.y) * 0.75); 213 | 214 | let p3 = ui.add.endpoint(x, y); 215 | 216 | let c = new this.curves[this.drawmode](p0, p1.vec2, p2.vec2, p3.vec2); 217 | 218 | this.path.add(c); 219 | c.controlpoints = []; 220 | 221 | p1.fuse(c); 222 | p2.fuse(c); 223 | p3.fuse(c); 224 | } 225 | 226 | if (this.drawmode == 'Spline') 227 | { 228 | 229 | let p0 = this.vectors[this.vectors.length - 1]; 230 | let px = 0; 231 | 232 | if (this.spline == null) 233 | { 234 | 235 | px = ui.add.endpoint(x, y); 236 | 237 | let c = new this.curves[this.drawmode]([ p0.x, p0.y, px.vec2.x, px.vec2.y ]); 238 | this.spline = c; 239 | 240 | this.path.add(c); 241 | c.controlpoints = []; 242 | 243 | } 244 | 245 | this.path.segments += 8; 246 | 247 | if (this.vectors.length == 1) 248 | { 249 | 250 | px = ui.add.controlpoint(x, y); 251 | 252 | } 253 | else 254 | { 255 | 256 | px = ui.add.controlpoint(x, y); 257 | 258 | this.spline.addPoints([ x, y ]); 259 | } 260 | 261 | this.spline.points[this.spline.points.length - 1] = px.vec2; 262 | this.spline.points[this.spline.points.length - 2] = p0; 263 | 264 | px.fuse(this.spline); 265 | 266 | } 267 | 268 | if (this.drawmode == 'Ellipse') 269 | { 270 | 271 | this.spline = null; 272 | 273 | let p0 = this.vectors[this.vectors.length - 1]; 274 | 275 | let p1 = ui.add.endpoint(x,y); 276 | let p2 = ui.add.controlpoint(x + 100, y + 100); 277 | 278 | let c = new this.curves[this.drawmode](p1.vec2.x, p1.vec2.y, 100, 100); 279 | 280 | this.path.add(c); 281 | c.controlpoints = []; 282 | 283 | p1.fuse(c); 284 | p2.fuse(c); 285 | 286 | c.p0 = p1; 287 | 288 | // map control point coordinates to radii 289 | 290 | p2.map({ 291 | src: c, 292 | data: 293 | { 294 | x: { property: 'xRadius', operator: function (src, x) { return src.p0.vec2.x - x; } }, 295 | y: { property: 'yRadius', operator: function (src, y) { return src.p0.vec2.y - y; } } 296 | } 297 | }); 298 | 299 | } 300 | 301 | this.draw(); 302 | 303 | this.setCameras(); 304 | } 305 | move (graphics, vector) 306 | { 307 | 308 | } 309 | draw () 310 | { 311 | this.graphics.clear(); 312 | this.graphics.lineStyle(2, 0xffffff, 1).fillStyle(0xffffff, 1); 313 | this.path.draw(this.graphics, this.path.segments); 314 | } 315 | look (camera) 316 | { 317 | 318 | camera.scrollY = (this.pointer.lastY - this.input.activePointer.y); 319 | camera.scrollX = (this.pointer.lastX - this.input.activePointer.x); 320 | } 321 | scroll (event) 322 | { 323 | let delta = 0; 324 | if (event.wheelDelta) 325 | { 326 | delta = event.wheelDelta / 120; 327 | } 328 | else if (event.detail) 329 | { 330 | delta = -event.detail / 3; 331 | } 332 | 333 | // TODO: move to pointer 334 | if (delta > 0) 335 | { 336 | this.gameCanvas.style.cursor = 'zoom-in'; 337 | } 338 | if (delta < 0) 339 | { 340 | this.gameCanvas.style.cursor = 'zoom-out'; 341 | } 342 | 343 | this.time.delayedCall(250, this.pointer.switchCursor, [], this.pointer); 344 | 345 | this.drawpanel.camera.setZoom(this.drawpanel.camera.zoom + delta * 0.1); 346 | this.drawpanel.camera.zoom += delta * 0.1; 347 | this.supercamera.zoom += delta * 0.1; 348 | return delta; 349 | } 350 | resetView () 351 | { 352 | this.drawpanel.camera.setZoom(1).setScroll(0, 0); 353 | this.supercamera.setZoom(1).setScroll(0, 0); 354 | } 355 | freeze () 356 | { 357 | this.scene.manager.scenes[0].scene.pause(); 358 | } 359 | unfreeze () 360 | { 361 | this.scene.manager.scenes[0].scene.resume(); 362 | } 363 | importJSON () 364 | { 365 | let data = this.cache.json.get('data'); 366 | this.path = this.path.fromJSON(data); 367 | this.draw(); 368 | } 369 | exportJSON () 370 | { 371 | let data = JSON.stringify(this.path.toJSON()); 372 | console.log(data); 373 | let file = new Blob([ data ], [ 'path.json' ]); 374 | let fuse = document.createElement('a'); 375 | fuse.href = URL.createObjectURL(file); 376 | fuse.download = 'path.json'; 377 | fuse.click(); 378 | 379 | } 380 | preview () 381 | { 382 | if (this.path.curves.length !== 0) 383 | { 384 | let follower = this.add.follower(this.path, 0, 0, 'dude'); 385 | 386 | this.cameras.main.ignore(follower); 387 | this.middle.camera.ignore(follower); 388 | this.top.camera.ignore(follower); 389 | 390 | follower.startFollow({ 391 | duration: 2000, 392 | rotateToPath: true, 393 | yoyo: true, 394 | onComplete: function (t, s, w) { w.destroy(); }, 395 | onCompleteParams: [ follower ], 396 | ease: 'Cubic.easeInOut' 397 | }); 398 | } 399 | } 400 | 401 | destroy () 402 | { 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /src/UI/Button.js: -------------------------------------------------------------------------------- 1 | 2 | import Element from './Element'; 3 | import UI from './UI'; 4 | 5 | export default class Button extends Element(Phaser.GameObjects.Text) 6 | { 7 | 8 | constructor (ui, x, y, text, key, frame, target, callbacks, args, context) 9 | { 10 | super(ui, x, y, text, UI.fonts['Button']); 11 | 12 | this.target = target; 13 | this.callbacks = callbacks; 14 | this.args = args; 15 | this.callbackcontext = context; 16 | this.tween = null; 17 | 18 | this.setInteractive(); 19 | 20 | this.on('pointerdown', function (pointer, gameObject) 21 | { 22 | this.click(); 23 | }); 24 | this.on('pointerover', function (pointer, gameObject) 25 | { 26 | this.hover(); 27 | }); 28 | this.on('pointerout', function (pointer, gameObject) 29 | { 30 | this.out(); 31 | }); 32 | 33 | return this; 34 | } 35 | 36 | click () 37 | { 38 | super.click(); 39 | this.tween = this.scene.tweens.add({ targets: this, scaleX: 1.2, scaleY: 1.2, duration: 100, ease: 'Linear', yoyo: true }); 40 | } 41 | 42 | hover () 43 | { 44 | this.scene.gameCanvas.style.cursor = 'pointer'; 45 | this.setScale(1.1, 1.1); 46 | super.hover(); 47 | } 48 | 49 | out () 50 | { 51 | this.scene.pointer.switchCursor(); 52 | this.setScale(1, 1); 53 | super.out(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/UI/Element.js: -------------------------------------------------------------------------------- 1 | 2 | let Element = (superclass) => class extends superclass 3 | { 4 | 5 | constructor (ui,x,y,...args) 6 | { 7 | super(ui.scene, x, y, ...args); 8 | 9 | this.ui = ui; 10 | this.x = x; 11 | this.y = y; 12 | this.scene = this.ui.scene; 13 | 14 | this.ui.elements.push(this); 15 | 16 | let l = (1 << this.scene.cameras.cameras.length) - 1; 17 | this.cameraFilter = l & ~this.ui.camera.id; 18 | 19 | return this; 20 | } 21 | 22 | update () 23 | { 24 | 25 | } 26 | 27 | toggle () 28 | { 29 | this.visible = !this.visible; 30 | } 31 | 32 | click () 33 | { 34 | let callbackcontext = this.callbackcontext; 35 | let args = this.args; 36 | 37 | if (this.callbacks instanceof Array) 38 | { 39 | this.callbacks.forEach(function (callback, index) { callback.apply(callbackcontext[index], args); }); 40 | } 41 | else 42 | { 43 | this.callbacks.apply(this.callbackcontext, this.args); 44 | } 45 | } 46 | 47 | hover () 48 | { 49 | 50 | } 51 | 52 | out () 53 | { 54 | 55 | } 56 | 57 | }; 58 | 59 | export default Element; 60 | -------------------------------------------------------------------------------- /src/UI/Label.js: -------------------------------------------------------------------------------- 1 | 2 | import Element from './Element'; 3 | import UI from './UI'; 4 | 5 | export default class Label extends Element(Phaser.GameObjects.Text) 6 | { 7 | 8 | constructor (ui, x, y, text, target, callbacks, args, context) 9 | { 10 | super(ui, x, y, text, UI.fonts['Label']); 11 | 12 | this.target = target; 13 | this.callbacks = callbacks; 14 | this.callbackcontext = context; 15 | this.args = args; 16 | this.tween = null; 17 | 18 | this.setInteractive(); 19 | 20 | return this; 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/UI/Menu.js: -------------------------------------------------------------------------------- 1 | import Element from './Element'; 2 | import UI from './UI'; 3 | 4 | export default class Menu extends Element(Phaser.GameObjects.Container) 5 | { 6 | 7 | constructor (ui, x, y) 8 | { 9 | super(ui, x, y); 10 | } 11 | 12 | add (x, y, item, callback, args, context) 13 | { 14 | this[item] = this.ui.add.button(x, y, item, null, null, null, callback, args, context).setFontStyle(UI.fonts['Button']); 15 | super.add(this[item]); 16 | return this[item]; 17 | } 18 | 19 | update () 20 | { 21 | 22 | } 23 | 24 | hide () 25 | { 26 | this.list.forEach(function (element) { element.setVisible(false); }); 27 | } 28 | 29 | show () 30 | { 31 | this.list.forEach(function (element) { element.setVisible(true); }); 32 | } 33 | 34 | divide () 35 | { 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/UI/Point/ControlPoint.js: -------------------------------------------------------------------------------- 1 | import Point from './Point'; 2 | import UI from '../UI'; 3 | 4 | export default class ControlPoint extends Point 5 | { 6 | 7 | constructor (ui, x, y, key = 'controlpoint') 8 | { 9 | super(ui, x, y, key); 10 | 11 | this.setScale(0.75, 0.75); 12 | 13 | this.lbl.setFontStyle(UI.fonts['ControlPoint']); 14 | this.lbl.visible = false; 15 | 16 | this.on('pointermove', function (pointer, gameObject) 17 | { 18 | this.lbl.visible = true; 19 | }); 20 | 21 | this.on('pointerout', function (pointer, gameObject) 22 | { 23 | this.lbl.visible = false; 24 | }); 25 | 26 | this.on('dragend', function (pointer, gameObject) 27 | { 28 | this.lbl.visible = false; 29 | }); 30 | 31 | return this; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/UI/Point/EndPoint.js: -------------------------------------------------------------------------------- 1 | import Point from './Point'; 2 | import UI from '../UI'; 3 | 4 | export default class EndPoint extends Point 5 | { 6 | 7 | constructor (ui, x, y, key = 'endpoint') 8 | { 9 | super(ui, x, y, key); 10 | 11 | this.lbl.setFontStyle(UI.fonts['EndPoint']); 12 | 13 | return this; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/UI/Point/Point.js: -------------------------------------------------------------------------------- 1 | 2 | import Element from '../Element'; 3 | import UI from '../UI'; 4 | 5 | export default class Point extends Element(Phaser.GameObjects.Image) 6 | { 7 | 8 | constructor (ui, x, y, key) 9 | { 10 | super(ui, x, y, key); 11 | 12 | // TODO: mixin vec2 13 | this.vec2 = new Phaser.Math.Vector2(x, y); 14 | 15 | this.setInteractive(); 16 | this.scene.input.setDraggable(this); 17 | 18 | this.setData('vector', this.vec2); 19 | this.scene.vectors.push(this.vec2); 20 | 21 | // TODO: abstract from point to A custom curve class. 22 | 23 | 24 | this.on('pointerout', function (pointer, gameObject) 25 | { 26 | this.scene.pointer.switchCursor(); 27 | 28 | this.scene.pointer.lbl.visible = true; 29 | }); 30 | 31 | this.on('drag', function (pointer, gameObject) 32 | { 33 | this.scene.gameCanvas.style.cursor = 'pointer'; 34 | 35 | this.x = this.scene.pointer.x + this.scene.drawpanel.camera.scrollX; 36 | this.y = this.scene.pointer.y + this.scene.drawpanel.camera.scrollY; 37 | 38 | this.scene.pointer.lbl.visible = false; 39 | 40 | this.data.get('vector').set(this.x, this.y); 41 | 42 | if (this.mapping) 43 | { 44 | let m = this.mapping; 45 | let _this = this; 46 | 47 | for (let t in m.data) 48 | { 49 | m.src[m.data[t].property] = m.data[t].operator(m.src, _this[t]); 50 | } 51 | } 52 | 53 | this.scene.graphics.clear(); 54 | this.scene.draw(); 55 | 56 | }); 57 | 58 | this.lbl = this.ui.add.label(this.x + 10, this.y + 10, '').setFontStyle(UI.fonts['Point']); 59 | 60 | return this; 61 | } 62 | map (data) 63 | { 64 | this.mapping = data; 65 | } 66 | fuse (curve) 67 | { 68 | if(curve !== null) 69 | { 70 | this.curve = curve; 71 | this.curve.controlpoints.push(this); 72 | } 73 | } 74 | 75 | update () 76 | { 77 | this.lbl.x = this.x + 10; 78 | this.lbl.y = this.y + 10; 79 | this.lbl.setText('x: ' + this.x + ' y: ' + this.y); 80 | } 81 | 82 | destroy () 83 | { 84 | this.lbl.destroy(); 85 | super.destroy(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/UI/Pointer.js: -------------------------------------------------------------------------------- 1 | 2 | import Element from './Element'; 3 | import UI from './UI'; 4 | 5 | export default class Pointer extends Element(Phaser.GameObjects.Image) 6 | { 7 | 8 | constructor (ui, x, y, key, frame) 9 | { 10 | super(ui, x, y, key); 11 | 12 | this.alpha = 0.5; 13 | 14 | this.delta = 1; 15 | 16 | // global input listener 17 | 18 | this.scene.input.on('pointerdown', function (pointer, gameObject) 19 | { 20 | 21 | if (this.scene.mode == 'draw' && this.scene.input.getDragState(pointer) == 0 && pointer.leftButtonDown()) 22 | { 23 | if (gameObject.length == 0 && (pointer.x > 50 && pointer.x < this.scene.W - 100)) 24 | { 25 | 26 | let _dx = this.scene.drawpanel.camera.scrollX; 27 | let _dy = this.scene.drawpanel.camera.scrollY; 28 | 29 | this.scene.place(this.scene.drawpanel, this.x + _dx, this.y + _dy); 30 | } 31 | } 32 | 33 | 34 | if(pointer.rightButtonDown() && this.scene.input.getDragState(pointer) == 0) 35 | { 36 | this.lockX = pointer.x; 37 | this.lockY = pointer.y; 38 | 39 | this.scene.switchmode('select'); 40 | } 41 | 42 | 43 | if(pointer.middleButtonDown()) 44 | { 45 | this.lastX = pointer.x + this.scene.drawpanel.camera.scrollX; 46 | this.lastY = pointer.y + this.scene.drawpanel.camera.scrollY; 47 | 48 | this.scene.switchmode('hand'); 49 | } 50 | 51 | }, this); 52 | 53 | this.scene.input.on('pointerup', function (pointer, gameObject) 54 | { 55 | 56 | if(this.scene.mode == 'hand') 57 | { 58 | this.scene.switchmode('normal'); 59 | } 60 | }, this); 61 | 62 | this.snapkey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S); 63 | 64 | this.lbl = this.ui.add.label(x + 20, y + 20, '').setFontStyle(UI.fonts['Label']); 65 | 66 | this.menu = this.ui.add.menu(x, y); 67 | 68 | this.menu.add(-50, -50, 'Line', this.switchdrawmode,[ 'Line' ],this); 69 | this.menu.add(0, -50, 'Quadratic', this.switchdrawmode,[ 'QuadraticBezier' ],this); 70 | this.menu.add(50, -50, 'Cubic', this.switchdrawmode,[ 'CubicBezier' ],this); 71 | this.menu.add(-50, 50, 'Spline', this.switchdrawmode,[ 'Spline' ],this); 72 | this.menu.add(0, 50, 'Ellipse', this.switchdrawmode,[ 'Ellipse' ],this); 73 | 74 | this.scene.events.on('switchmode', this.switchmode, this); 75 | 76 | this.scene.add.existing(this); 77 | 78 | } 79 | 80 | switchmode (mode) 81 | { 82 | 83 | if (mode == 'draw') 84 | { 85 | // TODO: abstraction 86 | 87 | this.setVisible(true); 88 | this.lbl.setVisible(true); 89 | this.menu.hide(); 90 | 91 | } 92 | if (mode == 'normal') 93 | { 94 | 95 | this.setVisible(false); 96 | this.menu.hide(); 97 | 98 | } 99 | if (mode == 'select') 100 | { 101 | this.setVisible(false); 102 | 103 | this.menu.setPosition(this.x , this.y); 104 | this.menu.show(); 105 | 106 | } 107 | if(mode == 'hand') 108 | { 109 | this.scene.gameCanvas.style.cursor = 'grab'; 110 | 111 | this.setVisible(true); 112 | this.lbl.setVisible(true); 113 | 114 | } 115 | } 116 | 117 | switchCursor () 118 | { 119 | this.scene.gameCanvas.style.cursor = this.scene.cursors[this.scene.mode]; 120 | } 121 | 122 | switchdrawmode (mode) 123 | { 124 | this.scene.drawmode = mode; 125 | this.scene.drawmodelabel.setText('curve: ' + mode); 126 | this.menu.hide(); 127 | this.scene.switchmode('draw'); 128 | } 129 | snap () 130 | { 131 | this.delta = this.delta == 1 ? 50 : 1; 132 | } 133 | update () 134 | { 135 | 136 | this.x = this.scene.input.activePointer.x; 137 | this.y = this.scene.input.activePointer.y; 138 | 139 | if(this.scene.mode == 'hand') 140 | { 141 | this.scene.look(this.scene.drawpanel.camera); 142 | this.scene.look(this.scene.supercamera); 143 | } 144 | 145 | if(this.scene.mode == 'select') 146 | { 147 | if(Phaser.Math.Distance.Between(this.x, this.y, this.lockX, this.lockY) > 150) 148 | { 149 | this.scene.switchmode('draw'); 150 | } 151 | } 152 | 153 | if(this.scene.mode !== 'select') 154 | { 155 | 156 | this.x = Math.round(this.x / this.delta) * this.delta; 157 | this.y = Math.round(this.y / this.delta) * this.delta; 158 | 159 | this.lbl.setPosition(this.x + 20, this.y + 20); 160 | this.lbl.setText('x: ' + this.x + ' y: ' + this.y); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/UI/Toggle.js: -------------------------------------------------------------------------------- 1 | import Button from './Button'; 2 | import UI from './UI'; 3 | 4 | export default class Toggle extends Button 5 | { 6 | 7 | constructor (...args) 8 | { 9 | super(...args); 10 | this.setColor('#ff0000'); 11 | this.val = false; 12 | } 13 | 14 | click () 15 | { 16 | super.click(); 17 | 18 | this.val = !this.val; 19 | 20 | if(this.val) 21 | { 22 | this.setColor('#00ff00'); 23 | } 24 | else 25 | { 26 | this.setColor('#ff0000'); 27 | } 28 | } 29 | 30 | hover () 31 | { 32 | this.scene.gameCanvas.style.cursor = 'pointer'; 33 | this.setScale(1.1, 1.1); 34 | super.hover(); 35 | } 36 | 37 | out () 38 | { 39 | this.scene.pointer.switchCursor(); 40 | this.setScale(1, 1); 41 | super.out(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/UI/UI.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | import Button from './Button'; 3 | import Toggle from './Toggle'; 4 | import Point from './Point/Point'; 5 | import EndPoint from './Point/EndPoint'; 6 | import ControlPoint from './Point/ControlPoint'; 7 | import Label from './Label'; 8 | 9 | export default class UI 10 | { 11 | 12 | constructor (scene) 13 | { 14 | this.scene = scene; 15 | this.elements = []; 16 | 17 | this.add = { 18 | menu: function (x, y) 19 | { 20 | let m = new Menu(this.ui, x, y); 21 | this.ui.scene.add.existing(m); 22 | return m; 23 | }, 24 | button: function (x, y, text, key, frame, target, callback, args, context) 25 | { 26 | let tb = new Button(this.ui, x, y, text, key, frame, target, callback, args, context); 27 | this.ui.scene.add.existing(tb); 28 | return tb; 29 | }, 30 | toggle: function (x, y, text, key, frame, target, callback, args, context) 31 | { 32 | let tb = new Toggle(this.ui, x, y, text, key, frame, target, callback, args, context); 33 | this.ui.scene.add.existing(tb); 34 | return tb; 35 | }, 36 | label: function (x, y, text, target, callback, args, context) 37 | { 38 | let l = new Label(this.ui, x, y, text, target, callback, args, context); 39 | this.ui.scene.add.existing(l); 40 | return l; 41 | }, 42 | point: function (x, y, key) 43 | { 44 | let p = new Point(this.ui, x, y, key); 45 | this.ui.scene.add.existing(p); 46 | return p; 47 | }, 48 | endpoint: function (x, y, key) 49 | { 50 | let p = new EndPoint(this.ui, x, y, key); 51 | this.ui.scene.add.existing(p); 52 | return p; 53 | }, 54 | controlpoint: function (x, y, key) 55 | { 56 | let p = new ControlPoint(this.ui, x, y, key); 57 | this.ui.scene.add.existing(p); 58 | return p; 59 | } 60 | 61 | // image: function (x, y, key, frame) { 62 | // let i = new Image(this.ui, x, y, key, frame); 63 | // this.ui.scene.add.existing(i); 64 | // return i; 65 | // } 66 | }; 67 | this.add.ui = this; 68 | this.camera = this.scene.cameras.add(); 69 | } 70 | 71 | hide () 72 | { 73 | this.elements.forEach(function (element) { element.visible = false; }); 74 | this.scene.switchmode('normal'); 75 | this.translate(0, this.scene.cameras.main.height, 400, this.scene.unfreeze); 76 | } 77 | show () 78 | { 79 | this.elements.forEach(function (element) { element.visible = true; }); 80 | this.scene.switchmode('normal'); 81 | this.translate(0, 0, 400, this.scene.freeze); 82 | } 83 | translate (x, y, speed, callback) 84 | { 85 | this.scene.tweens.add({ 86 | targets: this.camera, 87 | scrollX: x, 88 | scrollY: y, 89 | duration: speed, 90 | ease: 'Cubic.easeOut' 91 | }); 92 | this.scene.time.delayedCall(speed, callback, [], this.scene); 93 | } 94 | update () 95 | { 96 | this.elements.forEach(function (element) { element.update(); }); 97 | } 98 | destroy () 99 | { 100 | this.elements.forEach(function (element) { element.destroy(); }); 101 | } 102 | 103 | } 104 | 105 | // TODO: find sol 106 | UI.fonts = { 107 | Button: { fontFamily: 'Arial', fontSize: 16, color: '#00ff00' }, 108 | Point: { fontFamily: 'Arial', fontSize: 12, color: '#00ff00' }, 109 | EndPoint: { fontFamily: 'Arial', fontSize: 12, color: '#00ff00' }, 110 | ControlPoint: { fontFamily: 'Arial', fontSize: 10, color: '#00ff00' }, 111 | Label: { fontFamily: 'Arial', fontSize: 16, color: '#ffff00' } 112 | }; 113 | 114 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | 2 | import Scene from './Scene'; 3 | import UI from './UI/UI'; 4 | import Pointer from './UI/Pointer'; 5 | 6 | export class PathBuilder extends Phaser.Plugins.BasePlugin 7 | { 8 | 9 | constructor (pluginManager) 10 | { 11 | super(pluginManager); 12 | 13 | pluginManager.registerGameObject('ui', this.addUI); 14 | pluginManager.registerGameObject('superpointer', this.addPointer); 15 | 16 | this.game.scene.add('UI',Scene, true); 17 | } 18 | 19 | addUI (scene) 20 | { 21 | return new UI(scene); 22 | } 23 | 24 | addPointer (ui, x, y, key) 25 | { 26 | return new Pointer(ui, x, y, key); 27 | } 28 | } 29 | 30 | module.exports = PathBuilder; 31 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const webpack = require('webpack'); 4 | const path = require('path'); 5 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); 6 | 7 | module.exports = { 8 | 9 | context: `${__dirname}/src/`, 10 | 11 | entry: { 12 | PathBuilder: [ 13 | 'babel-polyfill', 14 | path.resolve(__dirname, 'src/main.js') 15 | ], 16 | 17 | PathBuilder: './main.js', 18 | 19 | 'PathBuilder.min': './main.js' 20 | }, 21 | 22 | output: { 23 | pathinfo: true, 24 | publicPath: '/dist/', 25 | path: `${__dirname}/dist/`, 26 | library: '[name]', 27 | libraryTarget: 'umd', 28 | filename: '[name].js' 29 | }, 30 | 31 | module: { 32 | rules: [ 33 | { 34 | exclude: /node_modules/, 35 | test: /\.js$/, use: ['babel-loader'], include: path.join(__dirname, 'src/') }, 36 | //{ test: /phaser-split\.js$/, use: ['expose-loader?Phaser'] }, 37 | //{ test: [/\.vert$/, /\.frag$/], use: 'raw-loader' } 38 | ] 39 | }, 40 | 41 | plugins: [ 42 | 43 | new UglifyJSPlugin({ 44 | include: /\.min\.js$/, 45 | parallel: true, 46 | sourceMap: false, 47 | uglifyOptions: { 48 | compress: true, 49 | ie8: false, 50 | ecma: 5, 51 | output: { 52 | comments: false 53 | }, 54 | warnings: false 55 | }, 56 | warningsFilter: (src) => false 57 | }) 58 | 59 | ] 60 | 61 | }; 62 | --------------------------------------------------------------------------------