22 |
23 | Phaser Inspector Plugin
24 |
25 | Phaser Inspector Plugin allows you to inspect your (or someone else) Phaser game.
26 |
27 | 

28 |
29 | The plugin is written using Angular.js and ES6, compiled with Babel and Browserify, tested on Phaser 2.1.3 and Phaser 2.4.3 running on Google Chrome Version 45.0.2454.85 (64-bit) on OSX Yosemite.
30 |
31 | Working features:
32 |
33 |
34 | - Display objects tree inspection.
35 | - Display object class guessing.
36 | - Display object name guessing (by looking for
this./name/
in parents).
37 | -
38 | Text as name for Phaser.Text.
39 | - Display objects non-case-sensitive search by name and class.
40 | - Display object destroy/kill/hide.
41 | -
42 | Properties inspection and editting.
43 | -
44 | Texture display for sprite/image
45 | -
46 | Bounds drawing
47 | -
48 | States list and state change
49 | -
50 | Floating, resizable and draggable panel
51 | - Panel remember position on page reload
52 | -
53 | Close/minimize panel
54 |
55 |
56 | Upcoming features:
57 |
58 |
59 | -
60 | Transparent/Clickthrough panel
61 | - Sprite/Image load Texture
62 |
63 | -
64 | Optimize when displaying too many display objects on the tree
65 |
66 |
67 | Feel free to follow me on twitter @netcell and check out my blog!
68 |
69 |
70 | Demo
71 |
72 | Check the example
folder (please run bower install
before that, and serve the folder on a server/localhost) or try that example rightaway on this codepen.
73 |
74 |
75 | Download
76 |
77 | The source is available for download from latest release or by cloning the repository or download the files in build
folder. Alternatively, you can install via:
78 |
79 |
80 | bower: bower install --save phaser-inspector
81 |
82 |
83 |
84 | Usage
85 |
86 | NOTE: I have only tested this plugin on Google Chrome so I am not sure how this would perform on other browsers. Also since this plugin is for debugging your game, you should not expect to use it on your mobile devices, meaning remove it (or conditionally not loading it) when deploying to mobile devices.
87 |
88 | Simply download the phaser-inspector.js
or phaser-inspector.min.js
script from latest release and include it on your page after including Phaser:
89 |
90 | <script src="phaser.js"></script>
91 | <script src="phaser-inspector.js"></script>
92 |
93 | In the create
method in your boot state:
94 |
95 | game.plugins.add(Phaser.Plugin.Inspector);
96 |
97 | Alternatively, when running a game in the browser, you can inject the plugin in the developer console as follow:
98 |
99 | var script = document.createElement('script'); script.src = "http://netcell.github.io/phaser-inspector/build/phaser-inspector.js"; document.getElementsByTagName('head')[0].appendChild(script); function phaserInspectorInject(){ if (Phaser.Plugin.Inspector) Phaser.GAMES[0].plugins.add(Phaser.Plugin.Inspector); else setTimeout(phaserInspectorInject); } setTimeout(phaserInspectorInject);
100 |
101 | Check the example in example
folder to see it in action :)
102 |
103 |
104 | License
105 |
106 | This content is released under the (http://opensource.org/licenses/MIT) MIT License.
107 |
108 |
113 |
114 |
115 |
116 |
120 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/javascripts/main.js:
--------------------------------------------------------------------------------
1 | console.log('This would be the main JS file.');
2 |
--------------------------------------------------------------------------------
/params.json:
--------------------------------------------------------------------------------
1 | {"name":"Phaser Inspector","tagline":"phaser, game, html5, framework, plugin, inspector, debug","body":"# Phaser Inspector Plugin\r\n\r\n**Phaser Inspector Plugin** allows you to inspect your (or someone else) [Phaser](http://phaser.io) game.\r\n\r\n\r\n\r\nThe plugin is written using [Angular.js](https://angularjs.org/) and ES6, compiled with [Babel](babeljs.io) and [Browserify](http://browserify.org/), tested on Phaser 2.1.3 and Phaser 2.4.3 running on Google Chrome Version 45.0.2454.85 (64-bit) on OSX Yosemite.\r\n\r\n**Working features:**\r\n- Display objects **tree** inspection.\r\n- Display object **class** guessing.\r\n- Display object **name** guessing (by looking for `this./name/` in parents).\r\n- **Text** as name for Phaser.Text.\r\n- Display objects **non-case-sensitive search** by name and class.\r\n- Display object **destroy/kill/hide**.\r\n- **Properties** inspection and editting.\r\n- **Texture display** for sprite/image\r\n- **Bounds** drawing\r\n- **States** list and state change\r\n- **Floating**, **resizable** and **draggable** panel\r\n- Panel **remember** position on page reload\r\n- **Close/minimize** panel\r\n \r\n**Upcoming features:**\r\n- **Transparent/Clickthrough** panel\r\n- Sprite/Image **load Texture**\r\n- **Optimize** when displaying too many display objects on the tree \r\n\r\nFeel free to follow me on twitter [@netcell](https://twitter.com/netcell) and check out [my blog](http://anhnt.ninja)!\r\n\r\n## Demo\r\n\r\nCheck the `example` folder (please run `bower install` before that, and serve the folder on a server/localhost) or try that example rightaway on [this codepen](http://codepen.io/netcell/full/gapZWG/).\r\n\r\n## Download\r\n\r\nThe source is available for download from [latest release](https://github.com/netcell/phaser-inspector/releases) or by cloning the repository or download the files in `build` folder. Alternatively, you can install via:\r\n- ~~[bower](http://bower.io/): `bower install --save phaser-inspector`~~\r\n\r\n## Usage\r\n\r\n**NOTE:** I have only tested this plugin on Google Chrome so I am not sure how this would perform on other browsers. Also since this plugin is for debugging your game, you should not expect to use it on your mobile devices, meaning remove it (or conditionally not loading it) when deploying to mobile devices.\r\n\r\nSimply download the `phaser-inspector.js` or `phaser-inspector.min.js` script from [latest release](https://github.com/netcell/phaser-inspector/releases) and include it on your page after including Phaser:\r\n\r\n```html\r\n\r\n\r\n```\r\n\r\nIn the `create` method in your boot state:\r\n```javascript\r\ngame.plugins.add(Phaser.Plugin.Inspector);\r\n```\r\n\r\nAlternatively, when running a game in the browser, you can inject the plugin in the developer console as follow:\r\n```javascript\r\nvar script = document.createElement('script'); script.src = \"http://netcell.github.io/phaser-inspector/build/phaser-inspector.js\"; document.getElementsByTagName('head')[0].appendChild(script); function phaserInspectorInject(){ if (Phaser.Plugin.Inspector) Phaser.GAMES[0].plugins.add(Phaser.Plugin.Inspector); else setTimeout(phaserInspectorInject); } setTimeout(phaserInspectorInject);\r\n```\r\n\r\nCheck the example in `example` folder to see it in action :)\r\n\r\n### License ###\r\n\r\nThis content is released under the (http://opensource.org/licenses/MIT) MIT License.\r\n\r\n","google":"UA-62565835-2","note":"Don't delete this file! It's used internally to help with page regeneration."}
--------------------------------------------------------------------------------
/src/css/main.sass:
--------------------------------------------------------------------------------
1 | html, body
2 | font : 14px "Avenir Next"
3 |
4 | ::-webkit-scrollbar
5 | width : 7px
6 | height : 7px
7 |
8 | ::-webkit-scrollbar-track
9 | border-radius : 10px
10 |
11 | /* Handle */
12 | ::-webkit-scrollbar-thumb
13 | background : rgba(22, 75, 96,0.5)
14 |
15 | ::-webkit-scrollbar-corner
16 | background : none
17 |
18 | ::-webkit-scrollbar-thumb:hover
19 | background : rgba(22, 75, 96,0.8)
20 |
21 | $pg-tooltip-background : #1C2B36
22 |
23 | .tooltip
24 |
25 | $_trns: 125ms ease-out
26 | pointer-events : none
27 | position : absolute
28 | z-index : 9999999999
29 | top :-9999px
30 | left :-9999px
31 | opacity :0
32 | padding : 0 12px
33 | margin : -12px 0 0 6px
34 | border-radius : 7px
35 | border : 2px solid #869FB1
36 | color : #fff
37 | background-color : rgba($pg-tooltip-background, 0.95)
38 | transform : scale(0.925,0.925)
39 | transition : opacity $_trns, transform $_trns
40 | &, *
41 | white-space : nowrap
42 |
43 | &.showing
44 | opacity : 0.95
45 | transform : translate(0, -40px) scale(1,1)
46 |
47 | .phaser-inspector-panel
48 | width : 500px
49 | height : 450px
50 | min-width : 450px!important
51 | min-height : 300px!important
52 | z-index : 500
53 | padding-top : 20px
54 |
55 | border: 5px solid #1C2B36
56 |
57 | background : rgba(0,0,0,0.85)
58 | color : #aaa
59 |
60 | display : flex
61 | justify-content : stretch
62 |
63 | &.collapsed
64 | min-height : 0px!important
65 | min-width : 0px!important
66 | height : 0px!important
67 | width : 50px!important
68 | border-bottom : none!important
69 |
70 | .button
71 | cursor : pointer
72 | display : inline-block
73 | padding : 0 5px
74 | margin-left : 5px
75 | height : 24px
76 | text-align : center
77 | line-height : 24px
78 | color : #fff
79 | border-radius : 8px
80 | i
81 | line-height : 25px
82 | &.info
83 | background : #1797be
84 | &.primary
85 | background : #6E2E92
86 | &.warn
87 | background : #AF8F0A
88 | &.danger
89 | background : #A21111
90 | &.success
91 | background : #11A218
92 | &.deactive
93 | background : #869FB1
94 |
95 | .grabber
96 | position : absolute
97 | top : 0
98 | left : 0
99 | width : 100%
100 | height : 20px
101 | background : #1C2B36
102 | color : #FFF
103 | font-size : 18px
104 | cursor : move
105 |
106 | div
107 | position : absolute
108 | top : -4px
109 | cursor : pointer
110 |
111 | .close
112 | left : 0
113 |
114 | .toggle-detail
115 | right : 0
116 |
117 | .panel
118 | overflow : auto
119 |
120 | #details
121 | overflow-x : hidden
122 | width : 250px
123 |
124 | >div
125 | margin-top : 10px
126 |
127 | #header
128 | user-select : none
129 | margin-left : 10px
130 |
131 | .row
132 | display : flex
133 | justify-content : stretch
134 | line-height : 23px
135 | border-bottom : 1px solid #222
136 |
137 | &.header
138 | border-bottom : none
139 | padding-left : 5px
140 |
141 | >div
142 | padding-left : 10px
143 |
144 | .label
145 | flex : 2
146 | background : #1c2b36
147 | color : #ccc
148 |
149 | .detail
150 | flex : 3
151 | color : #fff
152 |
153 | input
154 | background : none
155 | border : none
156 | outline : none
157 | -webkit-appearance : none
158 | padding-left : 10px
159 |
160 | input::-webkit-outer-spin-button, input::-webkit-inner-spin-button
161 | -webkit-appearance : none
162 | margin : 0
163 |
164 | .frame-img
165 | margin : 0 auto
166 | transform-origin : top left
167 |
168 | #tree
169 | box-shadow : -25px 30px 24px -30px #000 inset
170 | flex : 1
171 | color : #bbb9ff
172 | border-right : 1px solid #222
173 | background-color : #1c2b36
174 | display : flex
175 | flex-direction : column-reverse
176 | overflow : hidden
177 | .search
178 | width : 100%
179 | background-color : #dee5e7
180 | font-size : 14px
181 | color : #696969
182 | border : none
183 | outline : none
184 | padding : 5px 15px
185 | .scene
186 | user-select : none
187 | flex : 1
188 | overflow : auto
189 | .view
190 | .row
191 | font-weight : bold
192 | line-height : 30px
193 | white-space : nowrap
194 | user-select : none
195 | &.selected
196 | background-color : #0A1925
197 | &.parentSelected
198 | background-color : #17222B
199 | >div
200 | display : inline-block
201 | .middle
202 | div
203 | text-align : right
204 | padding-right : 5px
205 | color : #1797be
206 | display : inline-block
207 | cursor : pointer
208 | .eye
209 | text-align : center
210 | color : #1797be
211 | display : inline-block
212 | .fa-eye-slash, .fa-unlock-alt
213 | color : #869fb1
214 | .toggle
215 | padding-right : 5px
216 | width : 5px
217 | .fa
218 | width : 100%
219 | .fa-circle
220 | font-size : 5px
221 | font-weight : normal
222 | .name
223 | color : #869fb1
224 | .type
225 | .children
226 | &.weak
227 | .middle
228 | font-weight: normal
229 | font-style: italic
230 | .tree-control
231 | user-select : none
232 | padding : 5px 0
233 | background-color : #183848
234 | .states
235 | user-select : none
236 | height : 100px
237 | overflow-x : hidden
238 | overflow-y : auto
239 | position : relative
240 | .state
241 | padding : 5px 10px
242 | font-weight : bold
243 | color : #1797BE
244 | position : relative
245 | &.selected
246 | background-color : #0A1925
247 | color : #869FB1
248 |
249 | @for $i from 1 through 12
250 | .f#{$i}
251 | flex : $i!important
252 |
253 | @for $j from 1 through 5
254 | .pl#{$j}
255 | padding-left : 5px * $j!important
256 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | window.$ = require('jquery');
2 |
3 | var DetailPlugin = require('./js/classes/DetailPlugin');
4 | var PositionPlugin = require('./js/plugins/PositionPlugin');
5 | var WorldPlugin = require('./js/plugins/WorldPlugin');
6 | var SizePlugin = require('./js/plugins/SizePlugin');
7 | var TargetSizePlugin = require('./js/plugins/TargetSizePlugin');
8 | var BoundsPlugin = require('./js/plugins/BoundsPlugin');
9 | var ScalePlugin = require('./js/plugins/ScalePlugin');
10 | var AnchorPlugin = require('./js/plugins/AnchorPlugin');
11 | var FrameRenderPlugin = require('./js/plugins/FrameRenderPlugin');
12 |
13 | var GameManager = require('./js/services/GameManager');
14 | var ViewCtrl = require('./js/controllers/ViewCtrl');
15 | var DetailCtrl = require('./js/controllers/DetailCtrl');
16 | var TreeCtrl = require('./js/controllers/TreeCtrl');
17 | var interaction = require('./js/directives/interaction');
18 | var phaserInspectorTree = require('./js/directives/phaserInspectorTree');
19 | var phaserInspectorDetails = require('./js/directives/phaserInspectorDetails');
20 | var view = require('./js/directives/view');
21 | var viewCollection = require('./js/directives/viewCollection');
22 |
23 | var appTpl = require('./tpl/app.html');
24 | var main = require('./css/main.css');
25 | var fontAwesome = require('./css/font-awesome.css');
26 | // var tooltip = require('../node_modules/pg-ng-tooltip/dest/css/pg-ng-tooltip.min.css')
27 |
28 | Phaser.Plugin.Inspector = class Inspector extends Phaser.Plugin {
29 | constructor() {
30 | super(...arguments);
31 | DetailPlugin.add(PositionPlugin);
32 | DetailPlugin.add(WorldPlugin);
33 | DetailPlugin.add(SizePlugin);
34 | DetailPlugin.add(TargetSizePlugin);
35 | DetailPlugin.add(BoundsPlugin);
36 | DetailPlugin.add(ScalePlugin);
37 | DetailPlugin.add(AnchorPlugin);
38 | DetailPlugin.add(FrameRenderPlugin);
39 | /** Avoid multiple creation */
40 | if ($('.phaser-inspector-panel').length) return;
41 | /** @type {Phaser.Signal} Dispatched on game update */
42 | this.onUpdate = new Phaser.Signal();
43 | /**
44 | * Load Angular framework
45 | * Import angular pior to this behave badly
46 | * TODO: Figure out why
47 | */
48 | window.angular = require('angular');
49 | require('../node_modules/pg-ng-tooltip/dest/js/pg-ng-tooltip.js');
50 | require('angular-bindonce');
51 | require('ngstorage');
52 | /** Bootstrap app */
53 | $('html').attr('data-ng-app', 'app');
54 | $('body').append(appTpl);
55 | /** Angular app definition */
56 | require('./js/app')
57 | .factory('game', () => this.game)
58 | .factory('onUpdate', () => this.onUpdate)
59 | .controller('TreeCtrl', TreeCtrl)
60 | .controller('ViewCtrl', ViewCtrl)
61 | .controller('DetailCtrl', DetailCtrl)
62 | .factory('gameManager', ($timeout) => new GameManager($timeout, this.game, this.onUpdate))
63 | .directive('phaserInspectorPanel', interaction)
64 | .directive('phaserInspectorTree', phaserInspectorTree)
65 | .directive('phaserInspectorDetails', phaserInspectorDetails)
66 | .directive('view', view)
67 | .directive('viewCollection', viewCollection);
68 | }
69 | render() {
70 | this.onUpdate && this.onUpdate.dispatch();
71 | }
72 | }
73 |
74 | Phaser.Plugin.Inspector.DetailPlugin = require('./js/classes/DetailPlugin');
--------------------------------------------------------------------------------
/src/js/app.js:
--------------------------------------------------------------------------------
1 | export default angular.module('app', ['ngStorage', 'pasvaz.bindonce', 'pg-ng-tooltip'])
2 | .directive('bindHtmlUnsafe', function( $parse, $compile ) {
3 | return function( $scope, $element, $attrs ) {
4 | var compile = function( newHTML ) {
5 | newHTML = $compile(newHTML)($scope);
6 | $element.html('').append(newHTML);
7 | };
8 |
9 | var htmlName = $attrs.bindHtmlUnsafe;
10 |
11 | $scope.$watch(htmlName, function( newHTML ) {
12 | if(!newHTML) return;
13 | compile(newHTML);
14 | });
15 |
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/src/js/classes/DetailPlugin.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | export default class DetailPlugin {
3 | constructor({gameManager, header, fields }){
4 | this.show = true;
5 | this.gameManager = gameManager;
6 | this.header = header;
7 | this.fields = fields;
8 | var self = this;
9 |
10 | if ( fields.template ) {
11 | var data = fields.data;
12 | Object.defineProperty(fields, 'data', {
13 | get(){
14 | return self[data];
15 | },
16 | set(value){
17 | return self[data] = value;
18 | }
19 | })
20 | } else for (var i = fields.length - 1; i >= 0; i--) {
21 | let field = fields[i];
22 | field.label = field.label || {};
23 | let {label, detail} = field;
24 | if (_.isString(label)) {
25 | field.label = { text : label };
26 | label = field.label;
27 | }
28 | label.text = label.text || detail.data;
29 | let data = detail.data;
30 | Object.defineProperty(detail, 'data', {
31 | get(){
32 | return self[data];
33 | },
34 | set(value){
35 | return self[data] = value;
36 | }
37 | })
38 | };
39 | //TODO: Make some check here
40 | }
41 | reset(){
42 | }
43 | update(obj){
44 | }
45 | }
46 |
47 | DetailPlugin.plugins = [];
48 | DetailPlugin.add = function(Plugin){
49 | DetailPlugin.plugins.push(Plugin);
50 | }
--------------------------------------------------------------------------------
/src/js/classes/DisplayObject.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | /** Wrapping Phaser Object for external functionalities */
3 | export default class DisplayObject {
4 | constructor(obj, gameManager){
5 | this.obj = obj;
6 | this.gameManager = gameManager;
7 | }
8 | /** CHILDREN */
9 | get numberOfChildren() {
10 | return this.obj.children.length;
11 | }
12 | get hasChildren() {
13 | return !!this.numberOfChildren;
14 | }
15 | /** Number of parents above */
16 | get childLevel(){
17 | /** Exclude game.world and game.world.parent (Stage) */
18 | var count = -2;
19 | var parent = this.obj.parent;
20 | while (parent) {
21 | count++;
22 | parent = parent.parent;
23 | }
24 | return count;
25 | }
26 | /** NAME OF THE OBJECT */
27 | /**
28 | * Utility for get game()
29 | * Accessing parent to find
30 | * prop key name the obj attached to
31 | */
32 | find(parent, obj){
33 | console.log(parent)
34 | if (parent) {
35 | var keys = Object.keys(parent);
36 | var foundKey = null;
37 | for (var i = keys.length - 1; i >= 0; i--) {
38 | var key = keys[i];
39 | if (parent[key] === obj) {
40 | if (key === 'cursor') foundKey = key;
41 | else return key;
42 | }
43 | }
44 | return foundKey || this.find(parent.parent, obj);
45 | } else return false;
46 | }
47 | /**
48 | * Get the game of the current object
49 | * By finding the key name in the parent
50 | * Or the current state (if it is at top level)
51 | * Or the current text (if it's the Text object)
52 | * Or the specified name (if any, probably not, sadly)
53 | */
54 | get name(){
55 | var obj = this.obj;
56 | if (_.isString(obj.text)) return obj.text;
57 | if (this._name) return this._name;
58 | var state = this.gameManager.game.state;
59 | /** Search in current state */
60 | var name = this.find(state.states[state.current], obj);
61 | if (name) this._name = name;
62 | else {
63 | /** Recursively search in parent and parent of parent */
64 | name = this.find(obj.parent, obj);
65 | if (name) this._name = name;
66 | else if (_.isString(obj.name)) this._name = obj.name;
67 | else this._name = '';
68 | }
69 | return this._name;
70 | }
71 | /** THE CLASS OF THE OBJECT */
72 | /** Get the class type of the current object */
73 | get type() {
74 | if (this._type) return this._type;
75 | var node = this.obj;
76 | /** Either the constructor name */
77 | if (node.constructor.name) return this._type = node.constructor.name;
78 | /** Or guessing if it's not a Phaser class */
79 | if (node.type === undefined) {
80 | if (node instanceof PIXI.Stage) return this._type = 'PIXI Stage';
81 | else if (node instanceof PIXI.Sprite) return this._type = 'PIXI Sprite';
82 | else if (node instanceof PIXI.DisplayObjectContainer) return this._type = 'PIXI DisplayObjectContainer';
83 | else if (node instanceof PIXI.DisplayObject) return this._type = 'PIXI DisplayObject';
84 | else return this._type = 'Unknown';
85 | /** Or checking with Phaser classes table */
86 | } else {
87 | switch(node.type) {
88 | case Phaser.SPRITE : return this._type = 'Sprite';
89 | case Phaser.BUTTON : return this._type = 'Button';
90 | case Phaser.IMAGE : return this._type = 'Image';
91 | case Phaser.GRAPHICS : return this._type = 'Graphics';
92 | case Phaser.TEXT : return this._type = 'Text';
93 | case Phaser.TILESPRITE : return this._type = 'Tile Sprite';
94 | case Phaser.BITMAPTEXT : return this._type = 'Bitmap Text';
95 | case Phaser.GROUP : return this._type = 'Group';
96 | case Phaser.RENDERTEXTURE : return this._type = 'Render Texture';
97 | case Phaser.TILEMAP : return this._type = 'Tilemap';
98 | case Phaser.TILEMAPLAYER : return this._type = 'Tilemap Layer';
99 | case Phaser.EMITTER : return this._type = 'Emitter';
100 | case Phaser.POLYGON : return this._type = 'Polygon';
101 | case Phaser.BITMAPDATA : return this._type = 'Bitmap Data';
102 | case Phaser.CANVAS_FILTER : return this._type = 'Canvas Filter';
103 | case Phaser.WEBGL_FILTER : return this._type = 'WebGL Filter';
104 | case Phaser.ELLIPSE : return this._type = 'Ellipse';
105 | case Phaser.SPRITEBATCH : return this._type = 'Sprite Batch';
106 | case Phaser.RETROFONT : return this._type = 'Retro Font';
107 | case Phaser.POINTER : return this._type = 'Pointer';
108 | case Phaser.ROPE : return this._type = 'Rope';
109 | default : return this._type = 'Unknown';
110 | }
111 | }
112 | }
113 | get img(){
114 | var img = {};
115 | var texture = this.obj.texture;
116 | if ( texture && texture.baseTexture) {
117 | var source = texture.baseTexture.source;
118 | if (source && source.src) img.url = source.src;
119 | else if (texture.getBase64) {
120 | img.url = texture.getBase64();
121 | }
122 | if (img.url) {
123 | var frame = texture.frame;
124 | img.width = frame.width;
125 | img.height = frame.height;
126 | img.x = frame.x;
127 | img.y = frame.y;
128 | img.scale = 1;
129 | var detailPanelWidth = $('.frame-img').parent().innerWidth();
130 | if (frame.width > detailPanelWidth) {
131 | img.scale = detailPanelWidth/frame.width;
132 | }
133 | }
134 | }
135 | return img;
136 | }
137 | /** SELECTION AND EXPANSION FOR VIEW */
138 | get expanded(){
139 | return this.obj.$inspectorTreeExpanded;
140 | }
141 | set expanded(value){
142 | var obj = this.obj;
143 | obj.$inspectorTreeExpanded = value;
144 | /**
145 | * If an object is on expansion,
146 | * the parent should also be too
147 | */
148 | if (value) {
149 | var parent = obj.parent;
150 | while (parent) {
151 | parent.$inspectorTreeExpanded = true;
152 | parent = parent.parent;
153 | }
154 | }
155 | }
156 | get selected(){
157 | return this.gameManager.$inspectorTreeSelected === this.obj;
158 | }
159 | get parentSelected(){
160 | return this.gameManager.$inspectorTreeSelected === this.obj.parent;
161 | }
162 | expand(){
163 | this.expanded = !this.expanded;
164 | this.gameManager.$inspectorTreeSelected = this.obj;
165 | }
166 | select(){
167 | this.gameManager.$inspectorTreeSelected = this.obj;
168 | }
169 | }
--------------------------------------------------------------------------------
/src/js/classes/Vector.js:
--------------------------------------------------------------------------------
1 | export default class Vector {
2 | constructor(xname, yname){
3 | this.xname = xname;
4 | this.yname = yname;
5 | this.reset();
6 | }
7 | reset(){
8 | this.point = null;
9 | this._x = 0;
10 | this._y = 0;
11 | }
12 | update(point){
13 | this.point = point;
14 | this._x = point[this.xname].toFixed(3) * 1;
15 | this._y = point[this.yname].toFixed(3) * 1;
16 | }
17 | get x() {
18 | return this._x;
19 | }
20 | get y() {
21 | return this._y;
22 | }
23 | set x(value) {
24 | var point = this.point;
25 | if (point) {
26 | point[this.xname] = value;
27 | this._x = value;
28 | }
29 | }
30 | set y(value) {
31 | var point = this.point;
32 | if (point) {
33 | point[this.yname] = value;
34 | this._y = value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/js/classes/VectorDetailPlugin.js:
--------------------------------------------------------------------------------
1 | var DetailPlugin = require('./DetailPlugin');
2 | export default class VectorDetailPlugin extends DetailPlugin {
3 | constructor({xname = 'x', yname = 'y', header, fields}){
4 | super({header, fields});
5 | this.xname = xname;
6 | this.yname = yname;
7 | this.reset();
8 | }
9 | reset(){
10 | this.point = null;
11 | this._x = 0;
12 | this._y = 0;
13 | }
14 | update(point){
15 | this.point = point;
16 | this._x = point[this.xname];
17 | if (this._x && this._x.toFixed) {
18 | this._x = this._x.toFixed(3) * 1;
19 | }
20 | this._y = point[this.yname];
21 | if (this._y && this._y.toFixed) {
22 | this._y = this._y.toFixed(3) * 1;
23 | }
24 | }
25 | get x() {
26 | return this._x * 1;
27 | }
28 | get y() {
29 | return this._y * 1;
30 | }
31 | set x(value) {
32 | var point = this.point;
33 | if (point) {
34 | point[this.xname] = value;
35 | this._x = value;
36 | }
37 | }
38 | set y(value) {
39 | var point = this.point;
40 | if (point) {
41 | point[this.yname] = value;
42 | this._y = value;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/js/controllers/DetailCtrl.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | var DisplayObject = require('../classes/DisplayObject');
3 | var DetailPlugin = require('../classes/DetailPlugin');
4 |
5 | class Cache {
6 | constructor(gameManager){
7 | var plugins = DetailPlugin.plugins;
8 | this.plugins = [];
9 | _.each(plugins, Plugin => this.plugins.push( new Plugin(gameManager) ));
10 | }
11 | update(obj, wrapObj){
12 | this.plugins.forEach(plugin => {
13 | if (plugin.show) plugin.update(obj, wrapObj)
14 | });
15 | }
16 | reset(obj, wrapObj){
17 | this.plugins.forEach(plugin => plugin.reset(obj, wrapObj));
18 | }
19 | }
20 |
21 | export default class DetailCtrl {
22 | constructor($scope, $timeout, gameManager, onUpdate, game){
23 | this.game = game;
24 | this.world = game.world;
25 | this.gameManager = gameManager;
26 | $scope.cache = this.cache = new Cache(gameManager);
27 | $scope.$watch(function () {
28 | return gameManager.$inspectorTreeSelected;
29 | }, () => {
30 | this.realObj = gameManager.$inspectorTreeSelected || this.world;
31 | this.obj = new DisplayObject(this.realObj);
32 | this.cache.reset(this.realObj, this.obj);
33 | });
34 | onUpdate.add(() => this.updateInfo());
35 | }
36 |
37 | get isWorld(){
38 | return this.world === this.realObj;
39 | }
40 |
41 | updateInfo(){
42 | var {realObj, obj, cache, game} = this;
43 |
44 | if (this.isWorld) cache.className = 'World';
45 | else cache.className = obj.type;
46 |
47 | cache.alive = realObj.alive;
48 | cache.visible = realObj.visible;
49 | cache.kill = realObj.kill;
50 |
51 | var children = realObj.children;
52 | cache.noChildren = children.length;
53 | cache.noAlive = children.filter(child => child.alive).length;
54 | cache.noNested = getChildrenOf(realObj);
55 | cache.noNestedAlive = getAliveChildrenOf(realObj);
56 |
57 | cache.update(realObj, obj);
58 | }
59 |
60 | deselect(){
61 | this.gameManager.$inspectorTreeSelected = null;
62 | }
63 |
64 | destroy() {
65 | this.realObj.destroy();
66 | this.deselect();
67 | }
68 |
69 | killRevive() {
70 | var realObj = this.realObj;
71 | return realObj.alive ? realObj.kill() : realObj.revive();
72 | }
73 |
74 | showHide() {
75 | return this.realObj.visible = !this.cache.visible;
76 | }
77 |
78 | }
79 |
80 | function getChildrenOf(obj){
81 | var count = 0;
82 | var children = obj.children;
83 | if (children) {
84 | count += children.length;
85 | for (var i = children.length - 1; i >= 0; i--) {
86 | count += getChildrenOf(children[i]);
87 | }
88 | }
89 | return count;
90 | }
91 |
92 | function getAliveChildrenOf(obj){
93 | var count = 0;
94 | if (obj.children) {
95 | var children = obj.children.filter(function(child){
96 | return child.alive;
97 | });
98 | count += children.length;
99 | for (var i = children.length - 1; i >= 0; i--) {
100 | count += getAliveChildrenOf(children[i]);
101 | }
102 | }
103 | return count;
104 | }
--------------------------------------------------------------------------------
/src/js/controllers/TreeCtrl.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | export default class TreeCtrl {
3 | constructor($scope, gameManager, game, onUpdate){
4 | this.$scope = $scope;
5 | this.gameManager = gameManager;
6 | this.game = game;
7 | var apply = _.debounce(() => $scope.$apply());
8 | onUpdate.add(apply);
9 |
10 | this.registerStateScrollUpdate();
11 | this.registerDrawBounds();
12 | this.registerFilter();
13 | }
14 | registerStateScrollUpdate(){
15 | var parent = $('.states');
16 | var child = parent.find('.selected');
17 | this.$scope.$watch(function(){
18 | return child = parent.find('.selected')[0];
19 | }, function(){
20 | if ($(child).offset())
21 | parent.animate({
22 | scrollTop: $(child).offset().top - parent.offset().top + parent.scrollTop()
23 | });
24 | })
25 | }
26 | registerDrawBounds(){
27 | var {gameManager, game} = this;
28 | var {state, debug} = game;
29 | var render = state.render;
30 | /**
31 | * Rewrap the render function when it changes
32 | * to add hightlighting function for $inspectorTreeSelected
33 | */
34 | this.$scope.$watch(function(){
35 | return state.render === render;
36 | }, function() {
37 | render = state.render;
38 | /** TODO: Move the params to somewhere else */
39 | var min = 0.3;
40 | var max = 0.5;
41 | var alpha = min;
42 | var direction = 1;
43 | var delta = 0.005;
44 | /** Wrapping process */
45 | var _render = render;
46 | state.render = function(){
47 | _render.apply(this, arguments);
48 | if (!gameManager.$render) return;
49 | var obj = gameManager.$inspectorTreeSelected;
50 | if (!obj || !obj.getBounds) return;
51 | var bounds = obj.getBounds();
52 | if (!bounds) return;
53 | debug.geom(bounds, `rgba(0, 191, 0, ${alpha})`);
54 | alpha += delta * direction;
55 | if (alpha > max || alpha < min) direction = -direction;
56 | }
57 | })
58 | }
59 | registerFilter(){
60 | var {$scope, gameManager} = this;
61 | $scope.$watch(() => this.search, () => {
62 | gameManager.filter(this.search);
63 | });
64 | }
65 | goToState(state){
66 | this.gameManager.$inspectorTreeSelected = null;
67 | this.game.state.start(state)
68 | }
69 | }
--------------------------------------------------------------------------------
/src/js/controllers/ViewCtrl.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | var DisplayObject = require('../classes/DisplayObject');
3 | export default class ViewCtrl extends DisplayObject {
4 | constructor($scope, $timeout, gameManager){
5 | super($scope.obj, gameManager);
6 | var update = _.throttle(() => {
7 | $timeout(() => {
8 | var width = Math.max.apply(Math, $('#tree .middle').map(function(){
9 | return $(this).width();
10 | }).get());
11 | $('#tree .row').width(Math.max(width + this.childLevel * 20, $('#tree').width()));
12 | })
13 | }, 500);
14 | $('.row').click(update)
15 |
16 | var lastTime = Date.now();
17 | var delta = 1000;
18 | $(window).resize(function(){
19 | lastTime = Date.now();
20 | setTimeout(function(){
21 | if (Date.now() - lastTime > delta) update();
22 | }, delta)
23 | });
24 | }
25 | }
--------------------------------------------------------------------------------
/src/js/directives/interaction.js:
--------------------------------------------------------------------------------
1 | var interact = require('interact.js');
2 | var draggabilly = require('draggabilly');
3 |
4 | function fit({top, left}, width){
5 | top = Math.max(top, 0);
6 | top = Math.min(top, document.documentElement.clientHeight - 50);
7 | left = Math.max(left, - width + 50);
8 | left = Math.min(left, document.documentElement.clientWidth - 50);
9 | return {top, left};
10 | }
11 |
12 | export default function($localStorage, $timeout) {
13 | return {
14 | restrict : 'C',
15 | link : function(scope, element){
16 | var el = $(element);
17 | var element = element[0];
18 |
19 | var { elX, elY, elW, elH } = $localStorage;
20 | elW && el.css({ width : `${elW}px` });
21 | elH && el.css({ height : `${elH}px` });
22 |
23 | /** Enable drag */
24 | var draggie = new draggabilly(element, {
25 | handle: '.grabber'
26 | }).on('dragEnd', function(event, pointer) {
27 | var { left, top } = fit(el.position(), el.width());
28 | el.css({
29 | top : `${top}px`,
30 | left : `${left}px`
31 | });
32 | $localStorage.elX = left;
33 | $localStorage.elY = top;
34 | });
35 | /**
36 | * Since draggabilly set position to relative
37 | * this reset the css properties
38 | */
39 | $timeout(function(){
40 | el.css({
41 | position : 'fixed',
42 | top : `${elY || 0}px`,
43 | left : `${elX || 0}px`
44 | });
45 | });
46 | scope.$watch(function(){
47 | return scope.display.hide;
48 | }, function(){
49 | interact(element).resizable({ enabled : !scope.display.hide });
50 | })
51 | /** Enable resize */
52 | interact(element).resizable({
53 | edges: { left: true, right: true, bottom: true }
54 | }).on('resizemove', function (event) {
55 | /** This function is copied from interact.js example */
56 | var target = event.target,
57 | x = (parseFloat(target.getAttribute('data-x')) || 0),
58 | y = (parseFloat(target.getAttribute('data-y')) || 0);
59 | // update the element's style
60 | target.style.width = event.rect.width + 'px';
61 | target.style.height = event.rect.height + 'px';
62 |
63 | // translate when resizing from top or left edges
64 | x += event.deltaRect.left;
65 | y += event.deltaRect.top;
66 |
67 | target.style.webkitTransform =
68 | target.style.transform =
69 | 'translate(' + x + 'px,' + y + 'px)';
70 |
71 | target.setAttribute('data-x', x);
72 | target.setAttribute('data-y', y);
73 | }).on('resizeend', function(event){
74 | var { left, top } = fit(el.position());
75 | el.css({
76 | top : `${top}px`,
77 | left : `${left}px`
78 | });
79 | $localStorage.elW = el.width();
80 | $localStorage.elH = el.height();
81 | $localStorage.elX = left;
82 | $localStorage.elY = top;
83 | var target = event.target;
84 | target.style.webkitTransform =
85 | target.style.transform =
86 | 'translate(0px, 0px)';
87 | target.setAttribute('data-x', 0);
88 | target.setAttribute('data-y', 0);
89 | });
90 | }
91 | };
92 | }
--------------------------------------------------------------------------------
/src/js/directives/phaserInspectorDetails.js:
--------------------------------------------------------------------------------
1 | var detailsTpl = require('../../tpl/details.html');
2 |
3 | export default function($compile) {
4 | return {
5 | controller : 'DetailCtrl',
6 | controllerAs : 'detailCtrl',
7 | template : detailsTpl
8 | };
9 | };
--------------------------------------------------------------------------------
/src/js/directives/phaserInspectorTree.js:
--------------------------------------------------------------------------------
1 | var treeTpl = require('../../tpl/tree.html');
2 |
3 | export default function($compile) {
4 | return {
5 | controller : 'TreeCtrl',
6 | controllerAs : 'treeCtrl',
7 | template : treeTpl
8 | };
9 | };
--------------------------------------------------------------------------------
/src/js/directives/view.js:
--------------------------------------------------------------------------------
1 | var viewTpl = require('../../tpl/tree/view.html');
2 |
3 | export default function($compile) {
4 | return {
5 | replace : true,
6 | scope : {
7 | obj : '=',
8 | filtered : '='
9 | },
10 | controller : 'ViewCtrl',
11 | controllerAs : 'viewCtrl',
12 | template : viewTpl,
13 | link : function(scope, element){
14 | scope.parent = scope.filtered ? scope.obj.$inspectorFilteredChildren : scope.obj.children;
15 | var collection = $('