melonjs 12.0.0 → 13.1.1

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.
Files changed (54) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +4 -6
  3. package/dist/melonjs.js +22648 -22478
  4. package/dist/melonjs.min.js +5 -6
  5. package/dist/melonjs.module.d.ts +289 -264
  6. package/dist/melonjs.module.js +21979 -21804
  7. package/package.json +15 -16
  8. package/src/application/application.js +231 -0
  9. package/src/audio/audio.js +8 -2
  10. package/src/camera/camera2d.js +6 -6
  11. package/src/game.js +9 -232
  12. package/src/index.js +3 -3
  13. package/src/input/keyboard.js +2 -2
  14. package/src/input/pointer.js +4 -5
  15. package/src/input/pointerevent.js +10 -10
  16. package/src/lang/deprecated.js +27 -30
  17. package/src/level/level.js +2 -2
  18. package/src/level/tiled/TMXGroup.js +10 -0
  19. package/src/level/tiled/TMXLayer.js +11 -2
  20. package/src/level/tiled/TMXObject.js +13 -2
  21. package/src/level/tiled/TMXTileMap.js +15 -3
  22. package/src/level/tiled/TMXTileset.js +8 -0
  23. package/src/loader/loader.js +64 -28
  24. package/src/loader/loadingscreen.js +27 -28
  25. package/src/math/color.js +62 -42
  26. package/src/math/observable_vector2.js +26 -2
  27. package/src/math/observable_vector3.js +32 -4
  28. package/src/math/vector2.js +23 -0
  29. package/src/math/vector3.js +26 -0
  30. package/src/physics/body.js +27 -51
  31. package/src/physics/detector.js +3 -3
  32. package/src/physics/quadtree.js +58 -29
  33. package/src/physics/world.js +32 -3
  34. package/src/renderable/container.js +2 -2
  35. package/src/renderable/imagelayer.js +8 -8
  36. package/src/renderable/nineslicesprite.js +27 -1
  37. package/src/renderable/trigger.js +4 -4
  38. package/src/state/stage.js +1 -1
  39. package/src/state/state.js +50 -3
  40. package/src/system/device.js +814 -981
  41. package/src/system/event.js +2 -1
  42. package/src/system/platform.js +32 -0
  43. package/src/system/save.js +23 -14
  44. package/src/system/timer.js +12 -35
  45. package/src/text/text.js +9 -12
  46. package/src/tweens/tween.js +6 -6
  47. package/src/utils/string.js +13 -0
  48. package/src/video/canvas/canvas_renderer.js +30 -65
  49. package/src/video/renderer.js +23 -30
  50. package/src/video/texture/canvas_texture.js +39 -3
  51. package/src/video/video.js +27 -25
  52. package/src/video/webgl/glshader.js +1 -1
  53. package/src/video/webgl/webgl_compositor.js +2 -2
  54. package/src/video/webgl/webgl_renderer.js +8 -20
@@ -1,7 +1,6 @@
1
1
  import Vector2d from "./../math/vector2.js";
2
2
  import Container from "./../renderable/container.js";
3
3
  import * as arrayUtil from "./../utils/array.js";
4
- import { viewport } from "./../game.js";
5
4
 
6
5
  /*
7
6
  * A QuadTree implementation in JavaScript, a 2d spatial subdivision algorithm.
@@ -21,16 +20,17 @@ var QT_ARRAY = [];
21
20
  * or create a new one if the array is empty
22
21
  * @ignore
23
22
  */
24
- function QT_ARRAY_POP(bounds, max_objects = 4, max_levels = 4, level = 0) {
23
+ function QT_ARRAY_POP(world, bounds, max_objects = 4, max_levels = 4, level = 0) {
25
24
  if (QT_ARRAY.length > 0) {
26
25
  var _qt = QT_ARRAY.pop();
26
+ _qt.world = world;
27
27
  _qt.bounds = bounds;
28
28
  _qt.max_objects = max_objects;
29
29
  _qt.max_levels = max_levels;
30
30
  _qt.level = level;
31
31
  return _qt;
32
32
  } else {
33
- return new QuadTree(bounds, max_objects, max_levels, level);
33
+ return new QuadTree(world, bounds, max_objects, max_levels, level);
34
34
  }
35
35
  };
36
36
 
@@ -55,12 +55,17 @@ var QT_VECTOR = new Vector2d();
55
55
  */
56
56
  class QuadTree {
57
57
  /**
58
+ * @param {World} world the physic world this QuadTree belongs to
58
59
  * @param {Bounds} bounds bounds of the node
59
60
  * @param {number} [max_objects=4] max objects a node can hold before splitting into 4 subnodes
60
61
  * @param {number} [max_levels=4] total max levels inside root Quadtree
61
62
  * @param {number} [level] deepth level, required for subnodes
62
63
  */
63
- constructor(bounds, max_objects = 4, max_levels = 4, level = 0) {
64
+ constructor(world, bounds, max_objects = 4, max_levels = 4, level = 0) {
65
+
66
+ this.world = world;
67
+ this.bounds = bounds;
68
+
64
69
  this.max_objects = max_objects;
65
70
  this.max_levels = max_levels;
66
71
 
@@ -82,36 +87,60 @@ class QuadTree {
82
87
  top = this.bounds.top;
83
88
 
84
89
  //top right node
85
- this.nodes[0] = QT_ARRAY_POP({
86
- left : left + subWidth,
87
- top : top,
88
- width : subWidth,
89
- height : subHeight
90
- }, this.max_objects, this.max_levels, nextLevel);
90
+ this.nodes[0] = QT_ARRAY_POP(
91
+ this.world,
92
+ this.bounds, {
93
+ left : left + subWidth,
94
+ top : top,
95
+ width : subWidth,
96
+ height : subHeight
97
+ },
98
+ this.max_objects,
99
+ this.max_levels,
100
+ nextLevel
101
+ );
91
102
 
92
103
  //top left node
93
- this.nodes[1] = QT_ARRAY_POP({
94
- left : left,
95
- top: top,
96
- width : subWidth,
97
- height : subHeight
98
- }, this.max_objects, this.max_levels, nextLevel);
104
+ this.nodes[1] = QT_ARRAY_POP(
105
+ this.world,
106
+ this.bounds, {
107
+ left : left,
108
+ top: top,
109
+ width : subWidth,
110
+ height : subHeight
111
+ },
112
+ this.max_objects,
113
+ this.max_levels,
114
+ nextLevel
115
+ );
99
116
 
100
117
  //bottom left node
101
- this.nodes[2] = QT_ARRAY_POP({
102
- left : left,
103
- top : top + subHeight,
104
- width : subWidth,
105
- height : subHeight
106
- }, this.max_objects, this.max_levels, nextLevel);
118
+ this.nodes[2] = QT_ARRAY_POP(
119
+ this.world,
120
+ this.bounds, {
121
+ left : left,
122
+ top : top + subHeight,
123
+ width : subWidth,
124
+ height : subHeight
125
+ },
126
+ this.max_objects,
127
+ this.max_levels,
128
+ nextLevel
129
+ );
107
130
 
108
131
  //bottom right node
109
- this.nodes[3] = QT_ARRAY_POP({
110
- left : left + subWidth,
111
- top : top + subHeight,
112
- width : subWidth,
113
- height : subHeight
114
- }, this.max_objects, this.max_levels, nextLevel);
132
+ this.nodes[3] = QT_ARRAY_POP(
133
+ this.world,
134
+ this.bounds, {
135
+ left : left + subWidth,
136
+ top : top + subHeight,
137
+ width : subWidth,
138
+ height : subHeight
139
+ },
140
+ this.max_objects,
141
+ this.max_levels,
142
+ nextLevel
143
+ );
115
144
  }
116
145
 
117
146
  /*
@@ -125,7 +154,7 @@ class QuadTree {
125
154
 
126
155
  // use game world coordinates for floating items
127
156
  if (item.isFloating === true) {
128
- pos = viewport.localToWorld(bounds.left, bounds.top, QT_VECTOR);
157
+ pos = this.world.app.viewport.localToWorld(bounds.left, bounds.top, QT_VECTOR);
129
158
  } else {
130
159
  pos = QT_VECTOR.set(item.left, item.top);
131
160
  }
@@ -28,6 +28,13 @@ class World extends Container {
28
28
  // to mimic the previous behavior
29
29
  this.anchorPoint.set(0, 0);
30
30
 
31
+ /**
32
+ * the application (game) this physic world belong to
33
+ * @public
34
+ * @type {Application}
35
+ */
36
+ this.app = null;
37
+
31
38
  /**
32
39
  * the rate at which the game world is updated,
33
40
  * may be greater than or lower than the display fps
@@ -80,7 +87,7 @@ class World extends Container {
80
87
  * @public
81
88
  * @type {QuadTree}
82
89
  */
83
- this.broadphase = new QuadTree(this.getBounds().clone(), collision.maxChildren, collision.maxDepth);
90
+ this.broadphase = new QuadTree(this, this.getBounds().clone(), collision.maxChildren, collision.maxDepth);
84
91
 
85
92
  // reset the world container on the game reset signal
86
93
  event.on(event.GAME_RESET, this.reset, this);
@@ -140,6 +147,24 @@ class World extends Container {
140
147
  return this;
141
148
  }
142
149
 
150
+ /**
151
+ * Apply gravity to the given body
152
+ * @name bodyApplyVelocity
153
+ * @memberof World
154
+ * @private
155
+ * @param {Body} body
156
+ */
157
+ bodyApplyGravity(body) {
158
+ // apply gravity to the current velocity
159
+ if (!body.ignoreGravity && body.gravityScale !== 0) {
160
+ var gravity = this.gravity;
161
+
162
+ // apply gravity if defined
163
+ body.force.x += (body.mass * gravity.x) * body.gravityScale;
164
+ body.force.y += (body.mass * gravity.y) * body.gravityScale;
165
+ }
166
+ }
167
+
143
168
  /**
144
169
  * update the game world
145
170
  * @name reset
@@ -147,7 +172,7 @@ class World extends Container {
147
172
  * @param {number} dt the time passed since the last frame update
148
173
  * @returns {boolean} true if the word is dirty
149
174
  */
150
- update (dt) {
175
+ update(dt) {
151
176
  var isPaused = state.isPaused();
152
177
 
153
178
  // clear the quadtree
@@ -163,13 +188,17 @@ class World extends Container {
163
188
  // if the game is not paused, and ancestor can be updated
164
189
  if (!(isPaused && (!ancestor.updateWhenPaused)) &&
165
190
  (ancestor.inViewport || ancestor.alwaysUpdate)) {
166
- // apply physics to the body (this moves it)
191
+ // apply gravity to this body
192
+ this.bodyApplyGravity(body);
193
+ // body update function (this moves it)
167
194
  if (body.update(dt) === true) {
168
195
  // mark ancestor as dirty
169
196
  ancestor.isDirty = true;
170
197
  };
171
198
  // handle collisions against other objects
172
199
  collisionCheck(ancestor);
200
+ // clear body force
201
+ body.force.set(0, 0);
173
202
  }
174
203
  }
175
204
  });
@@ -1,5 +1,5 @@
1
1
  import utils from "./../utils/utils.js";
2
- import * as game from "./../game.js";
2
+ import game from "./../game.js";
3
3
  import * as event from "./../system/event.js";
4
4
  import pool from "./../system/pooling.js";
5
5
  import state from "./../state/state.js";
@@ -638,7 +638,7 @@ class Container extends Renderable {
638
638
  * @memberof Container
639
639
  * @public
640
640
  * @param {Renderable} child
641
- * @param {boolean} [keepalive=False] True to prevent calling child.destroy()
641
+ * @param {boolean} [keepalive=false] true to prevent calling child.destroy()
642
642
  */
643
643
  removeChild(child, keepalive) {
644
644
  if (this.hasChild(child)) {
@@ -1,7 +1,7 @@
1
1
  import { renderer } from "./../video/video.js";
2
2
  import * as event from "./../system/event.js";
3
3
  import pool from "./../system/pooling.js";
4
- import { viewport } from "./../game.js";
4
+ import game from "./../game.js";
5
5
  import Sprite from "./sprite.js";
6
6
  import * as stringUtil from "./../utils/string.js";
7
7
 
@@ -134,7 +134,7 @@ class ImageLayer extends Sprite {
134
134
  this.repeatY = true;
135
135
  break;
136
136
  }
137
- this.resize(viewport.width, viewport.height);
137
+ this.resize(game.viewport.width, game.viewport.height);
138
138
  this.createPattern();
139
139
  }
140
140
 
@@ -146,13 +146,13 @@ class ImageLayer extends Sprite {
146
146
  event.on(event.VIEWPORT_ONRESIZE, this.resize, this);
147
147
  // force a first refresh when the level is loaded
148
148
  event.once(event.LEVEL_LOADED, () => {
149
- this.updateLayer(viewport.pos);
149
+ this.updateLayer(game.viewport.pos);
150
150
  });
151
151
  // in case the level is not added to the root container,
152
152
  // the onActivateEvent call happens after the LEVEL_LOADED event
153
153
  // so we need to force a first update
154
154
  if (this.ancestor.root !== true) {
155
- this.updateLayer(viewport.pos);
155
+ this.updateLayer(game.viewport.pos);
156
156
  }
157
157
  }
158
158
 
@@ -193,8 +193,8 @@ class ImageLayer extends Sprite {
193
193
 
194
194
  var width = this.width,
195
195
  height = this.height,
196
- bw = viewport.bounds.width,
197
- bh = viewport.bounds.height,
196
+ bw = game.viewport.bounds.width,
197
+ bh = game.viewport.bounds.height,
198
198
  ax = this.anchorPoint.x,
199
199
  ay = this.anchorPoint.y,
200
200
 
@@ -204,8 +204,8 @@ class ImageLayer extends Sprite {
204
204
  * See https://github.com/melonjs/melonJS/issues/741#issuecomment-138431532
205
205
  * for a thorough description of how this works.
206
206
  */
207
- x = ax * (rx - 1) * (bw - viewport.width) + this.offset.x - rx * vpos.x,
208
- y = ay * (ry - 1) * (bh - viewport.height) + this.offset.y - ry * vpos.y;
207
+ x = ax * (rx - 1) * (bw - game.viewport.width) + this.offset.x - rx * vpos.x,
208
+ y = ay * (ry - 1) * (bh - game.viewport.height) + this.offset.y - ry * vpos.y;
209
209
 
210
210
 
211
211
  // Repeat horizontally; start drawing from left boundary
@@ -47,7 +47,7 @@ class NineSliceSprite extends Sprite {
47
47
  this.width = Math.floor(settings.width);
48
48
  this.height = Math.floor(settings.height);
49
49
 
50
- // nine slice sprite specific local variables
50
+ // nine slice sprite specific internal variables
51
51
  this.nss_width = this.width;
52
52
  this.nss_height = this.height;
53
53
 
@@ -55,6 +55,32 @@ class NineSliceSprite extends Sprite {
55
55
  this.insety = settings.insety;
56
56
  }
57
57
 
58
+ /**
59
+ * width of the NineSliceSprite
60
+ * @public
61
+ * @type {number}
62
+ * @name width
63
+ */
64
+ get width() {
65
+ return super.width;
66
+ }
67
+ set width(value) {
68
+ super.width = this.nss_width = value;
69
+ }
70
+
71
+ /**
72
+ * height of the NineSliceSprite
73
+ * @public
74
+ * @type {number}
75
+ * @name height
76
+ */
77
+ get height() {
78
+ return super.height;
79
+ }
80
+ set height(value) {
81
+ super.height = this.nss_height = value;
82
+ }
83
+
58
84
  /**
59
85
  * @ignore
60
86
  */
@@ -2,7 +2,7 @@ import Renderable from "./renderable.js";
2
2
  import collision from "./../physics/collision.js";
3
3
  import Body from "./../physics/body.js";
4
4
  import level from "./../level/level.js";
5
- import { world, viewport } from "./../game.js";
5
+ import game from "./../game.js";
6
6
  import pool from "./../system/pooling.js";
7
7
 
8
8
  /**
@@ -89,7 +89,7 @@ class Trigger extends Renderable {
89
89
  getTriggerSettings() {
90
90
  // Lookup for the container instance
91
91
  if (typeof(this.triggerSettings.container) === "string") {
92
- this.triggerSettings.container = world.getChildByName(this.triggerSettings.container)[0];
92
+ this.triggerSettings.container = game.world.getChildByName(this.triggerSettings.container)[0];
93
93
  }
94
94
  return this.triggerSettings;
95
95
  }
@@ -99,7 +99,7 @@ class Trigger extends Renderable {
99
99
  */
100
100
  onFadeComplete() {
101
101
  level.load(this.gotolevel, this.getTriggerSettings());
102
- viewport.fadeOut(this.fade, this.duration);
102
+ game.viewport.fadeOut(this.fade, this.duration);
103
103
  }
104
104
 
105
105
  /**
@@ -118,7 +118,7 @@ class Trigger extends Renderable {
118
118
  if (this.fade && this.duration) {
119
119
  if (!this.fading) {
120
120
  this.fading = true;
121
- viewport.fadeIn(this.fade, this.duration,
121
+ game.viewport.fadeIn(this.fade, this.duration,
122
122
  this.onFadeComplete.bind(this));
123
123
  }
124
124
  } else {
@@ -1,5 +1,5 @@
1
1
  import { renderer } from "./../video/video.js";
2
- import * as game from "./../game.js";
2
+ import game from "./../game.js";
3
3
  import Camera2d from "./../camera/camera2d.js";
4
4
  import Color from "./../math/color.js";
5
5
 
@@ -1,7 +1,8 @@
1
1
  import { pauseTrack, resumeTrack } from "./../audio/audio.js";
2
2
  import * as fctUtil from "./../utils/function.js";
3
3
  import * as event from "./../system/event.js";
4
- import * as game from "./../game.js";
4
+ import game from "./../game.js";
5
+ import * as device from "./../system/device.js";
5
6
  import Stage from "./../state/stage.js";
6
7
  import DefaultLoadingScreen from "./../loader/loadingscreen.js";
7
8
 
@@ -140,14 +141,60 @@ event.on(event.BOOT, () => {
140
141
  event.on(event.VIDEO_INIT, () => {
141
142
  state.change(state.DEFAULT, true);
142
143
  });
143
- });
144
144
 
145
+ if (typeof globalThis.addEventListener === "function") {
146
+ // set pause/stop action on losing focus
147
+ globalThis.addEventListener("blur", () => {
148
+ if (device.stopOnBlur) {
149
+ state.stop(true);
150
+ }
151
+ if (device.pauseOnBlur) {
152
+ state.pause(true);
153
+ }
154
+ }, false);
155
+ // set restart/resume action on gaining focus
156
+ globalThis.addEventListener("focus", () => {
157
+ if (device.stopOnBlur) {
158
+ state.restart(true);
159
+ }
160
+ if (device.resumeOnFocus) {
161
+ state.resume(true);
162
+ }
163
+ // force focus if autofocus is on
164
+ if (device.autoFocus) {
165
+ device.focus();
166
+ }
167
+ }, false);
168
+ }
169
+
170
+ if (typeof globalThis.document !== "undefined") {
171
+ if (typeof globalThis.document.addEventListener === "function") {
172
+ // register on the visibilitychange event if supported
173
+ globalThis.document.addEventListener("visibilitychange", () => {
174
+ if (globalThis.document.visibilityState === "visible") {
175
+ if (device.stopOnBlur) {
176
+ state.restart(true);
177
+ }
178
+ if (device.resumeOnFocus) {
179
+ state.resume(true);
180
+ }
181
+ } else {
182
+ if (device.stopOnBlur) {
183
+ state.stop(true);
184
+ }
185
+ if (device.pauseOnBlur) {
186
+ state.pause(true);
187
+ }
188
+ }
189
+ }, false );
190
+ }
191
+ }
192
+ });
145
193
 
146
194
  /**
147
195
  * a State Manager (state machine)
148
196
  * @namespace state
149
197
  */
150
-
151
198
  var state = {
152
199
 
153
200
  /**