@makefully/platypus 4.1.4 → 4.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +4 -2
- package/lib/platypus.js +1 -1
- package/package.json +7 -2
- package/src/AABB.js +685 -0
- package/src/ActionState.js +121 -0
- package/src/AssetManager.js +206 -0
- package/src/CollisionData.js +96 -0
- package/src/CollisionDataContainer.js +127 -0
- package/src/CollisionShape.js +315 -0
- package/src/Component.js +231 -0
- package/src/Data.js +64 -0
- package/src/DataMap.js +204 -0
- package/src/Entity.js +404 -0
- package/src/EntityLinker.js +113 -0
- package/src/EventHandler.js +115 -0
- package/src/EventHandlerList.js +251 -0
- package/src/Game.js +840 -0
- package/src/Gamepad.js +175 -0
- package/src/ID3CaptionPlayer.js +61 -0
- package/src/Messenger.js +365 -0
- package/src/PIXIAnimation.js +622 -0
- package/src/RandomSet.js +140 -0
- package/src/SFXPlayer.js +120 -0
- package/src/StateMap.js +252 -0
- package/src/Storage.js +152 -0
- package/src/TickerClient.js +48 -0
- package/src/TickerServer.js +23 -0
- package/src/TimeEvent.js +50 -0
- package/src/TimeEventList.js +100 -0
- package/src/VOPlayer.js +684 -0
- package/src/Vector.js +702 -0
- package/src/components/AIChaser.js +161 -0
- package/src/components/AIPacer.js +107 -0
- package/src/components/AudioMusic.js +151 -0
- package/src/components/AudioSFX.js +673 -0
- package/src/components/AudioVO.js +301 -0
- package/src/components/CollisionBasic.js +826 -0
- package/src/components/CollisionFilter.js +74 -0
- package/src/components/CollisionGroup.js +448 -0
- package/src/components/CollisionTiles.js +608 -0
- package/src/components/ComponentSwitcher.js +173 -0
- package/src/components/ControllerInput.js +65 -0
- package/src/components/Counter.js +165 -0
- package/src/components/EntityCamera.js +215 -0
- package/src/components/EntityContainer.js +549 -0
- package/src/components/EntityController.js +541 -0
- package/src/components/HandlerCamera.js +1052 -0
- package/src/components/HandlerCollision.js +1318 -0
- package/src/components/HandlerController.js +291 -0
- package/src/components/HandlerLogic.js +394 -0
- package/src/components/HandlerRender.js +217 -0
- package/src/components/Interactive.js +502 -0
- package/src/components/LevelBuilder.js +524 -0
- package/src/components/LogicAngularMovement.js +154 -0
- package/src/components/LogicAttachment.js +265 -0
- package/src/components/LogicButton.js +466 -0
- package/src/components/LogicCarrier.js +39 -0
- package/src/components/LogicDestroyMe.js +58 -0
- package/src/components/LogicDirectionalMovement.js +325 -0
- package/src/components/LogicDragDrop.js +371 -0
- package/src/components/LogicImpactLaunch.js +144 -0
- package/src/components/LogicPacingPlatform.js +64 -0
- package/src/components/LogicPortable.js +112 -0
- package/src/components/LogicPortal.js +115 -0
- package/src/components/LogicPushable.js +98 -0
- package/src/components/LogicRebounder.js +136 -0
- package/src/components/LogicRegionSpawner.js +139 -0
- package/src/components/LogicRotationalMovement.js +205 -0
- package/src/components/LogicSpawner.js +159 -0
- package/src/components/LogicStateMachine.js +382 -0
- package/src/components/LogicSwitch.js +77 -0
- package/src/components/LogicTeleportee.js +68 -0
- package/src/components/LogicTeleporter.js +93 -0
- package/src/components/LogicTimer.js +178 -0
- package/src/components/LogicWindUpRacer.js +179 -0
- package/src/components/Motion.js +371 -0
- package/src/components/Mover.js +566 -0
- package/src/components/Node.js +289 -0
- package/src/components/NodeMap.js +295 -0
- package/src/components/NodeResident.js +636 -0
- package/src/components/Orientation.js +697 -0
- package/src/components/RandomEvents.js +73 -0
- package/src/components/RelativePosition.js +204 -0
- package/src/components/RelayFamily.js +106 -0
- package/src/components/RelayGame.js +53 -0
- package/src/components/RelayLinker.js +94 -0
- package/src/components/RelayParent.js +57 -0
- package/src/components/RelaySelf.js +55 -0
- package/src/components/Render.js +178 -0
- package/src/components/RenderAnimator.js +275 -0
- package/src/components/RenderContainer.js +646 -0
- package/src/components/RenderDebug.js +319 -0
- package/src/components/RenderDestroyMe.js +59 -0
- package/src/components/RenderLottie.js +149 -0
- package/src/components/RenderProgress.js +120 -0
- package/src/components/RenderSpine.js +833 -0
- package/src/components/RenderSprite.js +527 -0
- package/src/components/RenderText.js +147 -0
- package/src/components/RenderTiles.js +1391 -0
- package/src/components/SceneChanger.js +128 -0
- package/src/components/TiledLoader.js +1451 -0
- package/src/components/Timeline.js +314 -0
- package/src/components/Tutorial.js +267 -0
- package/src/components/Tween.js +296 -0
- package/src/components/VoiceOver.js +373 -0
- package/src/components/XHR.js +129 -0
- package/src/components/index.js +79 -0
- package/src/factory.js +305 -0
- package/src/index.js +159 -0
- package/src/mixins/PIXI.Graphics.js +41 -0
- package/src/platypus.css +68 -0
- package/src/sayHello.js +50 -0
- package/src/shapes/Capsule.js +74 -0
- package/src/shapes/Circle.js +43 -0
- package/src/shapes/Ellipse.js +117 -0
- package/src/shapes/Point.js +191 -0
- package/src/shapes/Polygon.js +112 -0
- package/src/shapes/Polyline.js +97 -0
- package/src/shapes/Rectangle.js +78 -0
- package/src/shapes/RoundedRectangle.js +33 -0
- package/src/shapes/Segment.js +346 -0
- package/src/shapes/adjustedXY.js +19 -0
- package/src/shapes/castFromBox2D.js +92 -0
- package/src/shapes/castFromObject.js +53 -0
- package/src/shapes/castFromPixiGraphics.js +6 -0
- package/src/shapes/castFromPixiShape.js +89 -0
- package/src/shapes/castToBox2D.js +190 -0
- package/src/shapes/castToPixiGraphics.js +100 -0
- package/src/shapes/castToPixiShape.js +61 -0
- package/src/shapes/checkPointsMap.js +313 -0
- package/src/shapes/index.js +35 -0
- package/src/utils/array.js +73 -0
- package/src/utils/string.js +39 -0
|
@@ -0,0 +1,1052 @@
|
|
|
1
|
+
/*global platypus, window */
|
|
2
|
+
import AABB from '../AABB.js';
|
|
3
|
+
import {Container} from 'pixi.js';
|
|
4
|
+
import Data from '../Data.js';
|
|
5
|
+
import TweenJS from '@tweenjs/tween.js';
|
|
6
|
+
import Vector from '../Vector.js';
|
|
7
|
+
import createComponentClass from '../factory.js';
|
|
8
|
+
import EntityCamera from './EntityCamera.js';
|
|
9
|
+
|
|
10
|
+
const
|
|
11
|
+
DPR = window.devicePixelRatio || 1,
|
|
12
|
+
STATIC = 0,
|
|
13
|
+
PAN = 1,
|
|
14
|
+
FOLLOW = 2,
|
|
15
|
+
validModes = ['static', 'mouse-pan', 'following'],
|
|
16
|
+
deprecatedModes = {
|
|
17
|
+
pan: PAN,
|
|
18
|
+
locked: FOLLOW,
|
|
19
|
+
forward: FOLLOW,
|
|
20
|
+
bounding: FOLLOW,
|
|
21
|
+
'anchor-bound': FOLLOW
|
|
22
|
+
},
|
|
23
|
+
getCameraMode = (mode = 'static') => {
|
|
24
|
+
if (validModes.indexOf(mode) >= 0) {
|
|
25
|
+
return mode;
|
|
26
|
+
} else if (typeof deprecatedModes[mode] === 'number') {
|
|
27
|
+
const
|
|
28
|
+
alt = validModes[deprecatedModes[mode]];
|
|
29
|
+
|
|
30
|
+
platypus.debug.warn(`"${mode} has been deprecated. Use "${alt}" instead.`);
|
|
31
|
+
return alt;
|
|
32
|
+
} else {
|
|
33
|
+
platypus.debug.warn(`"${mode} is not a valid mode. Available modes include "${validModes.join('", "')}".`);
|
|
34
|
+
return 'static';
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
doNothing = function () {
|
|
38
|
+
return false;
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// These fix coords for touch events filling in for pointer events from the PIXI InteractiveManager
|
|
42
|
+
getClientX = function (event) {
|
|
43
|
+
if (!event.clientX) {
|
|
44
|
+
if (event.touches && event.touches[0] && event.touches[0].clientX) {
|
|
45
|
+
return event.touches[0].clientX;
|
|
46
|
+
}
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
return event.clientX;
|
|
50
|
+
},
|
|
51
|
+
getClientY = function (event) {
|
|
52
|
+
if (!event.clientY) {
|
|
53
|
+
if (event.touches && event.touches[0] && event.touches[0].clientY) {
|
|
54
|
+
return event.touches[0].clientY;
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
return event.clientY;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default createComponentClass(/** @lends platypus.components.Camera.prototype */{
|
|
62
|
+
id: 'HandlerCamera',
|
|
63
|
+
properties: {
|
|
64
|
+
/**
|
|
65
|
+
* Number specifying width of viewport in world coordinates.
|
|
66
|
+
*
|
|
67
|
+
* @property width
|
|
68
|
+
* @type number
|
|
69
|
+
* @default 0
|
|
70
|
+
**/
|
|
71
|
+
"width": 0,
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Number specifying height of viewport in world coordinates.
|
|
75
|
+
*
|
|
76
|
+
* @property height
|
|
77
|
+
* @type number
|
|
78
|
+
* @default 0
|
|
79
|
+
**/
|
|
80
|
+
"height": 0,
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Specifies whether the camera should be draggable via the mouse by setting to 'pan'.
|
|
84
|
+
*
|
|
85
|
+
* @property mode
|
|
86
|
+
* @type String
|
|
87
|
+
* @default 'static'
|
|
88
|
+
**/
|
|
89
|
+
"mode": "static",
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Whether camera overflows to cover the whole canvas or remains contained within its aspect ratio's boundary.
|
|
93
|
+
*
|
|
94
|
+
* @property overflow
|
|
95
|
+
* @type boolean
|
|
96
|
+
* @default true
|
|
97
|
+
* @deprecated
|
|
98
|
+
*/
|
|
99
|
+
"overflow": false,
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Boolean value that determines whether the camera should stretch the world viewport when window is resized. Defaults to false which maintains the proper aspect ratio.
|
|
103
|
+
*
|
|
104
|
+
* @property stretch
|
|
105
|
+
* @type boolean
|
|
106
|
+
* @default: false
|
|
107
|
+
* @deprecated
|
|
108
|
+
*/
|
|
109
|
+
"stretch": false,
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Sets how many units the followed entity can move before the camera will re-center. This should be lowered for small-value coordinate systems such as Box2D.
|
|
113
|
+
*
|
|
114
|
+
* @property threshold
|
|
115
|
+
* @type number
|
|
116
|
+
* @default 1
|
|
117
|
+
**/
|
|
118
|
+
"threshold": 1,
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Whether, when following an entity, the camera should rotate to match the entity's orientation.
|
|
122
|
+
*
|
|
123
|
+
* @property rotate
|
|
124
|
+
* @type boolean
|
|
125
|
+
* @default false
|
|
126
|
+
**/
|
|
127
|
+
"rotate": false,
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Number specifying the horizontal center of viewport in world coordinates.
|
|
131
|
+
*
|
|
132
|
+
* @property x
|
|
133
|
+
* @type number
|
|
134
|
+
* @default 0
|
|
135
|
+
**/
|
|
136
|
+
"x": 0,
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Number specifying the vertical center of viewport in world coordinates.
|
|
140
|
+
*
|
|
141
|
+
* @property y
|
|
142
|
+
* @type number
|
|
143
|
+
* @default 0
|
|
144
|
+
**/
|
|
145
|
+
"y": 0
|
|
146
|
+
},
|
|
147
|
+
publicProperties: {
|
|
148
|
+
/**
|
|
149
|
+
* The entity's canvas element is used to determine the window size of the camera.
|
|
150
|
+
*
|
|
151
|
+
* @property canvas
|
|
152
|
+
* @type Canvas
|
|
153
|
+
* @default null
|
|
154
|
+
*/
|
|
155
|
+
"canvas": null,
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Sets how quickly the camera should pan to a new position in the horizontal direction.
|
|
159
|
+
*
|
|
160
|
+
* @property transitionX
|
|
161
|
+
* @type number
|
|
162
|
+
* @default 400
|
|
163
|
+
**/
|
|
164
|
+
"transitionX": 400,
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Sets how quickly the camera should pan to a new position in the vertical direction.
|
|
168
|
+
*
|
|
169
|
+
* @property transitionY
|
|
170
|
+
* @type number
|
|
171
|
+
* @default 600
|
|
172
|
+
**/
|
|
173
|
+
"transitionY": 600,
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Sets how quickly the camera should rotate to a new orientation.
|
|
177
|
+
*
|
|
178
|
+
* @property transitionAngle
|
|
179
|
+
* @type number
|
|
180
|
+
* @default: 600
|
|
181
|
+
**/
|
|
182
|
+
"transitionAngle": 600,
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Provides access at the layer level to the camera's world viewport.
|
|
186
|
+
*
|
|
187
|
+
* ```javascript
|
|
188
|
+
* {
|
|
189
|
+
* viewport, //platypus.AABB
|
|
190
|
+
* orientation
|
|
191
|
+
* }
|
|
192
|
+
* ```
|
|
193
|
+
*
|
|
194
|
+
* @property worldCamera
|
|
195
|
+
* @type platypus.Data
|
|
196
|
+
*/
|
|
197
|
+
worldCamera: null,
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Sets the z-order of this layer relative to other loaded layers.
|
|
201
|
+
*
|
|
202
|
+
* @property z
|
|
203
|
+
* @type Number
|
|
204
|
+
* @default 0
|
|
205
|
+
*/
|
|
206
|
+
"z": 0
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* This component controls the game camera deciding where and how it should move. The camera also broadcasts messages when the window resizes or its orientation changes.
|
|
211
|
+
*
|
|
212
|
+
* @memberof platypus.components
|
|
213
|
+
* @uses platypus.Component
|
|
214
|
+
* @constructs
|
|
215
|
+
* @listens platypus.Entity#child-entity-added
|
|
216
|
+
* @listens platypus.Entity#child-entity-updated
|
|
217
|
+
* @listens platypus.Entity#follow
|
|
218
|
+
* @listens platypus.Entity#load
|
|
219
|
+
* @listens platypus.Entity#pointerdown
|
|
220
|
+
* @listens platypus.Entity#pressmove
|
|
221
|
+
* @listens platypus.Entity#pressup
|
|
222
|
+
* @listens platypus.Entity#relocate
|
|
223
|
+
* @listens platypus.Entity#render-world
|
|
224
|
+
* @listens platypus.Entity#resize-camera
|
|
225
|
+
* @listens platypus.Entity#shake
|
|
226
|
+
* @listens platypus.Entity#tick
|
|
227
|
+
* @listens platypus.Entity#world-loaded
|
|
228
|
+
* @fires platypus.Entity#camera-loaded
|
|
229
|
+
* @fires platypus.Entity#camera-update
|
|
230
|
+
* @fires platypus.Entity#render-update
|
|
231
|
+
*/
|
|
232
|
+
initialize: function (definition) {
|
|
233
|
+
const
|
|
234
|
+
worldVP = AABB.setUp(this.x, this.y, this.width, this.height),
|
|
235
|
+
worldCamera = Data.setUp(
|
|
236
|
+
"viewport", worldVP,
|
|
237
|
+
"orientation", definition.orientation || 0
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
//The dimensions of the camera in the window
|
|
241
|
+
this.viewport = AABB.setUp(0, 0, 0, 0);
|
|
242
|
+
|
|
243
|
+
//The dimensions of the camera in the game world
|
|
244
|
+
this.worldCamera = worldCamera;
|
|
245
|
+
|
|
246
|
+
//Message object defined here so it's reusable
|
|
247
|
+
this.worldDimensions = AABB.setUp();
|
|
248
|
+
|
|
249
|
+
this.unbounded = true; // no bounds on camera movement
|
|
250
|
+
|
|
251
|
+
//Whether the map has finished loading.
|
|
252
|
+
this.worldIsLoaded = false;
|
|
253
|
+
|
|
254
|
+
this.mode = getCameraMode(this.mode);
|
|
255
|
+
|
|
256
|
+
//FOLLOW MODE
|
|
257
|
+
this.cameras = [];
|
|
258
|
+
|
|
259
|
+
this.xMagnitude = 0;
|
|
260
|
+
this.yMagnitude = 0;
|
|
261
|
+
this.xWaveLength = 0;
|
|
262
|
+
this.yWaveLength = 0;
|
|
263
|
+
this.xShakeTime = 0;
|
|
264
|
+
this.yShakeTime = 0;
|
|
265
|
+
this.shakeTime = 0;
|
|
266
|
+
this.shakeIncrementor = 0;
|
|
267
|
+
|
|
268
|
+
this.direction = true;
|
|
269
|
+
this.stationary = false;
|
|
270
|
+
|
|
271
|
+
this.viewportUpdate = false;
|
|
272
|
+
|
|
273
|
+
if (this.owner.container) {
|
|
274
|
+
this.parentContainer = this.owner.container;
|
|
275
|
+
} else if (this.owner.stage) {
|
|
276
|
+
this.canvas = this.canvas || platypus.game.canvas; //TODO: Probably need to find a better way to handle resizing - DDD 10/4/2015
|
|
277
|
+
this.parentContainer = this.owner.stage;
|
|
278
|
+
this.owner.width = this.canvas.width;
|
|
279
|
+
this.owner.height = this.canvas.height;
|
|
280
|
+
} else {
|
|
281
|
+
platypus.debug.warn('Camera: There appears to be no Container on this entity for the camera to display.');
|
|
282
|
+
}
|
|
283
|
+
this.container = new Container();
|
|
284
|
+
this.container.zIndex = this.z;
|
|
285
|
+
this.container.visible = false;
|
|
286
|
+
this.parentContainer.addChild(this.container);
|
|
287
|
+
this.movedCamera = false;
|
|
288
|
+
},
|
|
289
|
+
events: {
|
|
290
|
+
"load": function () {
|
|
291
|
+
this.resize();
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
"render-world": function (data) {
|
|
295
|
+
this.world = data.world;
|
|
296
|
+
this.container.addChild(this.world);
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
"child-entity-added": function (entity) {
|
|
300
|
+
this.viewportUpdate = true;
|
|
301
|
+
|
|
302
|
+
if (this.worldIsLoaded) {
|
|
303
|
+
const
|
|
304
|
+
msg = Data.setUp(
|
|
305
|
+
"viewport", AABB.setUp(this.worldCamera.viewport),
|
|
306
|
+
"world", this.worldDimensions
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* On receiving a "world-loaded" message, the camera broadcasts the world size to all children in the world.
|
|
311
|
+
*
|
|
312
|
+
* @event platypus.Entity#camera-loaded
|
|
313
|
+
* @param camera {Object}
|
|
314
|
+
* @param camera.world {platypus.AABB} The dimensions of the world.
|
|
315
|
+
* @param camera.viewport {platypus.AABB} The AABB describing the camera viewport in world units.
|
|
316
|
+
**/
|
|
317
|
+
entity.triggerEvent('camera-loaded', msg);
|
|
318
|
+
|
|
319
|
+
msg.viewport.recycle();
|
|
320
|
+
msg.recycle();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (typeof entity.cameraFocus === 'number') {
|
|
324
|
+
this.addCamera(entity);
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
"child-entity-updated": function (entity) {
|
|
329
|
+
this.viewportUpdate = true;
|
|
330
|
+
|
|
331
|
+
if (this.worldIsLoaded) {
|
|
332
|
+
const
|
|
333
|
+
{container, owner, stationary, windowPerWorldUnitHeight, windowPerWorldUnitWidth, world, worldCamera} = this,
|
|
334
|
+
{viewport} = worldCamera,
|
|
335
|
+
msg = Data.setUp(
|
|
336
|
+
"viewport", AABB.setUp(viewport),
|
|
337
|
+
"scaleX", windowPerWorldUnitWidth,
|
|
338
|
+
"scaleY", windowPerWorldUnitHeight,
|
|
339
|
+
"orientation", worldCamera.orientation,
|
|
340
|
+
"stationary", stationary,
|
|
341
|
+
"world", this.worldDimensions
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
entity.triggerEvent('camera-update', msg);
|
|
345
|
+
msg.viewport.recycle();
|
|
346
|
+
msg.recycle();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (typeof entity.cameraFocus === 'number') {
|
|
350
|
+
this.addCamera(entity);
|
|
351
|
+
} else {
|
|
352
|
+
this.removeCamera(entity);
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
"child-entity-removed": function (entity) {
|
|
357
|
+
this.viewportUpdate = true;
|
|
358
|
+
|
|
359
|
+
this.removeCamera(entity);
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
"world-loaded": function (values) {
|
|
363
|
+
const
|
|
364
|
+
{owner} = this;
|
|
365
|
+
|
|
366
|
+
this.worldDimensions.set(values.world);
|
|
367
|
+
this.unbounded = !!values.level.infinite;
|
|
368
|
+
|
|
369
|
+
this.worldIsLoaded = true;
|
|
370
|
+
if (values.camera) {
|
|
371
|
+
this.follow(values.camera);
|
|
372
|
+
}
|
|
373
|
+
if (owner.triggerEventOnChildren) {
|
|
374
|
+
const
|
|
375
|
+
msg = Data.setUp(
|
|
376
|
+
"viewport", AABB.setUp(this.worldCamera.viewport),
|
|
377
|
+
"world", this.worldDimensions
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
owner.triggerEventOnChildren('camera-loaded', msg);
|
|
381
|
+
|
|
382
|
+
msg.viewport.recycle();
|
|
383
|
+
msg.recycle();
|
|
384
|
+
}
|
|
385
|
+
this.updateMovementMethods();
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
"pointerdown": function (event) {
|
|
389
|
+
if (this.state === 'mouse-pan') {
|
|
390
|
+
const
|
|
391
|
+
{viewport} = this.worldCamera;
|
|
392
|
+
|
|
393
|
+
if (!this.mouseVector) {
|
|
394
|
+
this.mouseVector = Vector.setUp();
|
|
395
|
+
this.mouseWorldOrigin = Vector.setUp();
|
|
396
|
+
}
|
|
397
|
+
this.mouse = this.mouseVector;
|
|
398
|
+
this.mouse.x = getClientX(event.event);
|
|
399
|
+
this.mouse.y = getClientY(event.event);
|
|
400
|
+
this.mouseWorldOrigin.x = viewport.x;
|
|
401
|
+
this.mouseWorldOrigin.y = viewport.y;
|
|
402
|
+
event.pixiEvent.stopPropagation();
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
"pressmove": function (event) {
|
|
407
|
+
if (this.mouse) {
|
|
408
|
+
if (this.move(this.mouseWorldOrigin.x + ((this.mouse.x - getClientX(event.event)) * DPR) / this.world.worldTransform.a, this.mouseWorldOrigin.y + ((this.mouse.y - getClientY(event.event)) * DPR) / this.world.worldTransform.d)) {
|
|
409
|
+
this.viewportUpdate = true;
|
|
410
|
+
this.movedCamera = true;
|
|
411
|
+
event.pixiEvent.stopPropagation();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
"pressup": function (event) {
|
|
417
|
+
if (this.mouse) {
|
|
418
|
+
this.mouse = null;
|
|
419
|
+
if (this.movedCamera) {
|
|
420
|
+
this.movedCamera = false;
|
|
421
|
+
event.pixiEvent.stopPropagation();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
|
|
426
|
+
"tick": function ({delta}) {
|
|
427
|
+
if ((this.mode === 'following') && this.lockedFollow(this.cameras, delta)) {
|
|
428
|
+
this.viewportUpdate = true;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Need to update owner's size information for changes to canvas size
|
|
432
|
+
if (this.canvas) {
|
|
433
|
+
this.owner.width = this.canvas.width;
|
|
434
|
+
this.owner.height = this.canvas.height;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Check for owner resizing
|
|
438
|
+
if ((this.owner.width !== this.lastWidth) || (this.owner.height !== this.lastHeight)) {
|
|
439
|
+
this.resize();
|
|
440
|
+
this.lastWidth = this.owner.width;
|
|
441
|
+
this.lastHeight = this.owner.height;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (this.shakeIncrementor < this.shakeTime) {
|
|
445
|
+
const viewport = this.worldCamera.viewport;
|
|
446
|
+
|
|
447
|
+
this.viewportUpdate = true;
|
|
448
|
+
this.shakeIncrementor += delta;
|
|
449
|
+
this.shakeIncrementor = Math.min(this.shakeIncrementor, this.shakeTime);
|
|
450
|
+
|
|
451
|
+
if (this.shakeIncrementor < this.xShakeTime) {
|
|
452
|
+
viewport.moveX(viewport.x + Math.sin((this.shakeIncrementor / this.xWaveLength) * (Math.PI * 2)) * this.xMagnitude);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (this.shakeIncrementor < this.yShakeTime) {
|
|
456
|
+
viewport.moveY(viewport.y + Math.sin((this.shakeIncrementor / this.yWaveLength) * (Math.PI * 2)) * this.yMagnitude);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
this.updateViewport();
|
|
461
|
+
|
|
462
|
+
if (this.container.zIndex !== this.z) {
|
|
463
|
+
this.container.zIndex = this.z;
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* The camera listens for this event to change its world viewport size.
|
|
469
|
+
*
|
|
470
|
+
* @event platypus.Entity#resize-camera
|
|
471
|
+
* @param {Object} [dimensions] List of key/value pairs describing new viewport size
|
|
472
|
+
* @param {number} dimensions.width Width of the camera viewport
|
|
473
|
+
* @param {number} dimensions.height Height of the camera viewport
|
|
474
|
+
* @param {number} dimensions.time Time in millseconds over which to tween the scale change.
|
|
475
|
+
* @param {Boolean} [forceUpdate] Whether to update graphics.
|
|
476
|
+
**/
|
|
477
|
+
"resize-camera": function (dimensions = {}, forceUpdate = false) {
|
|
478
|
+
const
|
|
479
|
+
{width, height, time, ease} = dimensions,
|
|
480
|
+
forcedUpdate = forceUpdate || dimensions.forceUpdate;
|
|
481
|
+
|
|
482
|
+
if (time) {
|
|
483
|
+
const
|
|
484
|
+
tween = new TweenJS.Tween(this);
|
|
485
|
+
|
|
486
|
+
tween.to({width, height}, time);
|
|
487
|
+
if (ease) {
|
|
488
|
+
tween.easing(ease);
|
|
489
|
+
}
|
|
490
|
+
tween.onUpdate(() => {
|
|
491
|
+
this.resize();
|
|
492
|
+
}).start();
|
|
493
|
+
} else {
|
|
494
|
+
if (width && height) {
|
|
495
|
+
this.width = dimensions.width;
|
|
496
|
+
this.height = dimensions.height;
|
|
497
|
+
}
|
|
498
|
+
if (this.canvas) {
|
|
499
|
+
this.owner.width = this.canvas.width;
|
|
500
|
+
this.owner.height = this.canvas.height;
|
|
501
|
+
}
|
|
502
|
+
this.resize();
|
|
503
|
+
}
|
|
504
|
+
if (forcedUpdate) {
|
|
505
|
+
this.updateViewport();
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Sends a 'handle-render' message to all the children in the Container. This bypasses a render pause value and is useful for resizes happening outside the game loop.
|
|
509
|
+
*
|
|
510
|
+
* @event platypus.Entity#render-update
|
|
511
|
+
* @param tick {Object} An object containing tick data.
|
|
512
|
+
*/
|
|
513
|
+
this.owner.triggerEvent('render-update');
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* The camera listens for this event to change its position in the world.
|
|
519
|
+
*
|
|
520
|
+
* @event platypus.Entity#relocate
|
|
521
|
+
* @param {Vector|Object} location List of key/value pairs describing new location
|
|
522
|
+
* @param {Number} location.x New position along the x-axis.
|
|
523
|
+
* @param {Number} location.y New position along the y-axis.
|
|
524
|
+
* @param {Number} [location.time] The time to transition to the new location.
|
|
525
|
+
* @param {Function} [location.ease] The ease function to use. Defaults to a linear transition.
|
|
526
|
+
*/
|
|
527
|
+
"relocate": function (location) {
|
|
528
|
+
if (location.time) {
|
|
529
|
+
const
|
|
530
|
+
worldVP = this.worldCamera.viewport,
|
|
531
|
+
v = Vector.setUp(worldVP.x, worldVP.y),
|
|
532
|
+
tween = new TweenJS.Tween(v);
|
|
533
|
+
|
|
534
|
+
tween.to({x: location.x, y: location.y}, location.time);
|
|
535
|
+
if (location.ease) {
|
|
536
|
+
tween.easing(location.ease);
|
|
537
|
+
}
|
|
538
|
+
tween.onUpdate(() => this.viewportUpdate |= !this.owner.destroyed && this.move(v.x, v.y)).onStop(() => v.recycle()).start();
|
|
539
|
+
} else if (this.move(location.x, location.y)) {
|
|
540
|
+
this.viewportUpdate = true;
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
"follow": function (def) {
|
|
545
|
+
this.follow(def);
|
|
546
|
+
},
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* On receiving this message, the camera will shake around its target location.
|
|
550
|
+
*
|
|
551
|
+
* @event platypus.Entity#shake
|
|
552
|
+
* @param {Object} shake
|
|
553
|
+
* @param {number} [shake.xMagnitude] How much to move along the x axis.
|
|
554
|
+
* @param {number} [shake.yMagnitude] How much to move along the y axis.
|
|
555
|
+
* @param {number} [shake.xFrequency] How quickly to shake along the x axis.
|
|
556
|
+
* @param {number} [shake.yFrequency] How quickly to shake along the y axis.
|
|
557
|
+
* @param {number} [shake.time] How long the camera should shake.
|
|
558
|
+
*/
|
|
559
|
+
"shake": function (def = {}) {
|
|
560
|
+
const
|
|
561
|
+
xMag = def.xMagnitude || 0,
|
|
562
|
+
yMag = def.yMagnitude || 0,
|
|
563
|
+
xFreq = def.xFrequency || 0, //Cycles per second
|
|
564
|
+
yFreq = def.yFrequency || 0, //Cycles per second
|
|
565
|
+
second = 1000,
|
|
566
|
+
time = def.time || 0;
|
|
567
|
+
|
|
568
|
+
this.viewportUpdate = true;
|
|
569
|
+
|
|
570
|
+
this.shakeIncrementor = 0;
|
|
571
|
+
|
|
572
|
+
this.xMagnitude = xMag;
|
|
573
|
+
this.yMagnitude = yMag;
|
|
574
|
+
|
|
575
|
+
if (xFreq === 0) {
|
|
576
|
+
this.xWaveLength = 1;
|
|
577
|
+
this.xShakeTime = 0;
|
|
578
|
+
} else {
|
|
579
|
+
this.xWaveLength = (second / xFreq);
|
|
580
|
+
this.xShakeTime = Math.ceil(time / this.xWaveLength) * this.xWaveLength;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (yFreq === 0) {
|
|
584
|
+
this.yWaveLength = 1;
|
|
585
|
+
this.yShakeTime = 0;
|
|
586
|
+
} else {
|
|
587
|
+
this.yWaveLength = (second / yFreq);
|
|
588
|
+
this.yShakeTime = Math.ceil(time / this.yWaveLength) * this.yWaveLength;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
this.shakeTime = Math.max(this.xShakeTime, this.yShakeTime);
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
|
|
595
|
+
methods: {
|
|
596
|
+
follow: function ({begin = 0, entity, mode, orientation = 0, time = 0, x, y} = {}) {
|
|
597
|
+
if (begin || time) {
|
|
598
|
+
platypus.debug.warn(`"begin" and "time" syntax has been deprecated. Use the "Timeline" component to set up transitional timings instead.`);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
this.mode = getCameraMode(mode);
|
|
602
|
+
|
|
603
|
+
switch (this.mode) {
|
|
604
|
+
case 'following':
|
|
605
|
+
this.addCamera(entity);
|
|
606
|
+
break;
|
|
607
|
+
case 'mouse-pan':
|
|
608
|
+
if ((typeof x === 'number') && (typeof y === 'number')) {
|
|
609
|
+
this.move(x, y, orientation);
|
|
610
|
+
this.viewportUpdate = true;
|
|
611
|
+
}
|
|
612
|
+
break;
|
|
613
|
+
default:
|
|
614
|
+
if ((typeof x === 'number') && (typeof y === 'number')) {
|
|
615
|
+
this.move(x, y, orientation);
|
|
616
|
+
this.viewportUpdate = true;
|
|
617
|
+
}
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
},
|
|
621
|
+
|
|
622
|
+
addCamera (entity) {
|
|
623
|
+
const
|
|
624
|
+
needsAdding = this.cameras.indexOf(entity) === -1;
|
|
625
|
+
|
|
626
|
+
if (needsAdding) {
|
|
627
|
+
if (typeof entity.cameraFocus !== 'number') {
|
|
628
|
+
entity.addComponent(new EntityCamera(entity, {}));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
this.cameras.push(entity);
|
|
632
|
+
this.mode = 'following';
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return needsAdding;
|
|
636
|
+
},
|
|
637
|
+
|
|
638
|
+
removeCamera (entity) {
|
|
639
|
+
const
|
|
640
|
+
index = this.cameras.indexOf(entity),
|
|
641
|
+
canBeRemoved = index >= 0;
|
|
642
|
+
|
|
643
|
+
if (canBeRemoved) {
|
|
644
|
+
this.cameras.splice(index, 1);
|
|
645
|
+
if (this.cameras.length === 0) {
|
|
646
|
+
this.mode = 'static';
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return canBeRemoved;
|
|
651
|
+
},
|
|
652
|
+
|
|
653
|
+
move: function (x, y, newOrientation) {
|
|
654
|
+
const
|
|
655
|
+
movedX = this.moveX(x),
|
|
656
|
+
movedY = this.moveY(y),
|
|
657
|
+
movedR = this.rotate && this.reorient(newOrientation || 0);
|
|
658
|
+
|
|
659
|
+
return movedX || movedY || movedR;
|
|
660
|
+
},
|
|
661
|
+
|
|
662
|
+
moveX: doNothing,
|
|
663
|
+
|
|
664
|
+
moveY: doNothing,
|
|
665
|
+
|
|
666
|
+
reorient: function (newOrientation) {
|
|
667
|
+
const
|
|
668
|
+
errMargin = 0.0001,
|
|
669
|
+
{worldCamera} = this;
|
|
670
|
+
|
|
671
|
+
if (Math.abs(worldCamera.orientation - newOrientation) > errMargin) {
|
|
672
|
+
worldCamera.orientation = newOrientation;
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
675
|
+
return false;
|
|
676
|
+
},
|
|
677
|
+
|
|
678
|
+
lockedFollow (entities) {
|
|
679
|
+
const
|
|
680
|
+
{worldCamera: {viewport: currentBounds}, worldDimensions: {top, bottom, left, right, height, width, x, y}, unbounded} = this,
|
|
681
|
+
sum = entities.reduce((prev, {cameraFocus}) => prev + cameraFocus, 0),
|
|
682
|
+
useCurrentBounds = sum < 1,
|
|
683
|
+
list = entities.filter(({cameraFocus}) => cameraFocus > 0),
|
|
684
|
+
reduce = (property) => [({focus, result}, {cameraFocus, cameraBounds}) => {
|
|
685
|
+
const
|
|
686
|
+
value = cameraBounds[property];
|
|
687
|
+
|
|
688
|
+
if (cameraFocus > focus) {
|
|
689
|
+
cameraFocus = focus;
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
focus: (cameraFocus <= focus) ? focus - cameraFocus : 0,
|
|
693
|
+
result: result + cameraFocus * value
|
|
694
|
+
};
|
|
695
|
+
}, {
|
|
696
|
+
focus: sum,
|
|
697
|
+
result: useCurrentBounds ? currentBounds[property] * (1 - sum) : 0
|
|
698
|
+
}],
|
|
699
|
+
{result: t} = list.sort(({cameraBounds: {top: a}}, {cameraBounds: {top: b}}) => a - b).reduce(...reduce('top')),
|
|
700
|
+
{result: b} = list.sort(({cameraBounds: {bottom: a}}, {cameraBounds: {bottom: b}}) => b - a).reduce(...reduce('bottom')),
|
|
701
|
+
{result: l} = list.sort(({cameraBounds: {left: a}}, {cameraBounds: {left: b}}) => a - b).reduce(...reduce('left')),
|
|
702
|
+
{result: r} = list.sort(({cameraBounds: {right: a}}, {cameraBounds: {right: b}}) => b - a).reduce(...reduce('right')),
|
|
703
|
+
lastBounds = AABB.setUp(currentBounds);
|
|
704
|
+
|
|
705
|
+
currentBounds.setBounds(l, t, r, b);
|
|
706
|
+
if (this.width !== currentBounds.width || this.height !== currentBounds.height) {
|
|
707
|
+
this.width = currentBounds.width;
|
|
708
|
+
this.height = currentBounds.height;
|
|
709
|
+
this.matchAspectRatio();
|
|
710
|
+
this.resize();
|
|
711
|
+
} else {
|
|
712
|
+
this.matchAspectRatio();
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (!unbounded) {
|
|
716
|
+
if (width) {
|
|
717
|
+
if (width < currentBounds.width) {
|
|
718
|
+
currentBounds.moveX(x);
|
|
719
|
+
} else if (left > currentBounds.left) {
|
|
720
|
+
currentBounds.moveX(left + currentBounds.halfWidth);
|
|
721
|
+
} else if (right < currentBounds.right) {
|
|
722
|
+
currentBounds.moveX(right - currentBounds.halfWidth);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (height) {
|
|
726
|
+
if (height < currentBounds.height) {
|
|
727
|
+
currentBounds.moveY(y);
|
|
728
|
+
} else if (top > currentBounds.top) {
|
|
729
|
+
currentBounds.moveY(top + currentBounds.halfHeight);
|
|
730
|
+
} else if (bottom < currentBounds.bottom) {
|
|
731
|
+
currentBounds.moveY(bottom - currentBounds.halfHeight);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (lastBounds.equals(currentBounds)) {
|
|
737
|
+
lastBounds.recycle();
|
|
738
|
+
return false;
|
|
739
|
+
} else {
|
|
740
|
+
lastBounds.recycle();
|
|
741
|
+
return true;
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
|
|
745
|
+
matchAspectRatio () {
|
|
746
|
+
const
|
|
747
|
+
{height, owner, width, worldCamera} = this,
|
|
748
|
+
{viewport} = worldCamera,
|
|
749
|
+
worldAspectRatio = width / height,
|
|
750
|
+
windowAspectRatio = owner.width / owner.height;
|
|
751
|
+
|
|
752
|
+
if (windowAspectRatio > worldAspectRatio) {
|
|
753
|
+
viewport.resize(height * windowAspectRatio, height);
|
|
754
|
+
} else {
|
|
755
|
+
viewport.resize(width, width / windowAspectRatio);
|
|
756
|
+
}
|
|
757
|
+
},
|
|
758
|
+
|
|
759
|
+
clampAspectRatio () {
|
|
760
|
+
const
|
|
761
|
+
{height, owner, viewport, width} = this,
|
|
762
|
+
worldAspectRatio = width / height,
|
|
763
|
+
windowAspectRatio = owner.width / owner.height;
|
|
764
|
+
|
|
765
|
+
if (windowAspectRatio > worldAspectRatio) {
|
|
766
|
+
viewport.resize(viewport.height * worldAspectRatio, viewport.height);
|
|
767
|
+
} else {
|
|
768
|
+
viewport.resize(viewport.width, viewport.width / worldAspectRatio);
|
|
769
|
+
}
|
|
770
|
+
},
|
|
771
|
+
|
|
772
|
+
resize: function () {
|
|
773
|
+
const
|
|
774
|
+
{container, height, owner, viewport, width, worldCamera} = this,
|
|
775
|
+
worldAspectRatio = width / height,
|
|
776
|
+
windowAspectRatio = owner.width / owner.height,
|
|
777
|
+
{viewport: worldVP} = worldCamera;
|
|
778
|
+
|
|
779
|
+
//The dimensions of the camera in the window
|
|
780
|
+
viewport.setAll(owner.width / 2, owner.height / 2, owner.width, owner.height);
|
|
781
|
+
|
|
782
|
+
if (windowAspectRatio > worldAspectRatio) {
|
|
783
|
+
worldVP.resize(height * windowAspectRatio, height);
|
|
784
|
+
} else {
|
|
785
|
+
worldVP.resize(width, width / windowAspectRatio);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
this.worldPerWindowUnitWidth = worldVP.width / viewport.width;
|
|
789
|
+
this.worldPerWindowUnitHeight = worldVP.height / viewport.height;
|
|
790
|
+
this.windowPerWorldUnitWidth = viewport.width / worldVP.width;
|
|
791
|
+
this.windowPerWorldUnitHeight = viewport.height / worldVP.height;
|
|
792
|
+
|
|
793
|
+
container.updateTransform({
|
|
794
|
+
x: viewport.x - viewport.halfWidth,
|
|
795
|
+
y: viewport.y - viewport.halfHeight
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
this.viewportUpdate = true;
|
|
799
|
+
|
|
800
|
+
this.updateMovementMethods();
|
|
801
|
+
},
|
|
802
|
+
|
|
803
|
+
updateMovementMethods: (function () {
|
|
804
|
+
// This is used to change movement modes as needed rather than doing a check every tick to determine movement type. - DDD 2/29/2016
|
|
805
|
+
const
|
|
806
|
+
centerX = function () {
|
|
807
|
+
const
|
|
808
|
+
{worldDimensions} = this;
|
|
809
|
+
|
|
810
|
+
this.worldCamera.viewport.moveX(worldDimensions.width / 2 + worldDimensions.left);
|
|
811
|
+
this.moveX = doNothing;
|
|
812
|
+
return true;
|
|
813
|
+
},
|
|
814
|
+
centerY = function () {
|
|
815
|
+
const
|
|
816
|
+
{worldDimensions} = this;
|
|
817
|
+
|
|
818
|
+
this.worldCamera.viewport.moveY(worldDimensions.height / 2 + worldDimensions.top);
|
|
819
|
+
this.moveY = doNothing;
|
|
820
|
+
return true;
|
|
821
|
+
},
|
|
822
|
+
containX = function (x) {
|
|
823
|
+
const
|
|
824
|
+
{worldDimensions, worldCamera} = this,
|
|
825
|
+
{viewport} = worldCamera,
|
|
826
|
+
{left, width} = worldDimensions;
|
|
827
|
+
|
|
828
|
+
if (Math.abs(viewport.x - x) > this.threshold) {
|
|
829
|
+
if (x + viewport.halfWidth > width + left) {
|
|
830
|
+
viewport.moveX(width - viewport.halfWidth + left);
|
|
831
|
+
} else if (x < viewport.halfWidth + left) {
|
|
832
|
+
viewport.moveX(viewport.halfWidth + left);
|
|
833
|
+
} else {
|
|
834
|
+
viewport.moveX(x);
|
|
835
|
+
}
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
return false;
|
|
839
|
+
},
|
|
840
|
+
containY = function (y) {
|
|
841
|
+
const
|
|
842
|
+
{worldDimensions, worldCamera} = this,
|
|
843
|
+
{viewport} = worldCamera,
|
|
844
|
+
{height, top} = worldDimensions;
|
|
845
|
+
|
|
846
|
+
if (Math.abs(viewport.y - y) > this.threshold) {
|
|
847
|
+
if (y + viewport.halfHeight > height + top) {
|
|
848
|
+
viewport.moveY(height - viewport.halfHeight + top);
|
|
849
|
+
} else if (y < viewport.halfHeight + top) {
|
|
850
|
+
viewport.moveY(viewport.halfHeight + top);
|
|
851
|
+
} else {
|
|
852
|
+
viewport.moveY(y);
|
|
853
|
+
}
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
return false;
|
|
857
|
+
},
|
|
858
|
+
allX = function (x) {
|
|
859
|
+
const
|
|
860
|
+
{threshold, worldCamera} = this,
|
|
861
|
+
{viewport} = worldCamera;
|
|
862
|
+
|
|
863
|
+
if (Math.abs(viewport.x - x) > threshold) {
|
|
864
|
+
viewport.moveX(x);
|
|
865
|
+
return true;
|
|
866
|
+
}
|
|
867
|
+
return false;
|
|
868
|
+
},
|
|
869
|
+
allY = function (y) {
|
|
870
|
+
const
|
|
871
|
+
{threshold, worldCamera} = this,
|
|
872
|
+
{viewport} = worldCamera;
|
|
873
|
+
|
|
874
|
+
if (Math.abs(viewport.y - y) > threshold) {
|
|
875
|
+
viewport.moveY(y);
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
return false;
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
return function () {
|
|
882
|
+
const
|
|
883
|
+
{threshold, unbounded, worldCamera, worldDimensions} = this,
|
|
884
|
+
{viewport} = worldCamera,
|
|
885
|
+
{height, width} = worldDimensions;
|
|
886
|
+
|
|
887
|
+
if (unbounded || !width) {
|
|
888
|
+
this.moveX = allX;
|
|
889
|
+
} else if (width < viewport.width) {
|
|
890
|
+
this.moveX = centerX;
|
|
891
|
+
} else {
|
|
892
|
+
this.moveX = containX;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (unbounded || !height) {
|
|
896
|
+
this.moveY = allY;
|
|
897
|
+
} else if (height < viewport.height) {
|
|
898
|
+
this.moveY = centerY;
|
|
899
|
+
} else {
|
|
900
|
+
this.moveY = containY;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// Make sure camera is correctly contained:
|
|
904
|
+
this.threshold = -1; // forces update
|
|
905
|
+
this.moveX(viewport.x);
|
|
906
|
+
this.moveY(viewport.y);
|
|
907
|
+
this.threshold = threshold;
|
|
908
|
+
};
|
|
909
|
+
}()),
|
|
910
|
+
|
|
911
|
+
updateViewport: function () {
|
|
912
|
+
const
|
|
913
|
+
{container, owner, stationary, viewportUpdate, windowPerWorldUnitHeight, windowPerWorldUnitWidth, world, worldCamera} = this,
|
|
914
|
+
{viewport} = worldCamera,
|
|
915
|
+
msg = Data.setUp(
|
|
916
|
+
"viewport", AABB.setUp(viewport),
|
|
917
|
+
"scaleX", windowPerWorldUnitWidth,
|
|
918
|
+
"scaleY", windowPerWorldUnitHeight,
|
|
919
|
+
"orientation", worldCamera.orientation,
|
|
920
|
+
"stationary", !viewportUpdate && !stationary,
|
|
921
|
+
"world", this.worldDimensions
|
|
922
|
+
);
|
|
923
|
+
|
|
924
|
+
if (viewportUpdate) {
|
|
925
|
+
this.viewportUpdate = false;
|
|
926
|
+
this.stationary = false;
|
|
927
|
+
|
|
928
|
+
// Transform the world to appear within camera
|
|
929
|
+
world.x = -viewport.x;
|
|
930
|
+
world.y = -viewport.y;
|
|
931
|
+
container.x = viewport.halfWidth * msg.scaleX;
|
|
932
|
+
container.y = viewport.halfHeight * msg.scaleY;
|
|
933
|
+
container.scale.x = msg.scaleX;
|
|
934
|
+
container.scale.y = msg.scaleY;
|
|
935
|
+
container.rotation = msg.orientation;
|
|
936
|
+
container.visible = true;
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* This component fires "camera-update" when the position of the camera in the world has changed. This event is triggered on both the entity (typically a layer) as well as children of the entity.
|
|
940
|
+
*
|
|
941
|
+
* @event platypus.Entity#camera-update
|
|
942
|
+
* @param message {Object}
|
|
943
|
+
* @param message.world {platypus.AABB} The dimensions of the world map.
|
|
944
|
+
* @param message.orientation {Number} Number describing the orientation of the camera.
|
|
945
|
+
* @param message.scaleX {Number} Number of window pixels that comprise a single world coordinate on the x-axis.
|
|
946
|
+
* @param message.scaleY {Number} Number of window pixels that comprise a single world coordinate on the y-axis.
|
|
947
|
+
* @param message.viewport {platypus.AABB} An AABB describing the world viewport area.
|
|
948
|
+
* @param message.stationary {Boolean} Whether the camera is moving.
|
|
949
|
+
**/
|
|
950
|
+
owner.triggerEvent('camera-update', msg);
|
|
951
|
+
if (owner.triggerEventOnChildren) {
|
|
952
|
+
owner.triggerEventOnChildren('camera-update', msg);
|
|
953
|
+
}
|
|
954
|
+
} else if (!stationary) {
|
|
955
|
+
this.stationary = true;
|
|
956
|
+
|
|
957
|
+
owner.triggerEvent('camera-update', msg);
|
|
958
|
+
if (owner.triggerEventOnChildren) {
|
|
959
|
+
owner.triggerEventOnChildren('camera-update', msg);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
msg.viewport.recycle();
|
|
964
|
+
msg.recycle();
|
|
965
|
+
},
|
|
966
|
+
|
|
967
|
+
destroy: function () {
|
|
968
|
+
this.parentContainer.removeChild(this.container);
|
|
969
|
+
this.parentContainer = null;
|
|
970
|
+
this.container = null;
|
|
971
|
+
if (this.mouseVector) {
|
|
972
|
+
this.mouseVector.recycle();
|
|
973
|
+
this.mouseWorldOrigin.recycle();
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
this.viewport.recycle();
|
|
977
|
+
this.worldCamera.viewport.recycle();
|
|
978
|
+
this.worldCamera.recycle();
|
|
979
|
+
this.worldDimensions.recycle();
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
|
|
983
|
+
publicMethods: {
|
|
984
|
+
/**
|
|
985
|
+
* Returns whether a particular display object intersects the camera's viewport on the canvas.
|
|
986
|
+
*
|
|
987
|
+
* @method platypus.components.Camera#isOnCanvas
|
|
988
|
+
* @param bounds {PIXI.Rectangle|Object} The bounds of the display object.
|
|
989
|
+
* @param bounds.height {Number} The height of the display object.
|
|
990
|
+
* @param bounds.width {Number} The width of the display object.
|
|
991
|
+
* @param bounds.x {Number} The left edge of the display object.
|
|
992
|
+
* @param bounds.y {Number} The top edge of the display object.
|
|
993
|
+
* @return {Boolean} Whether the display object intersects the camera's bounds.
|
|
994
|
+
*/
|
|
995
|
+
isOnCanvas: function (bounds) {
|
|
996
|
+
const
|
|
997
|
+
{canvas} = this;
|
|
998
|
+
|
|
999
|
+
return !bounds || !((bounds.x + bounds.width < 0) || (bounds.x > canvas.width) || (bounds.y + bounds.height < 0) || (bounds.y > canvas.height));
|
|
1000
|
+
},
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Returns a world coordinate corresponding to a provided window coordinate.
|
|
1004
|
+
*
|
|
1005
|
+
* @method platypus.components.Camera#windowToWorld
|
|
1006
|
+
* @param windowVector {platypus.Vector} A vector describing a window position.
|
|
1007
|
+
* @param withOffset {Boolean} Whether to provide a world position relative to the camera's location.
|
|
1008
|
+
* @param vector {platypus.Vector} If provided, this is used as the return vector.
|
|
1009
|
+
* @return {platypus.Vector} A vector describing a world position.
|
|
1010
|
+
*/
|
|
1011
|
+
windowToWorld: function (windowVector, withOffset, vector) {
|
|
1012
|
+
const
|
|
1013
|
+
{worldPerWindowUnitHeight, worldPerWindowUnitWidth, viewport, worldCamera: {viewport: wcViewport}} = this,
|
|
1014
|
+
worldVector = vector || Vector.setUp();
|
|
1015
|
+
|
|
1016
|
+
worldVector.x = windowVector.x * worldPerWindowUnitWidth;
|
|
1017
|
+
worldVector.y = windowVector.y * worldPerWindowUnitHeight;
|
|
1018
|
+
|
|
1019
|
+
if (withOffset !== false) {
|
|
1020
|
+
worldVector.x -= viewport.left * worldPerWindowUnitWidth - wcViewport.left;
|
|
1021
|
+
worldVector.y -= viewport.top * worldPerWindowUnitHeight - wcViewport.top;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
return worldVector;
|
|
1025
|
+
},
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Returns a window coordinate corresponding to a provided world coordinate.
|
|
1029
|
+
*
|
|
1030
|
+
* @method platypus.components.Camera#worldToWindow
|
|
1031
|
+
* @param worldVector {platypus.Vector} A vector describing a world position.
|
|
1032
|
+
* @param withOffset {Boolean} Whether to provide a window position relative to the camera's location.
|
|
1033
|
+
* @param vector {platypus.Vector} If provided, this is used as the return vector.
|
|
1034
|
+
* @return {platypus.Vector} A vector describing a window position.
|
|
1035
|
+
*/
|
|
1036
|
+
worldToWindow: function (worldVector, withOffset, vector) {
|
|
1037
|
+
const
|
|
1038
|
+
{windowPerWorldUnitHeight, windowPerWorldUnitWidth, viewport, worldCamera: {viewport: wcViewport}} = this,
|
|
1039
|
+
windowVector = vector || Vector.setUp();
|
|
1040
|
+
|
|
1041
|
+
windowVector.x = worldVector.x * windowPerWorldUnitWidth;
|
|
1042
|
+
windowVector.y = worldVector.y * windowPerWorldUnitHeight;
|
|
1043
|
+
|
|
1044
|
+
if (withOffset !== false) {
|
|
1045
|
+
windowVector.x -= wcViewport.left * windowPerWorldUnitWidth - viewport.left;
|
|
1046
|
+
windowVector.y -= wcViewport.top * windowPerWorldUnitHeight - viewport.top;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
return windowVector;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
});
|