Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
FogNetwork
GitHub Repository: FogNetwork/Tsunami
Path: blob/main/public/games/files/algaes-escapade/js/lib/camera.js
1036 views
1
/**
2
* Represents the a camera, which is capable of panning around the world
3
*
4
* @author David North
5
*/
6
function camera( world )
7
{
8
//The maximum speed that the camera can move at
9
const MAX_VELOCITY = 300;
10
11
/**
12
* @var world The world that is being looked at
13
*/
14
var _world = world;
15
16
/**
17
* @var boolean|gamejs.Rect Whether or not the camera is currently
18
* tracking an object. This variable contains the object being tracked if so.
19
*/
20
var _track = false;
21
22
/**
23
* @var boolean Whether or not the camera is animating to position
24
*/
25
var _animating = false;
26
27
/**
28
* @var gamejs.Rect The size and position of the viewport
29
* (what the player can see)
30
*/
31
var _viewport = new gamejs.Rect([0, 0], [0, 0]);
32
33
/**
34
* Sets the width of the viewport
35
*
36
* @param float width
37
*
38
* @return camera
39
*/
40
this.setWidth = function( width ){
41
if ( typeof(width) != 'number' )
42
{
43
throw 'Width must be a number';
44
}
45
46
_viewport.width = width;
47
return this;
48
}
49
50
/**
51
* Sets the height of the viewport
52
*
53
* @param float height
54
*
55
* @return camera
56
*/
57
this.setHeight = function( height ){
58
if ( typeof(height) != 'number' )
59
{
60
throw 'Height must be a number';
61
}
62
63
_viewport.height = height;
64
return this;
65
}
66
67
/**
68
* Sets the new position of the camera. Sanatises the position so that it
69
* never looks outside of the world
70
*
71
* @param float x
72
* @param float y
73
*
74
* @return camera
75
*/
76
this.setPosition = function( x, y ){
77
if ( typeof(x) != 'number' )
78
{
79
throw 'X position must be a number';
80
}
81
82
if ( typeof(y) != 'number' )
83
{
84
throw 'Y position must be a number';
85
}
86
87
//Get the last position that the camera was at before being moved
88
var oldX = _viewport.x;
89
var oldY = _viewport.y;
90
91
//Sanitise the position and set the camera
92
var newPosition = _getSanatisedPosition(x, y);
93
_viewport.x = newPosition.x;
94
_viewport.y = newPosition.y;
95
96
//Update the objects in the world (i.e. shift them, the number of pixels
97
//the camera has 'moved', giving the impression of movement)
98
_updateObjects((_viewport.x - oldX), (_viewport.y - oldY));
99
100
return this;
101
}
102
103
/**
104
* Whether or not the camera is currently animating towards an object
105
*
106
* @return boolean
107
*/
108
this.isAnimating = function(){
109
return _animating;
110
}
111
112
/**
113
* Focuses the camera on a rectangle, with the option to track it
114
* continuously and to animate the movement
115
*
116
* @param gamejs.Rect rect The rectangle to focus on
117
* @param boolean track Optional. Whether to track the object continuously
118
* @param boolean animate Optional. Whether to animate the camera or not
119
*
120
* @return camera
121
*/
122
this.focusOn = function( rect, track, animate ){
123
//Mmmmm.. type hinting
124
if ( !(rect instanceof gamejs.Rect) )
125
{
126
throw 'Rectangle must be an instance of gamejs.Rect';
127
}
128
129
//If track is not defined, then set the default value to false.
130
//Otherwise, ensure that it's a boolean
131
if (typeof(track) === "undefined")
132
{
133
track = false;
134
}
135
else if ( typeof(track) !== 'boolean' )
136
{
137
throw 'Optional track flag must be a boolean';
138
}
139
140
//If animate is not defined, then set the default value to false.
141
//Otherwise, ensure that it's a boolean
142
if (typeof(animate) === "undefined")
143
{
144
animate = false;
145
}
146
else if ( typeof(animate) !== 'boolean' )
147
{
148
throw 'Optional animate flag must be a boolean';
149
}
150
151
//The new Camera position should have the middle of the camera pointing
152
//at the middle of the rectangle
153
var newCameraX = rect.center[0] - (_viewport.width / 2);
154
var newCameraY = rect.center[1] - (_viewport.height / 2);
155
156
//If we are contantly tracking this object, then set that here
157
if ( track )
158
{
159
_track = rect;
160
}
161
else
162
{
163
_track = false;
164
}
165
166
//If we are animating, don't move the camera (let the update method
167
//do it), otherwise set the position now
168
if ( animate )
169
{
170
_animating = true;
171
}
172
else
173
{
174
this.setPosition( newCameraX, newCameraY );
175
}
176
177
return this;
178
}
179
180
/**
181
* Updates the camera position if it is tracking and also moves the camera
182
* animation if animating
183
*
184
* @param int msDuration
185
*/
186
this.update = function( msDuration ){
187
if ( _track )
188
{
189
//Get the new X and Y c-ordinates, so that the camera is focused
190
//on the middle of the object
191
var destinationX = _track.center[0] - (_viewport.width / 2);
192
var destinationY = _track.center[1] - (_viewport.height / 2);
193
194
destinationX += _viewport.x
195
destinationY += _viewport.y
196
197
//If the tracking is animated then get the next frame, before
198
//setting the new position to that instead
199
if ( _animating )
200
{
201
var pos = _getNextAnimatedPosition(
202
destinationX, destinationY, msDuration
203
);
204
205
destinationX = pos.x;
206
destinationY = pos.y;
207
}
208
209
this.setPosition( destinationX, destinationY );
210
}
211
}
212
213
/**
214
* Ensures that the camera is not intersecting the level (i.e. going over
215
* the bounding box). This is so that the camera is always focused on
216
* objects inside the level, not outside it
217
*
218
* @param float x The proposed X position
219
* @param float y The proposed Y position
220
*
221
* @return object An object containing the x and y position
222
*/
223
var _getSanatisedPosition = function( x, y ){
224
var position = { 'x': x, 'y': y };
225
var level = _world.getBoundingRect();
226
227
//Set up the collision test object (essentially a copy of the cameras
228
//viewport, with the new x and y co-ordinates)
229
var collideTest = new gamejs.Rect(
230
[x, y], [_viewport.width, _viewport.height]
231
);
232
233
//Set up the edges of trhe level to test collisions on
234
var rightEdge = [
235
[level.right, level.top],
236
[level.right, level.bottom]
237
];
238
239
var leftEdge = [
240
[level.left, level.top],
241
[level.left, level.bottom]
242
];
243
244
var topEdge = [
245
[level.left, level.top],
246
[level.right, level.top]
247
];
248
249
var bottomEdge = [
250
[level.left, level.bottom],
251
[level.right, level.bottom]
252
];
253
254
//Test the left and right edges, setting as appropriate
255
if ( collideTest.collideLine(rightEdge[0], rightEdge[1]) )
256
{
257
position['x'] = level.right - collideTest.width;
258
}
259
else if ( collideTest.collideLine(leftEdge[0], leftEdge[1]) )
260
{
261
position['x'] = level.left;
262
}
263
264
//Test the top and bottom edges, setting as appropriate
265
if ( collideTest.collideLine( topEdge[0], topEdge[1]) )
266
{
267
position['y'] = level.top;
268
}
269
else if ( collideTest.collideLine(bottomEdge[0], bottomEdge[1]) )
270
{
271
position['y'] = (level.bottom - collideTest.height);
272
}
273
274
return position;
275
}
276
277
/**
278
* Gets the next frame for the camera animation
279
*
280
* @param float destinationX The target destination X position
281
* @param float destinationY Tha target destination Y position
282
* @param int msDuration The amount of time that has passed since the
283
* last frame
284
*
285
* @return object An object containing the new X and Y
286
*/
287
var _getNextAnimatedPosition = function(
288
destinationX, destinationY, msDuration
289
){
290
//Make sure that the new destination is not outside the world
291
var sanePosition = _getSanatisedPosition(destinationX, destinationY);
292
293
var position = { 'x': sanePosition.x, 'y': sanePosition.y };
294
295
var targetX = position.x;
296
var targetY = position.y;
297
var deltaX = _viewport.x - targetX;
298
var deltaY = _viewport.y - targetY;
299
var velocityX = velocityY = MAX_VELOCITY;
300
301
//If the delta Y is zero, then the velocity is zero as the camera is
302
//not moving anywhere along the Y axis
303
if ( 0 === deltaY )
304
{
305
velocityY = 0;
306
}
307
308
//If the delta X is zero, then the velocity is zero as the camera is
309
//not moving anywhere along the X axis
310
if ( 0 === deltaX )
311
{
312
velocityX = 0;
313
}
314
315
//Find out if the difference on the X or Y axis is bigger and slow
316
//down the smaller of the two. This gives a nice diagonal effect so
317
//that the camera doesn't look like it's panning around trying to find
318
//the object
319
if ( Math.abs(deltaX) > Math.abs(deltaY) )
320
{
321
if ( 0 != deltaY )
322
{
323
velocityY *= (deltaX / deltaY);
324
}
325
}
326
else if ( Math.abs(deltaX) < Math.abs(deltaY) )
327
{
328
if ( 0 != deltaX )
329
{
330
velocityX *= (deltaX / deltaY);
331
}
332
}
333
334
//A small delta X means that the camera needs to move to the right, and
335
//a large delta means to move it to the left
336
if ( deltaX < 0 )
337
{
338
position.x = _viewport.x + ( velocityX * (msDuration / 1000) );
339
}
340
else
341
{
342
position.x = _viewport.x - ( velocityX * (msDuration / 1000) );
343
}
344
345
//A small delta X means that the camera needs to move down, and
346
//a large delta means to move it up
347
if ( deltaY < 0 )
348
{
349
position.y = _viewport.y + ( velocityY * (msDuration / 1000) );
350
}
351
else if ( deltaY < 0 )
352
{
353
position.y = _viewport.y - ( velocityY * (msDuration / 1000) );
354
}
355
356
357
//Check to see if the camera is close to the target. If it is, then move
358
//it to the target so that on the next frame it doesn't overshoot
359
if ( position.x <= (targetX + 5) && position.x >= (targetX - 5) )
360
{
361
position.x = targetX;
362
}
363
364
if ( position.y <= (targetY + 5) && position.y >= (targetY - 5) )
365
{
366
position.y = targetY;
367
}
368
369
//If the camera has reached it's target, then stop it animating
370
if ( targetX == position.x && targetY == position.y )
371
{
372
_animating = false;
373
}
374
375
return position;
376
}
377
378
/**
379
* Updates all objects with their new position. This gives the illusion that
380
* the camera has moved, when in reality it's all the objects that
381
* have moved
382
*
383
* @param float distanceX The distance the camera has travelled on the X
384
* @param float distanceY The distance the camera has travelled on the Y
385
*/
386
var _updateObjects = function( distanceX, distanceY ){
387
var objects = _world.getObjects();
388
for ( var i = 0; i < objects.length; i++)
389
{
390
objects[i].forEach(function(obj){
391
obj.rect.x -= distanceX;
392
obj.rect.y -= distanceY;
393
});
394
}
395
}
396
}
397