-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.ts
4921 lines (4598 loc) · 193 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
const Matter = require("matter-js");
const { createCanvas } = require("canvas")
const GPU = require("gpu.js");
// Options interfaces
/**
* @interface TextOptions
* @property {string} text - The text to render
* @property {Point} coordinates - The coordinates of the text
* @property {string} font - The font of the text, eg: "Arial", "Times New Roman", etc.
* @property {number} fontSize - The font size of the text, in pixels
* @property {string} color - The color of the text, in hex or rgb format
* @property {string} type - The type of the object, "text"
*/
interface TextOptions extends GameObjectOptions {
text: string;
coordinates: Point;
font: string;
color: string;
fontSize: number;
}
/**
* Used for configuring properties of a SoundEmitter game object
*
* @interface SoundEmitterOptions
* @extends {SoundOptions}
* @property {(distance: number) => number} [fallOffFunction] - A function that takes the distance from the listener and returns the volume of the sound. Default is a linear fall off function
* @property {number} [maxDistance=1000] - The maximum distance the sound can be heard from (if outside this range, the volume of the sound will be 0)
* @property {number} [minDistance=0] - The minimum distance the sound can be heard from (if inside this range, the volume of the sound will be 1)
* @property {boolean} [startPlaying=true] - Set to false to prevent the sound from playing when the scene starts (default is true)
* @property {GameObject} listener - The game object that will listen to the sound- eg: the player. Distance from the source is calculated from this object
*/
interface SoundEmitterOptions extends SoundOptions {
fallOffFunction?: (distance: number) => number;
maxDistance?: number;
minDistance?: number;
startPlaying?: boolean;
listener: GameObject;
}
/**
* For configuring Sounds in a scene
*
* @interface SoundOptions
* @property {string} source - The source of the sound (eg: "sound.mp3")
* @property {number} [volume=1] - The volume of the sound, default is 1
* @property {boolean} [loop=false] - Set to true to loop the sound, default is false
* @property {number} [playbackRate=1] - The playback rate of the sound, default is 1
*/
interface SoundOptions {
source: string;
volume?: number;
loop?: boolean;
playbackRate?: number;
}
/**
* For configuring the properties of a Layer in a scene
*
* @interface LayerOptions
* @property {boolean} [physics=false] - Set to true to enable physics, default is false
* @property {any} physicsOptions - Options passed to matter.js engine
* @property {Array<GameObject>} objects - Array of GameObjects in the layer
* @property {Array<number>} bounds - Set the bounds of the layer, by default bounds are not enabled
* @property {boolean} [boundsActive=false] - Set to true to enable bounds, default is false
* @property {Vec2} [parallax=[1,1]] - Set the parallax of the layer, by default parallax is [1, 1]
*/
interface LayerOptions {
physics?: boolean;
physicsOptions?: any;
objects?: Array<GameObject>;
boundsActive?: boolean;
bounds?: Array<number>;
parallax?: Vec2;
}
/**
* Returned from amplifyMedia
*
* @interface AmplifiedMedia
* @property {AudioContext} context - An instance of AudioContext which was used to change the media’s volume.
* @property {MediaElementAudioSourceNode} source - A media source created from the AudioContext instance and the mediaElem. This may be useful if you desire to do more with the web audio API regarding this media. NOTE: Only one source node can be created from an element.
* @property {GainNode} gain - A media gain created from the AudioContext instance and the mediaElem. This may be useful if you desire to do more with the web audio API regarding this media.
* @property {HTMLMediaElement} media - A reference to the mediaElem passed into the function.
* @property {Function} getAmpLevel - A function which returns the multiplier (amplification level).
* @property {Function} amplify - A function which takes a multiplier (amplification level) and sets the media’s volume to that level.
*/
interface AmplifiedMedia {
context: AudioContext;
source: MediaElementAudioSourceNode;
gain: GainNode;
media: HTMLMediaElement;
amplify: (multiplier: number) => void;
getAmpLevel: () => number;
}
/**
* Stack of data to be emitted when the socket is ready
*
* @interface ReadyStackEmitItem
* @property {string} type - The type item "on" or "emit"
* @property {Array<any>} args - The arguments to pass to the stack. For "on" it is [event, callback], for "emit" it is [event, data]
*/
interface NotReadyStackEmitItem {
type: string;
args: Array<any>;
}
interface CollisionMonitorOptions {
crossLayers?: boolean;
}
/**
* Options to configure PlayerClient
*
* @interface PlayerClientOptions
* @property {HTMLCanvasElement} canvas - The canvas to render to
* @property {number} [width=canvas.width] - Width of canvas (this will overwrite the canvas width)
* @property {number} [height=canvas.height] - Height of canvas (this will overwrite the canvas height)
* @property {Function} modifyLocalObject - Function to modify the local GameObject (the player, eg. change label, color, etc.)
* @property {SceneOptions} [sceneOptions={}] - Options for the scene
* @example
* ```js
* const options: PlayerClientOptions = {
* canvas: document.getElementById("canvas"),
* width: 500,
* height: 500,
* modifyLocalObject: (gameObject) => {
* gameObject.label = "You";
* gameObject.backgroundColor = "red";
* }
* }
*
* const playerClient = new PlayerClient(options);
* ```
*/
interface PlayerClientOptions {
canvas: HTMLCanvasElement;
width?: number;
height?: number;
modifyLocalObject: (gameObject: GameObject) => void;
sceneOptions?: SceneOptions;
}
/**
* For configuring basic properties of a GameObject
*
* @interface GameObjectOptions
* @property {boolean} [physicsEnabled=false] - Set to true to enable physics, default is false
* @property {PhysicsOptions} [physicsOptions={}] - Options passed to matter.js engine
* @property {string} [id] - A unique id for the GameObject, generated automatically by default
* @property {Vec2} [bounds] - Set the bounds of the GameObject, by default bounds are not enabled
* @property {boolean} [boundsActive=false] - Set to true to enable bounds, default is false
* @property {boolean} [square=false] - Set to true to make the GameObject a square, default is false
* @property {Vec2} [hitbox] - Set the hitbox of the GameObject, by default hitbox is not enabled
* @property {Array<Point>} [points] - Set the points of the GameObject, by default points are not enabled
* @property {Vec2} [coordinates] - Set the coordinates of the GameObject, by default coordinates are [0, 0]
* @property {string} [type] - Set the type of the GameObject, by defualt is "gameObject", "polygon", or "sprite"
* @property {boolean} [convex=false] - Set to true to make the GameObject convex, default is false
* @property {string} [backgroundColor="white"] - CSS color, background color of the GameObject (only applies to polygons)
* @property {any} [_state] - Internal state of the GameObject, do not modify, but you can pass an initial state to the GameObject (use "_state": { ...gameObject, ...newProperties})
* @property {GameObjectOptions} [gameObjectOptions={}] - Options for the GameObject (applied to clones when cloned using GameObject.From())
* @property {any} [meta] - Any meta data you want to store in the GameObject (Do not overwrite "label", "player", or "id" if you are using a MultiPlayerSceneManager)
* @property {Array<GameObject>} [blocks] - Array of GameObjects that are blocked by this GameObject
* @example
* ```js
* const options: GameObjectOptions = {
* physicsEnabled: true,
* physicsOptions: {
* restitution: 0.5
* }
* ```
*/
interface GameObjectOptions {
physicsEnabled?: boolean; // set to true to enable physics, default is false
physicsOptions?: PhysicsOptions; // options passed to matter.js engine
id?: string;
bounds?: Vec2;
boundsActive?: boolean;
square?: boolean;
hitbox?: Vec2;
points?: Array<Point>;
coordinates?: Vec2;
type?: string;
convex?: boolean;
backgroundColor?: string;
_state?: any;
gameObjectOptions?: GameObjectOptions;
meta?: any;
blocks?: Array<GameObject>;
}
/**
* For configuring the gravity of a scene
*
* @interface Gravity
* @property {number} [scale=0.001] - The magnitude of the gravitational acceleration. Set to 0 if you want a scene with physics but no gravity, default is 0.001
* @example
* ```js
* const gravity: Gravity = {
* scale: 0.001
* x: 0,
* y: 1
* }
* ```
*/
interface Gravity extends Vector {
scale?: number; // The magnitude of the gravitational acceleration. Set to 0 if you want a scene with physics but no gravity, default is 0.001
}
/**
* For configuring the properties of a polygon
*
* @interface PolygonOptions
* @property {Point[]} points - A list of points that make up the polygon
* @property {string} backgroundColor - CSS color, background color of the polygon
* @example
* ```js
* const options: PolygonOptions = {
* points: [[0, 0], [0, 100], [100, 100], [100, 0]],
* backgroundColor: "red"
* }
* ```
*/
interface PolygonOptions extends GameObjectOptions {
points: Point[]; // A list of points that make up the polygon
backgroundColor: string; // CSS color, background color of the polygon
}
// For configuring the properties of a sprite
/**
* For configuring the properties of a sprite
*
* @interface SpriteOptions
* @property {string} url - Url of the image
* @property {Vec2} coordinates - Initial coordinates of the sprite
* @property {number} width - Width of the sprite
* @property {number} height - Height of the sprite
* @example
* ```js
* const options: SpriteOptions = {
* url: "https://i.imgur.com/0xq2Mgj.png",
* coordinates: [0, 0],
* width: 100,
* height: 100
* }
* ```
*/
interface SpriteOptions extends GameObjectOptions {
url: string; // url of the image
coordinates: Vec2; // initial coordinates of the sprite
width: number; // width of the sprite
height: number; // height of the sprite
}
// For configuring the properties of Scene Manager
/**
* For configuring the properties of Scene Manager
*
* @interface SceneManagerOptions
* @property {Scene} [initialScene] - The scene to start with
* @property {HTMLCanvasElement} canvas - The canvas to render to
* @property {number} width - Width of canvas (this will overwrite the canvas width)
* @property {number} height - Height of canvas (this will overwrite the canvas height)
* @property {boolean} [start=true] - Whether or not to start drawing the scene when the SceneManager is created
* @property {boolean} [autoDraw=true] - Start drawing over and over automatically if start is true or if the draw method was called.
* @example
* ```js
* const options: SceneManagerOptions = {
* initialScene: scene,
* canvas: document.getElementById("canvas"),
* width: 500,
* height: 500
* }
* ```
*/
interface SceneManagerOptions {
initialScene?: Scene; // The scene to start with
canvas: HTMLCanvasElement; // The canvas to render to
width: number; // Width of canvas (this will overwrite the canvas width)
height: number; // Height of canvas (this will overwrite the canvas height),
start?: boolean; // Whether or not to start drawing the scene when the SceneManager is created. True by default
autoDraw?: boolean;
}
/**
* For configuring properties of a MultiPlayerSceneManager
*
* @interface MultiPlayerSceneManagerOptions
* @property {HTMLCanvasElement} [canvas] - Synthetic canvas to render to, if not provided a canvas will be created
* @property {number} [width=canvas.width] - Width of canvas (this will overwrite the canvas width on the client)
* @property {number} [height=canvas.height] - Height of canvas (this will overwrite the canvas height on the client)
* @property {Scene} initialScene - The scene to start with
* @property {boolean} [showPlayerLabels=true] - Whether or not to show player labels
*/
interface MultiPlayerSceneManagerOptions {
canvas?: HTMLCanvasElement;
width?: number;
height?: number;
initialScene: Scene;
showPlayerLabels?: boolean;
}
// For configuring the properties of a Scene
/**
* For configuring the properties of a Scene
*
* @interface SceneOptions
* @property {boolean} [fpsMonitoringEnabled=false] - Set to true to enable an FPS counter in top-left, default is false
* @property {boolean} [lighting=false] - True to enable lighting, default is false
* @property {lightingOptions} [lightOptions={}] - Options for lighting, default {} (empty object)
* @property {GPUsettings} [GPUsettings={}] - Settings for GPU.js, default {} (empty object)
* @property {boolean} [physics=false] - Set to true to enable physics (Uses matter.js under the hood), default is false. If true and no layers are specified, a default layer will be created with physics enabled. Othewise, physics is handled by each individual layer.
* @property {WorldPhysicsOptions} [physicsOptions={}] - Options passed to matter.js engine
* @property {Function} [update] - Function to run on every tick, default is an empty function
* @property {boolean} [clear=true] - Set to false to disable clearing the canvas on every tick, default is true
* @property {Vec2} [bounds] - Set the bounds of the scene, by default bounds are not enabled
* @property {number} [FPS_BUFFER_SIZE=60] - Size of the buffer used to calculate FPS, default is 60
* @property {GameObject} [bindCameraTo] - Bind the camera to a GameObject, by default camera is static
* @property {Array<Layer>} [layers] - Layers in the scene. Will be rendered in the order they are in the array
* @property {string} [backgroundColor="white"] - Background color of the scene
* @example
* ```js
* const options: SceneOptions = {
* fpsMonitoringEnabled: true,
* lighting: true,
* lightOptions: {
* fog: 1.3,
* ambient: 0.2
* },
* GPUsettings: {
* mode: "webgl"
* },
* physics: true,
* physicsOptions: {
* gravity: {
* x: 0,
* y: 1,
* scale: 0.001
* }
* },
* update: () => {
* // do something
* },
* clear: false,
* bounds: [500, 500],
* FPS_BUFFER_SIZE: 60,
* bindCameraTo: gameObject
* }
* ```
*/
interface SceneOptions {
fpsMonitoringEnabled?: boolean; // set to true to enable an FPS counter in top-left, default is false
lighting?: boolean; // true to enable lighting, default is false
lightOptions?: lightingOptions; // options for lighting, default {} (empty object)
GPUsettings?: GPUsettings; // settings for GPU.js, default {} (empty object)
physics?: boolean; // set to true to enable physics (Uses matter.js under the hood), default is false
physicsOptions?: WorldPhysicsOptions; // options passed to matter.js engine
update?: Function; // function to run on every tick, default is an empty function
clear?: boolean; // set to false to disable clearing the canvas on every tick, default is true
bounds?: Vec2; // set the bounds of the scene, by default bounds are not enabled
FPS_BUFFER_SIZE?: number; // size of the buffer used to calculate FPS, default is 60
bindCameraTo?: GameObject; // bind the camera to a GameObject, by default camera is static
layers?: Array<Layer>; // layers in the scene
backgroundColor?: string; // background color of the scene
}
// Configuring lights in a scene
/**
* For configuring lights in a scene
*
* @interface lightingOptions
* @property {number} [fog=1.3] - Changes how light spreads, default is 1.3
* @property {number} [ambient=0.2] - Ambient lighting of the scene, default is 0.2
* @example
* ```js
* const options: lightingOptions = {
* fog: 1.3,
* ambient: 0.2
* }
* ```
*/
interface lightingOptions {
fog?: number; // changes how light spreads, default is 1.3
ambient?: number; // ambient lighting of the scene, default is 0.2
}
// Settings passed to GPU.js
/**
* Settings passed to GPU.js
*
* @interface GPUsettings
* @property {string} [mode="webgl"] - Mode to run GPU.js in, default is "webgl"
* @example
* ```js
* const options: GPUsettings = {
* mode: "webgl"
* }
* ```
*/
interface GPUsettings {
mode?: "dev" | "webgl" | "webgl2" | "headlessgl" | "cpu"; // mode to run GPU.js in, default is "webgl"
}
/**
* For configuring the properties of a World (matter.js)
* Refer to matter.js documentation
*
* @interface WorldPhysicsOptions
* @property {number} [constraintIterations=2] - An integer Number that specifies the number of constraint iterations to perform each update. The higher the value, the higher quality the simulation will be at the expense of performance. The default value of 2 is usually very adequate.
* @property {any} [detector=Matter.Detector] - A Matter.Detector instance, default is Matter.Detector
* @property {boolean} [enableSleeping=false] - A flag that specifies whether the engine should allow sleeping via the Matter.Sleeping module. Sleeping can improve stability and performance, but often at the expense of accuracy. default is false
* @property {Gravity} [gravity={ x: 0, y: 1, scale: 0.001 }] - An optional gravitational acceleration applied to all bodies in engine.world on every update. default is { x: 0, y: 1, scale: 0.001 }
* @property {any} [plugin={}] - An object reserved for storing plugin-specific properties.
* @property {number} [positionIterations=6] - An integer Number that specifies the number of position iterations to perform each update. The higher the value, the higher quality the simulation will be at the expense of performance. The default value is 6
* @property {PhysicsTimingOptions} [timing={ lastDelta: 1000 / 60, lastElapsed: 1000 / 60, timeScale: 1, timestamp: 0 }] - An object that specifies the timing systems to use for engine updates and rendering. default is { lastDelta: 1000 / 60, lastElapsed: 1000 / 60, timeScale: 1, timestamp: 0 }
* @property {number} [velocityIterations=4] - An integer Number that specifies the number of velocity iterations to perform each update. The higher the value, the higher quality the simulation will be at the expense of performance.
* @property {Object} [world={}] - The root Matter.Composite instance that will contain all bodies, constraints and other composites to be simulated by this engine. default is a Matter.Composite with no bodies (Handled under the hood, do not worry about this)
*/
interface WorldPhysicsOptions {
constraintIterations?: number; // An integer Number that specifies the number of constraint iterations to perform each update. The higher the value, the higher quality the simulation will be at the expense of performance. The default value of 2 is usually very adequate.
detector?: any; // A Matter.Detector instance, default is Matter.Detector
enableSleeping?: boolean; // A flag that specifies whether the engine should allow sleeping via the Matter.Sleeping module. Sleeping can improve stability and performance, but often at the expense of accuracy. default is false
gravity?: Gravity; // An optional gravitational acceleration applied to all bodies in engine.world on every update. default is { x: 0, y: 1, scale: 0.001 }
plugin?: any; // An object reserved for storing plugin-specific properties.
positionIterations?: number; // An integer Number that specifies the number of position iterations to perform each update. The higher the value, the higher quality the simulation will be at the expense of performance. The default value is 6
timing?: PhysicsTimingOptions; // An object that specifies the timing systems to use for engine updates and rendering. default is { lastDelta: 1000 / 60, lastElapsed: 1000 / 60, timeScale: 1, timestamp: 0 }
velocityIterations?: number; // An integer Number that specifies the number of velocity iterations to perform each update. The higher the value, the higher quality the simulation will be at the expense of performance.
world?: Object; // The root Matter.Composite instance that will contain all bodies, constraints and other composites to be simulated by this engine. default is a Matter.Composite with no bodies (Handled under the hood, do not worry about this)
}
/**
* For configuring the timing systems to use for engine updates and rendering
*
* @interface PhysicsTimingOptions
* @property {number} [lastDelta=1000 / 60] - A Number that represents the delta value used in the last engine update. default is 0
* @property {number} [lastElapsed=1000 / 60] - A Number that represents the total execution time elapsed during the last Engine.update in milliseconds. It is updated by timing from the start of the last Engine.update call until it ends. default is 0. This value will also include the total execution time of all event handlers directly or indirectly triggered by the engine update.
* @property {number} [timeScale=1] - A Number that specifies the global scaling factor of time for all bodies. A value of 0 freezes the simulation. A value of 0.1 gives a slow-motion effect. A value of 1.2 gives a speed-up effect. default is 1
* @property {number} [timestamp=0] - A Number that specifies the current simulation-time in milliseconds starting from 0. It is incremented on every Engine.update by the given delta argument. default is 0
*/
interface PhysicsTimingOptions {
lastDelta: number; // A Number that represents the delta value used in the last engine update. default is 0
lastElapsed: number; // A Number that represents the total execution time elapsed during the last Engine.update in milliseconds. It is updated by timing from the start of the last Engine.update call until it ends. default is 0. This value will also include the total execution time of all event handlers directly or indirectly triggered by the engine update.
timeScale: number; // A Number that specifies the global scaling factor of time for all bodies. A value of 0 freezes the simulation. A value of 0.1 gives a slow-motion effect. A value of 1.2 gives a speed-up effect. default is 1
timestamp: number; // A Number that specifies the current simulation-time in milliseconds starting from 0. It is incremented on every Engine.update by the given delta argument. default is 0
}
/**
* For configuring the phyisical properties of GameObjects in a scene
*
* @interface PhysicsOptions
* @property {number} [angle=0] - A Number specifying the angle of the body, in radians. default is 0
* @property {number} [angularSpeed=0] - Read only. Use GameObject.body.setAngularSpeed to set. default is 0. The current rotational speed of the body.
* @property {number} [anglarVelocity=0] - Read only. Use Body.setAngularVelocity to set. default is 0. Gets the current rotational velocity of the body.
* @property {string} [area] - Read only. Calculated automatically when vertices are set. A Number that measures the area of the body's convex hull.
* @property {Vector} [axes] - Read only. Calculated automatically when vertices are set. An array of unique axis vectors (edge normals) used for collision detection. These are automatically calculated when vertices are set. They are constantly updated by Body.update during the simulation.
* @property {Bounds} [bounds] - A Bounds object that defines the AABB region for the body. It is automatically calculated when vertices are set and constantly updated by Body.update during simulation.
* @property {Object} [collisionFileter] - An Object that specifies the collision filtering properties of this body. See https://brm.io/matter-js/docs/classes/Body.html#property_collisionFilter
* @property {number} [collisionFileter.category=1] - A bit field that specifies the collision category this body belongs to. The category value should have only one bit set, for example 0x0001. This means there are up to 32 unique collision categories available. See body.collisionFilter for more information. default is 1
* @property {number} [collisionFileter.group=0] - An Integer Number, that specifies the collision group this body belongs to. See body.collisionFilter for more information. default is 0
* @property {number} [collisionFileter.mask=-1] - A bit mask that specifies the collision categories this body may collide with. See body.collisionFilter for more information. default is -1
* @property {number} [deltaTime=1000 / 60] - Read only. Updated during engine update. A Number that records the last delta time value used to update this body. Used to calculate speed and velocity. default is 1000 / 60
* @property {number} [density=0.001] - Read only. Use GameObject.body.setDensity to set. A Number that defines the density of the body (mass per unit area). Mass will also be updated when set. default is 0.001
* @property {Vector} [force] - A Vector that accumulates the total force applied to the body for a single update. Force is zeroed after every Engine.update, so constant forces should be applied for every update they are needed. Apply force with GameObject.body.applyForce (https://brm.io/matter-js/docs/classes/Body.html#method_applyForce)
* @property {number} [friction=0.1] - A Number that defines the friction of the body. The value is always positive and is in the range (0, 1). A value of 0 means that the body may slide indefinitely. A value of 1 means the body may come to a stop almost instantly after a force is applied. The effects of the value may be non-linear. High values may be unstable depending on the body. The engine uses a Coulomb friction model including static and kinetic friction. Note that collision response is based on pairs of bodies, and that friction values are combined with the following formula: Math.min(bodyA.friction, bodyB.friction). default is 0.1
* @property {number} [frictionAir=0.01] - A Number that defines the air friction of the body (air resistance). A value of 0 means the body will never slow as it moves through space. The higher the value, the faster a body slows when moving through space. The effects of the value are non-linear. default is 0.01
* @property {number} [frictionStatic=0.5] - A Number that defines the static friction of the body (in the Coulomb friction model). A value of 0 means the body will never 'stick' when it is nearly stationary and only dynamic friction is used. The higher the value (e.g. 10), the more force it will take to initially get the body moving when nearly stationary. This value is multiplied with the friction property to make it easier to change friction and maintain an appropriate amount of static friction. default is 0.5
* @property {number} [id] - An integer Number uniquely identifying number generated in Body.create by Common.nextId.
* @property {number} [inertia] - Read only. Automatically calculated when vertices, mass or density are set or set through GameObject.body.setInertia. A Number that defines the moment of inertia of the body. This is the second moment of area in two dimensions. Can be manually set to Infinity to prevent rotation of the body. See https://brm.io/matter-js/docs/classes/Body.html#method_setInertia.
* @property {number} [inverseInertia] - Read only. Automatically calculated when vertices, mass or density are set or calculated by Body.setInertia. A Number that defines the inverse moment of inertia of the body (1 / inertia).
* @property {number} [inverseMass] - Read only. Use GameObject.body.setMass to set. A Number that defines the inverse mass of the body (1 / mass).
* @property {boolean} [isSensor=false] - A flag that indicates whether a body is a sensor. Sensor triggers collision events, but doesn't react with colliding body physically. False by default.
* @property {boolean} [isSleeping] - Read only. Use Matter.Sleeping.set to set. A flag that indicates whether the body is considered sleeping. A sleeping body acts similar to a static body, except it is only temporary and can be awoken. False by default.
* @property {boolean} [isStatic] - Read only. Use GameObject.body.setStatic to set. A flag that indicates whether a body is considered static. A static body can never change position or angle and is completely fixed. False by default.
* @property {string} [label] - A String that defines the label property of the body. See https://brm.io/matter-js/docs/classes/Body.html#property_label
* @property {number} [mass] - Read only. Use GameObject.body.setMass to set. Calculated automatically from object properties
* @property {number} [motion] - Read only. Calculated during engine update only when sleeping is enabled. A Number that loosely measures the amount of movement a body currently has. Derived from body.speed^2 + body.angularSpeed^2. See Sleeping.update. 0 by default.
* @property {Object} [parent] - Read only. Updated by GameObject.body.setParts. A reference to the body that this is a part of. See body.parts. This is a self reference if the body is not a part of another body.
* @property {Object[]} [parts] - Read only. Use Body.setParts to set. An array of bodies that make up this body. The first body in the array must always be a self reference to the current body instance. All bodies in the parts array together form a single rigid compound body. Parts are allowed to overlap, have gaps or holes or even form concave bodies. Parts themselves should never be added to a World, only the parent body should be. Use Body.setParts when setting parts to ensure correct updates of all properties.
* @property {Vector} [position] - Read only. Use GameObject.body.setPosition to set. A Vector that specifies the current world-space position of the body. Default is { x: 0, y: 0 }
* @property {number} [restitution=0] - A Number that defines the restitution (elasticity) of the body. The value is always positive and is in the range (0, 1). A value of 0 means collisions may be perfectly inelastic and no bouncing may occur. A value of 0.8 means the body may bounce back with approximately 80% of its kinetic energy. Note that collision response is based on pairs of bodies, and that restitution values are combined with the following formula: Math.max(bodyA.restitution, bodyB.restitution). 0 by default.
* @property {number} [sleepThreshold=60] - A Number that defines the length of time during which this body must have near-zero velocity before it is set as sleeping by the Matter.Sleeping module (if sleeping is enabled by the engine). Default is 60.
* @property {number} [slop=0.05] - A Number that specifies a thin boundary around the body where it is allowed to slightly sink into other bodies. This is required for proper collision response, including friction and restitution effects. The default should generally suffice in most cases. You may need to decrease this value for very small bodies that are nearing the default value in scale. Default is 0.05.
* @property {number} [speed] - Read only. Use GameObject.body.setVelocity to set. A Number that specifies the speed of the body. This value is always positive, representing the magnitude of velocity. 0 by default.
* @property {number} [timeScale=1] - A Number that specifies per-body time scaling. Defualt is 1.... to make time slower relative to other objects in the scene, set to a number less than 1. To make time faster relative to other objects in the scene, set to a number greater than 1.
* @property {number} [torque] - A Number that accumulates the total torque (turning force) applied to the body for a single update. See also Body.applyForce. Torque is zeroed after every Matter.Engine.update (Scene.engine), so constant torques should be applied for every update they are needed. Torques result in angular acceleration on every update, which depends on body inertia and the engine update delta. 0 by default.
* @property {Vector} [velocity] - Read only. Use GameObject.body.setVelocity to set. A Vector that specifies the current world-space velocity of the body. Default is { x: 0, y: 0 }
*/
interface PhysicsOptions {
angle?: number; // A Number specifying the angle of the body, in radians. default is 0
readonly angularSpeed?: number; // Read only. Use GameObject.body.setAngularSpeed to set. default is 0. The current rotational speed of the body.
anglarVelocity?: number; // Read only. Use Body.setAngularVelocity to set. default is 0. Gets the current rotational velocity of the body.
readonly area?: string; // Read only. Calculated automatically when vertices are set. A Number that measures the area of the body's convex hull.
readonly axes?: Vector; // Read only. Calculated automatically when vertices are set. An array of unique axis vectors (edge normals) used for collision detection. These are automatically calculated when vertices are set. They are constantly updated by Body.update during the simulation.
bounds?: Bounds; // A Bounds object that defines the AABB region for the body. It is automatically calculated when vertices are set and constantly updated by Body.update during simulation.
collisionFileter?: { // An Object that specifies the collision filtering properties of this body. See https://brm.io/matter-js/docs/classes/Body.html#property_collisionFilter
category?: number; // A bit field that specifies the collision category this body belongs to. The category value should have only one bit set, for example 0x0001. This means there are up to 32 unique collision categories available. See body.collisionFilter for more information. default is 1
group?: number; // An Integer Number, that specifies the collision group this body belongs to. See body.collisionFilter for more information. default is 0
mask?: number; // A bit mask that specifies the collision categories this body may collide with. See body.collisionFilter for more information. default is -1
};
readonly deltaTime?: number; // Read only. Updated during engine update. A Number that records the last delta time value used to update this body. Used to calculate speed and velocity. default is 1000 / 60
readonly density?: number; // Read only. Use GameObject.body.setDensity to set. A Number that defines the density of the body (mass per unit area). Mass will also be updated when set. default is 0.001
force?: Vector; // A Vector that accumulates the total force applied to the body for a single update. Force is zeroed after every Engine.update, so constant forces should be applied for every update they are needed. Apply force with GameObject.body.applyForce (https://brm.io/matter-js/docs/classes/Body.html#method_applyForce)
friction?: number; // A Number that defines the friction of the body. The value is always positive and is in the range (0, 1). A value of 0 means that the body may slide indefinitely. A value of 1 means the body may come to a stop almost instantly after a force is applied. The effects of the value may be non-linear. High values may be unstable depending on the body. The engine uses a Coulomb friction model including static and kinetic friction. Note that collision response is based on pairs of bodies, and that friction values are combined with the following formula: Math.min(bodyA.friction, bodyB.friction). default is 0.1
frictionAir?: number; // A Number that defines the air friction of the body (air resistance). A value of 0 means the body will never slow as it moves through space. The higher the value, the faster a body slows when moving through space. The effects of the value are non-linear. default is 0.01
frictionStatic?: number; // A Number that defines the static friction of the body (in the Coulomb friction model). A value of 0 means the body will never 'stick' when it is nearly stationary and only dynamic friction is used. The higher the value (e.g. 10), the more force it will take to initially get the body moving when nearly stationary. This value is multiplied with the friction property to make it easier to change friction and maintain an appropriate amount of static friction. default is 0.5
id?: number; // An integer Number uniquely identifying number generated in Body.create by Common.nextId.
readonly inertia?: number; // Read only. Automatically calculated when vertices, mass or density are set or set through GameObject.body.setInertia. A Number that defines the moment of inertia of the body. This is the second moment of area in two dimensions. Can be manually set to Infinity to prevent rotation of the body. See https://brm.io/matter-js/docs/classes/Body.html#method_setInertia.
readonly inverseInertia?: number; // Read only. Automatically calculated when vertices, mass or density are set or calculated by Body.setInertia. A Number that defines the inverse moment of inertia of the body (1 / inertia).
readonly inverseMass?: number; // Read only. Use GameObject.body.setMass to set. A Number that defines the inverse mass of the body (1 / mass).
isSensor?: boolean; // A flag that indicates whether a body is a sensor. Sensor triggers collision events, but doesn't react with colliding body physically. False by default.
readonly isSleeping?: boolean; // Read only. Use Matter.Sleeping.set to set. A flag that indicates whether the body is considered sleeping. A sleeping body acts similar to a static body, except it is only temporary and can be awoken. False by default.
readonly isStatic?: boolean; // Read only. Use GameObject.body.setStatic to set. A flag that indicates whether a body is considered static. A static body can never change position or angle and is completely fixed. False by default.
label?: string; // A String that defines the label property of the body. See https://brm.io/matter-js/docs/classes/Body.html#property_label
readonly mass?: number; // A Number that defines the mass of the body. Density will also be updated when set. Read only. Use GameObject.body.setMass to set. Calculated automatically from object properties
readonly motion?: number; // Read only. Calculated during engine update only when sleeping is enabled. A Number that loosely measures the amount of movement a body currently has. Derived from body.speed^2 + body.angularSpeed^2. See Sleeping.update. 0 by default.
readonly parent?: Object; // Read only. Updated by GameObject.body.setParts. A reference to the body that this is a part of. See body.parts. This is a self reference if the body is not a part of another body.
readonly parts?: Object[]; // Read only. Use Body.setParts to set. An array of bodies that make up this body. The first body in the array must always be a self reference to the current body instance. All bodies in the parts array together form a single rigid compound body. Parts are allowed to overlap, have gaps or holes or even form concave bodies. Parts themselves should never be added to a World, only the parent body should be. Use Body.setParts when setting parts to ensure correct updates of all properties.
readonly position?: Vector; // Read only. Use GameObject.body.setPosition to set. A Vector that specifies the current world-space position of the body. Default is { x: 0, y: 0 }
restitution?: number; // A Number that defines the restitution (elasticity) of the body. The value is always positive and is in the range (0, 1). A value of 0 means collisions may be perfectly inelastic and no bouncing may occur. A value of 0.8 means the body may bounce back with approximately 80% of its kinetic energy. Note that collision response is based on pairs of bodies, and that restitution values are combined with the following formula: Math.max(bodyA.restitution, bodyB.restitution). 0 by default.
sleepThreshold?: number; // A Number that defines the length of time during which this body must have near-zero velocity before it is set as sleeping by the Matter.Sleeping module (if sleeping is enabled by the engine). Default is 60.
slop?: number; // A Number that specifies a thin boundary around the body where it is allowed to slightly sink into other bodies. This is required for proper collision response, including friction and restitution effects. The default should generally suffice in most cases. You may need to decrease this value for very small bodies that are nearing the default value in scale. Default is 0.05.
readonly speed?: number; // Read only. Use GameObject.body.setVelocity to set. A Number that specifies the speed of the body. This value is always positive, representing the magnitude of velocity. 0 by default.
timeScale?: number; // A Number that specifies per-body time scaling. Defualt is 1.... to make time slower relative to other objects in the scene, set to a number less than 1. To make time faster relative to other objects in the scene, set to a number greater than 1.
torque?: number; // A Number that accumulates the total torque (turning force) applied to the body for a single update. See also Body.applyForce. Torque is zeroed after every Matter.Engine.update (Scene.engine), so constant torques should be applied for every update they are needed. Torques result in angular acceleration on every update, which depends on body inertia and the engine update delta. 0 by default.
readonly "type"?: string; // A String denoting the type of object. Should always be "body". Read only.
readonly velocity?: Vector; // Read only. Use Body.setVelocity to set. Equivalent to the magnitude of body.angularVelocity (always positive). { x: 0, y: 0 } by default.
readonly vertices?: Vector[]; // Read only. Use GameObject.body.setVertices or GameObject.body.setParts to set. See also Scene.Bodies.fromVertices(). An array of Vector objects that specify the convex hull of the rigid body. These should be provided about the origin (0, 0). E.g. [{ x: 0, y: 0 }, { x: 25, y: 50 }, { x: 50, y: 0 }]. Vertices must always be convex, in clockwise order and must not contain any duplicate points. Do not worry about this, it is handled under the hood. If the polygon is not convex and is built using the Polygon class, the points will be deconstructed into multiple convex polygons.
}
// Passed to the draw function of a GameObject
/**
* Passed to the draw function of a GameObject
*
* @interface DrawOptions
* @property {HTMLCanvasElement} [canvas] - Used for positional calculations.
* @property {CanvasRenderingContext2D} ctx - Canvas rendering context that the object draws with
* @property {Vec2} camera - Position of the camera in the scene
*
* @example
* ```js
* const options: DrawOptions = {
* canvas: document.getElementById("canvas") as HTMLCanvasElement,
* ctx: document.getElementById("canvas").getContext("2d"),
* camera: [0, 0]
* }
* ```
*/
interface DrawOptions {
canvas?: HTMLCanvasElement; // Used for positional calculations.
ctx: CanvasRenderingContext2D; // Canvas rendering context that the object draws with
camera: Vec2; // Position of the camera in the scene
}
/**
* Options to configure the MultiPlayerInputHandler (on the server)
*
* @interface MultiPlayerInputHandlerOptions
* @property {Array<ServerInputHandler>} monitors - Array of ServerInputHandlers to monitor
*/
interface MultiPlayerInputHandlerOptions {
monitors: Array<ServerInputHandler>;
}
/**
* Options to configure a ServerInputHandler
*
* @interface ServerInputHandlerOptions
* @property {string} key - Key to listen for
* @property {number} fireRate - How often to fire the event (in milliseconds)
* @property {Function} on - Function to run when the event is fired
*/
interface ServerInputHandlerOptions {
key: string;
fireRate: number;
on: (socket: any, playerObject: GameObject) => void;
}
/**
* Options to configure a Player
*
* @interface PlayerOptions
* @property {string} id - ID of the player
* @property {string} label - Label of the player
* @property {any} socket - Socket.io socket of the player
* @property {GameObject} playerGameObject - Player GameObject. (Optional, an empty object will be created if none is provided)
* @property {boolean} showLabel - Whether to show the label of the player (default is false)
*/
interface PlayerOptions {
id: string;
label: string;
socket: any;
playerGameObject?: GameObject;
showLabel?: boolean;
}
/**
* Options to configure a MultiPlayerServer
*
* @interface MultiPlayerServerOptions
* @property {number} port - Port to run the server on
* @property {any} httpServer - HTTP server to run the server on
* @property {Function} onNewConnection - Function to run when a new connection is made. Should return a label to assign to the connection. If no lable is returned, one is automatically generated
* @property {Function} onDisconnect - Function to run when a connection is lost
* @property {MultiPlayerSceneManager} sceneManager - MultiPlayerSceneManager to use
* @property {number} [tickSpeed=1000 / 60] - How often to update the scene (in milliseconds)
* @property {Function} [serverLive] - Function to run when the server is live
* @property {GameObject} newPlayerObject - GameObject to use for new players
* @property {MultiPlayerInputHandler} [inputHandler] - MultiPlayerInputHandler to use
* @property {(socket: any, id: string, data: object) => [GameObject, string] | Promise<[GameObject, string]>} onNewPlayer - Function to run when a new player joins
*/
interface MultiPlayerServerOptions {
port: number;
httpServer: any;
onNewConnection: (socket: any) => string;
onDisconnect: Function;
sceneManager: MultiPlayerSceneManager;
tickSpeed?: number;
serverLive?: Function;
newPlayerObject: GameObjectBuilder;
inputHandler?: MultiPlayerInputHandler;
onNewPlayer: (socket: any, id: string, data: object) => [GameObject, string] | Promise<[GameObject, string]>;
}
/**
* A connection to a client
*
* @interface Connection
* @property {any} socket - Socket.io socket of the connection
* @property {string} id - ID of the connection (matches ID of the player)
* @property {string} label - Label of the connection
*/
interface Connection {
socket: any;
id: string;
label: string;
}
/**
* Options passed to create a new player. Used by the MultiPlayerSceneManager
*
* @interface NewPlayerOptions
* @property {GameObject | Sprite | Polygon} class - The class of the game object to create
* @property {GameObjectOptions | SpriteOptions | PolygonOptions} options - Options to pass to the game object
*/
interface GameObjectBuilder {
class: any;
options: GameObjectOptions | SpriteOptions | PolygonOptions;
}
// Bounds defining the boundaries of a physics body
/**
* Bounds defining the boundaries of a physics body
*
* @type Bounds
* @property {Vec2} min - minimum bounds
* @property {Vec2} max - maximum bounds
*/
type Bounds = {
min: Vec2; // minimum bounds
max: Vec2; // maximum bounds
}
/**
* An alias for Vec2
*
* @type {Point}
* @alias Vec2
*/
type Point = Vec2; // alias for a Vec2, for easier readability
/**
* A 2D vector
* @type {Vec2}
*/
type Vec2 = [number, number]; // 2D vector
// Same as Vec2, but with a different structure for compatability with matter.js
/**
* Same as Vec2, but with a different structure for compatability with matter.js
*
* @type {Vector}
*/
type Vertex = {
x: number;
y: number;
};
// Alias for Vertex
/**
* Alias for Vertex
*
* @type {Vector}
* @alias Vertex
*/
type Vector = Vertex;
// A projection of a polygon onto an axis, used for collision detection
/**
* A projection of a polygon onto an axis, used for collision detection
*
* @type {Projection}
* @property {number} min - minimum projection
* @property {number} max - maximum projection
*/
type Projection = {
min: number;
max: number;
};
// Defines a collision between two objects
// [object1, object2, function to run on collision, function to run on seperation, active]
/**
* Defines a collision between two objects
*
* @type {CollisionMonitor}
* @property {GameObject} 0 - First object
* @property {GameObject} 1 - Second object
* @property {Function} 2 - Function to run on collision
* @property {Function} 3 - Function to run on seperation
* @property {boolean} 4 - Whether the collision is active
*/
type CollisionMonitor = [GameObject, GameObject, Function, Function, boolean];
/**
* Function for handling user input
*
* @type {EventOnFunction}
* @param {Event | GameObject} event - The event that triggered the function. If the input listens for keyboard input, this is an Event object, otherwise it is the GameObject that was clicked.
*/
type EventOnFunction = (event: Event | GameObject) => void;
/**
* Used to amplify the sound of a media element
* https://cwestblog.com/2017/08/17/html5-getting-more-volume-from-the-web-audio-api/
*
* @param mediaElem Media element to apply gain on
* @param multiplier % to amplify sound
* @returns All relevant data for the amplified media
*/
function amplifyMedia(mediaElem: HTMLMediaElement, multiplier: number): AmplifiedMedia {
var context = new (window.AudioContext),
result = {
context: context,
source: context.createMediaElementSource(mediaElem),
gain: context.createGain(),
media: mediaElem,
amplify: function (multiplier: number) { result.gain.gain.value = multiplier; },
getAmpLevel: function () { return result.gain.gain.value; }
};
result.source.connect(result.gain);
result.gain.connect(context.destination);
result.amplify(multiplier);
return result;
}
// Used for rendering lights onto a scene, called each pixel and calculates the brightness of the pixel based on the lights in the scene
/**
* Used for rendering lights onto a scene, called each pixel and calculates the brightness of the pixel based on the lights in the scene
* @param pix Uint8ClampedArray of the pixels of the canvas
* @param width Width of the canvas
* @param height Height of the canvas
* @param lights Array of lights in the scene (formatted)
* @param numLights Number of lights in the scene
* @param dlights Array of directional lights in the scene (formatted)
* @param numDlights Number of directional lights in the scene
* @param ambient Ambient lighting in the scene
* @param fog Constant that defines how lights spread in the scene
* @param globalAlpha Contant transparency of the scene
*/
function GPULightingKernel(this: any, pix: Uint8ClampedArray, width: number, height: number, lights: Array<number>, numLights: number, dlights: Array<number>, numDlights: number, ambient: number, fog: number, globalAlpha: number) {
// aliases for coordinates (loop friendly)
const i = this.thread.y;
const j = this.thread.x;
// calculates the rows down from the top of the canvas
var rowsDown = height - i;
// calculates the pixel number
var pixNum = ((width * rowsDown) + j) * 4;
// initial brightness (format is r,g,b)
var brightness = [ambient, ambient, ambient];
// loop over each light, calculate brightness of each pixel based on proximity to the light, how far the light spreads (diffuse), and the strength of the light
for (var k = 0; k < numLights; k++) {
var ln = k * 7;
var attenuation = (1 - Math.pow(Math.min(distance([j, i], [lights[ln], height - lights[ln + 1]]), lights[ln + 3]) / lights[ln + 3], fog));
var strength = lights[ln + 2];
brightness[0] += attenuation * strength * (lights[ln + 4] / 255);
brightness[1] += attenuation * strength * (lights[ln + 5] / 255);
brightness[2] += attenuation * strength * (lights[ln + 6] / 255);
}
// loops over each directional light, calculates brightness of each pixel based on proximity to the light, how far the light spreads (diffuse), the angle of the light, and the strength of the light
for (var d = 0; d < numDlights; d++) {
var ln = d * 9;
var angle = dlights[ln];
var lightDir = angle
const lightX = dlights[ln + 1];
const lightY = dlights[ln + 2];
const intensity = dlights[ln + 3];
const diffuse = dlights[ln + 4];
const spread = dlights[ln + 5];
const redL = dlights[ln + 6];
const greenL = dlights[ln + 7];
const blueL = dlights[ln + 8];
const dirX = this.thread.x - lightX;
const dirY = this.thread.y - (height - lightY);
const dist = Math.sqrt(dirX * dirX + dirY * dirY);
const angleToLight = (Math.atan2(dirY, dirX) + 2 * Math.PI) % (2 * Math.PI);
const angleDiff = Math.acos(Math.cos(angleToLight - lightDir));
// if the pixel is within the spread of the light, calculate the brightness of the pixel
if (angleDiff <= spread / 2) {
const diffuseFactor = Math.max(0, Math.cos(angleDiff) * (1 - (dist / diffuse)));
brightness[0] += diffuseFactor * intensity * (redL / 255);
brightness[1] += diffuseFactor * intensity * (greenL / 255);
brightness[2] += diffuseFactor * intensity * (blueL / 255);
}
}
// apply brightness to the pixel
var red = ((pix[pixNum] / 255) * brightness[0]) * globalAlpha;
var green = ((pix[pixNum + 1] / 255) * brightness[1]) * globalAlpha;
var blue = ((pix[pixNum + 2] / 255) * brightness[2]) * globalAlpha;
var alpha = (pix[pixNum + 3] / 255) * globalAlpha;
// return the color
this.color(
red, green, blue, alpha
)
}
// checks if a polygon is convex
/**
* Checks if a polygon is convex
*
* @param points List of points that make up the polygon
* @returns Returns true of the polygon is convex, false if it is concave
*/
function isConvex(points: Point[]): boolean {
if (points.length < 3) {
// A polygon must have at least 3 points
return false;
}
function crossProduct(p1: Point, p2: Point, p3: Point): number {
// Cross product of vectors (p2 - p1) and (p3 - p2)
return (p2[0] - p1[0]) * (p3[1] - p2[1]) - (p2[1] - p1[1]) * (p3[0] - p2[0]);
}
let signDetected: Boolean = false;
let foundSign: string = '';
for (let i = 0; i < points.length; i++) {
const p1 = points[i];
const p2 = points[(i + 1) % points.length];
const p3 = points[(i + 2) % points.length];
const crossProductResult = crossProduct(p1, p2, p3);
if (crossProductResult !== 0) {
// Check if the sign of the cross product changes
if (signDetected === false) {
foundSign = crossProductResult > 0 ? 'positive' : 'negative';
signDetected = true;
} else {
if ((crossProductResult > 0 && foundSign === 'negative') ||
(crossProductResult < 0 && foundSign === 'positive')) {
// If the sign changes again, the polygon is concave
return false;
}
}
}
}
// If no sign change is detected, the polygon is convex
return true;
}
/**
* Checks to see if a light is a directional light or not
*
* @param light Light instnace to check
* @returns Returns true if the light is a directional light, false if it is not
*/
function instanceOfDirectionalLight(light: Light | DirectionalLight): light is DirectionalLight {
return (<DirectionalLight>light).angle !== undefined;
}
// gets the centroid of a polygon
/**
* Finds the centroid of the polygon
*
* @param points List of points that make up the polygon
* @returns Returns the centroid of the polygon
*/
function getCentroid(points: Point[]): Point {
var x: number = 0, y: number = 0;
for (var i = 0; i < points.length; i++) {
x += points[i][0];
y += points[i][1];
}
return [x / points.length, y / points.length];
}
// calculates FPS based on a buffer
/**
* Calculates FPS of the scene based on a buffer of timestamps
*
* @param buffer Array of numbers that represent the time between frames
* @param FPS_BUFFER_LENGTH How long the buffer should be before an accurate FPS can be calculated
* @returns FPS of the scene
*/
function calculateFPS(buffer: Array<number>, FPS_BUFFER_LENGTH: number = 60): number | string {
buffer = buffer.map(t => {
var seconds = 1000 / t;
return seconds
});
const average = (array: Array<number>): number => array.reduce((a, b) => a + b) / array.length;
if (buffer.length < FPS_BUFFER_LENGTH) return "--"
return Math.round(average(buffer));
}
// gets the top left most point of a polygon
/**
* Finds the top left most point of a given polyogon
*
* @param points List of points that make up the polygon
* @returns Returns the top left most point of the polygon
*/
function findTopLeftMostPoint(points: Point[]): Point {
if (points.length === 0) {
throw new Error('Points array must not be empty');
}
// Initialize with the first point
let topLeftMost = points[0];
// Iterate through the rest of the points
for (let i = 1; i < points.length; i++) {
const currentPoint = points[i];
// Compare x-coordinates first
if (currentPoint[0] < topLeftMost[0]) {
// If the current point's x-coordinate is smaller, update topLeftMost
topLeftMost = currentPoint;
} else if (currentPoint[0] === topLeftMost[0]) {
// If x-coordinates are equal, compare y-coordinates
if (currentPoint[1] < topLeftMost[1]) {
// If the current point's y-coordinate is smaller, update topLeftMost
topLeftMost = currentPoint;
}
}
// If the x-coordinate is greater, no need to update
}
return topLeftMost;
}
// checks to see if a polygon is a square
/**
* Checks to see if a given polygon is a square (used for collision detection)
*
* @param points List of points that make up the polygon
* @returns Returns true if the polygon is a square, false if it is not
*/
function isSquare(points: Point[]): boolean {
if (points.length !== 4) return false;
// Assuming points is an array of 4 points
const sideLengths = [
distance(points[0], points[1]),
distance(points[1], points[2]),
distance(points[2], points[3]),
distance(points[3], points[0])
];
// A square has 4 equal side lengths, Set() removes duplicates
const uniqueSideLengths = new Set(sideLengths);
return uniqueSideLengths.size === 1;
}
// gets the distance between two points
/**
* Gets the distance between two points
*
* @param point1 The first point
* @param point2 The second point
* @returns The distance between the points