Skip to content

Commit 1a51e85

Browse files
committed
New Camera Shake effect class
1 parent a56465f commit 1a51e85

1 file changed

Lines changed: 321 additions & 0 deletions

File tree

src/cameras/2d/effects/Shake.js

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
/**
2+
* @author Richard Davey <rich@photonstorm.com>
3+
* @copyright 2018 Photon Storm Ltd.
4+
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
5+
*/
6+
7+
var Clamp = require('../../../math/Clamp');
8+
var Class = require('../../../utils/Class');
9+
10+
/**
11+
* @classdesc
12+
* A Camera Shake effect.
13+
*
14+
* This effect will shake the camera viewport by a random amount, bounded by the specified intensity, each frame.
15+
*
16+
* Only the camera viewport is moved. None of the objects it is displaying are impacted, i.e. their positions do
17+
* not change.
18+
*
19+
* The effect will dispatch several events on the Camera itself and you can also specify an `onUpdate` callback,
20+
* which is invoked each frame for the duration of the effect if required.
21+
*
22+
* @class Shake
23+
* @memberOf Phaser.Cameras.Scene2D.Effects
24+
* @constructor
25+
* @since 3.5.0
26+
*
27+
* @param {Phaser.Cameras.Scene2D.Camera} camera - The camera this effect is acting upon.
28+
*/
29+
var Shake = new Class({
30+
31+
initialize:
32+
33+
function Shake (camera)
34+
{
35+
/**
36+
* The Camera this effect belongs to.
37+
*
38+
* @name Phaser.Cameras.Scene2D.Effects.Shake#camera
39+
* @type {Phaser.Cameras.Scene2D.Camera}
40+
* @readOnly
41+
* @since 3.5.0
42+
*/
43+
this.camera = camera;
44+
45+
/**
46+
* Is this effect actively running?
47+
*
48+
* @name Phaser.Cameras.Scene2D.Effects.Shake#isRunning
49+
* @type {boolean}
50+
* @readOnly
51+
* @default false
52+
* @since 3.5.0
53+
*/
54+
this.isRunning = false;
55+
56+
/**
57+
* The duration of the effect, in milliseconds.
58+
*
59+
* @name Phaser.Cameras.Scene2D.Effects.Shake#duration
60+
* @type {integer}
61+
* @readOnly
62+
* @default 0
63+
* @since 3.5.0
64+
*/
65+
this.duration = 0;
66+
67+
/**
68+
* The intensity of the effect. Use small float values.
69+
* The default when the effect starts is 0.05.
70+
* You can modify this value while the effect is active to create
71+
* more varied shake effects.
72+
*
73+
* @name Phaser.Cameras.Scene2D.Effects.Shake#intensity
74+
* @type {float}
75+
* @default 0
76+
* @since 3.5.0
77+
*/
78+
this.intensity = 0;
79+
80+
/**
81+
* If this effect is running this holds the current percentage of the progress, a value between 0 and 1.
82+
*
83+
* @name Phaser.Cameras.Scene2D.Effects.Shake#progress
84+
* @type {float}
85+
* @since 3.5.0
86+
*/
87+
this.progress = 0;
88+
89+
/**
90+
* Effect elapsed timer.
91+
*
92+
* @name Phaser.Cameras.Scene2D.Effects.Shake#_elapsed
93+
* @type {number}
94+
* @private
95+
* @since 3.5.0
96+
*/
97+
this._elapsed = 0;
98+
99+
/**
100+
* How much to offset the camera by horizontally.
101+
*
102+
* @name Phaser.Cameras.Scene2D.Effects.Shake#_offsetX
103+
* @type {number}
104+
* @private
105+
* @default 0
106+
* @since 3.0.0
107+
*/
108+
this._offsetX = 0;
109+
110+
/**
111+
* How much to offset the camera by vertically.
112+
*
113+
* @name Phaser.Cameras.Scene2D.Effects.Shake#_offsetY
114+
* @type {number}
115+
* @private
116+
* @default 0
117+
* @since 3.0.0
118+
*/
119+
this._offsetY = 0;
120+
121+
/**
122+
* This callback is invoked every frame for the duration of the effect.
123+
*
124+
* @name Phaser.Cameras.Scene2D.Effects.Shake#_onUpdate
125+
* @type {?Camera2DCallback}
126+
* @private
127+
* @default null
128+
* @since 3.5.0
129+
*/
130+
this._onUpdate;
131+
132+
/**
133+
* On Complete callback scope.
134+
*
135+
* @name Phaser.Cameras.Scene2D.Effects.Shake#_onUpdateScope
136+
* @type {any}
137+
* @private
138+
* @since 3.5.0
139+
*/
140+
this._onUpdateScope;
141+
},
142+
143+
/**
144+
* This event is fired when the shake effect begins to run on a camera.
145+
*
146+
* @event CameraShakeStartEvent
147+
* @param {Phaser.Cameras.Scene2D.Camera} camera - The camera that the effect began on.
148+
* @param {Phaser.Cameras.Scene2D.Effects.Shake} effect - A reference to the effect instance.
149+
* @param {integer} duration - The duration of the effect.
150+
* @param {float} intensity - The intensity of the effect.
151+
*/
152+
153+
/**
154+
* This event is fired when the shake effect completes.
155+
*
156+
* @event CameraShakeCompleteEvent
157+
* @param {Phaser.Cameras.Scene2D.Camera} camera - The camera that the effect began on.
158+
* @param {Phaser.Cameras.Scene2D.Effects.Shake} effect - A reference to the effect instance.
159+
*/
160+
161+
/**
162+
* Shakes the Camera by the given intensity over the duration specified.
163+
*
164+
* @method Phaser.Cameras.Scene2D.Effects.Shake#start
165+
* @fires CameraShakeStartEvent
166+
* @fires CameraShakeCompleteEvent
167+
* @since 3.5.0
168+
*
169+
* @param {number} duration - The duration of the effect in milliseconds.
170+
* @param {number} [intensity=0.05] - The intensity of the shake.
171+
* @param {boolean} [force=false] - Force the shake effect to start immediately, even if already running.
172+
* @param {function} [callback] - This callback will be invoked every frame for the duration of the effect.
173+
* It is sent two arguments: A reference to the camera and a progress amount between 0 and 1 indicating how complete the effect is.
174+
* @param {any} [context] - The context in which the callback is invoked. Defaults to the Scene to which the Camera belongs.
175+
*
176+
* @return {Phaser.Cameras.Scene2D.Camera} The Camera on which the effect was started.
177+
*/
178+
start: function (duration, intensity, force, callback, context)
179+
{
180+
if (!duration) { duration = Number.MIN_VALUE; }
181+
if (intensity === undefined) { intensity = 0.05; }
182+
if (force === undefined) { force = false; }
183+
if (callback === undefined) { callback = null; }
184+
if (context === undefined) { context = this.camera.scene; }
185+
186+
if (!force && this.isRunning)
187+
{
188+
return this.camera;
189+
}
190+
191+
this.isRunning = true;
192+
this.duration = duration;
193+
this.intensity = intensity;
194+
this.progress = 0;
195+
196+
this._elapsed = 0;
197+
this._offsetX = 0;
198+
this._offsetY = 0;
199+
200+
this._onUpdate = callback;
201+
this._onUpdateScope = context;
202+
203+
this.camera.emit('camerashakestart', this.camera, this, duration, intensity);
204+
205+
return this.camera;
206+
},
207+
208+
/**
209+
* The pre-render step for this effect. Called automatically by the Camera.
210+
*
211+
* @method Phaser.Cameras.Scene2D.Effects.Shake#preRender
212+
* @since 3.5.0
213+
*/
214+
preRender: function ()
215+
{
216+
if (this.isRunning)
217+
{
218+
this.camera.matrix.translate(this._offsetX, this._offsetY);
219+
}
220+
},
221+
222+
/**
223+
* The main update loop for this effect. Called automatically by the Camera.
224+
*
225+
* @method Phaser.Cameras.Scene2D.Effects.Shake#update
226+
* @since 3.5.0
227+
*
228+
* @param {integer} time - The current timestamp as generated by the Request Animation Frame or SetTimeout.
229+
* @param {number} delta - The delta time, in ms, elapsed since the last frame.
230+
*/
231+
update: function (time, delta)
232+
{
233+
if (!this.isRunning)
234+
{
235+
return;
236+
}
237+
238+
this._elapsed += delta;
239+
240+
this.progress = Clamp(this._elapsed / this.duration, 0, 1);
241+
242+
if (this._onUpdate)
243+
{
244+
this._onUpdate.call(this._onUpdateScope, this.camera, this.progress);
245+
}
246+
247+
if (this._elapsed < this.duration)
248+
{
249+
var intensity = this.intensity;
250+
var width = this.camera.width;
251+
var height = this.camera.height;
252+
var zoom = this.camera.zoom;
253+
254+
this._offsetX = (Math.random() * intensity * width * 2 - intensity * width) * zoom;
255+
this._offsetY = (Math.random() * intensity * height * 2 - intensity * height) * zoom;
256+
257+
if (this.camera.roundPixels)
258+
{
259+
this._offsetX |= 0;
260+
this._offsetY |= 0;
261+
}
262+
}
263+
else
264+
{
265+
this.effectComplete();
266+
}
267+
},
268+
269+
/**
270+
* Called internally when the effect completes.
271+
*
272+
* @method Phaser.Cameras.Scene2D.Effects.Shake#effectComplete
273+
* @since 3.5.0
274+
*/
275+
effectComplete: function ()
276+
{
277+
this._offsetX = 0;
278+
this._offsetY = 0;
279+
280+
this._onUpdate = null;
281+
this._onUpdateScope = null;
282+
283+
this.isRunning = false;
284+
285+
this.camera.emit('camerashakecomplete', this, this.camera);
286+
},
287+
288+
/**
289+
* Resets this camera effect.
290+
* If it was previously running, it stops instantly without calling its onComplete callback or emitting an event.
291+
*
292+
* @method Phaser.Cameras.Scene2D.Effects.Shake#reset
293+
* @since 3.5.0
294+
*/
295+
reset: function ()
296+
{
297+
this.isRunning = false;
298+
299+
this._offsetX = 0;
300+
this._offsetY = 0;
301+
302+
this._onUpdate = null;
303+
this._onUpdateScope = null;
304+
},
305+
306+
/**
307+
* Destroys this effect, releasing it from the Camera.
308+
*
309+
* @method Phaser.Cameras.Scene2D.Effects.Shake#destroy
310+
* @since 3.5.0
311+
*/
312+
destroy: function ()
313+
{
314+
this.reset();
315+
316+
this.camera = null;
317+
}
318+
319+
});
320+
321+
module.exports = Shake;

0 commit comments

Comments
 (0)