melonjs 10.12.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/README.md +6 -6
- package/dist/melonjs.js +22651 -22529
- package/dist/melonjs.min.js +5 -6
- package/dist/melonjs.module.d.ts +195 -195
- package/dist/melonjs.module.js +22107 -21977
- package/package.json +14 -14
- package/src/application/application.js +231 -0
- package/src/audio/audio.js +13 -7
- package/src/camera/camera2d.js +6 -6
- package/src/game.js +9 -232
- package/src/index.js +3 -3
- package/src/input/keyboard.js +2 -2
- package/src/input/pointer.js +4 -5
- package/src/input/pointerevent.js +8 -8
- package/src/lang/deprecated.js +0 -29
- package/src/level/level.js +2 -2
- package/src/level/tiled/TMXLayer.js +2 -2
- package/src/level/tiled/TMXTileMap.js +3 -3
- package/src/loader/loader.js +64 -28
- package/src/loader/loadingscreen.js +28 -115
- package/src/loader/melonjs_logo.png +0 -0
- package/src/physics/body.js +27 -51
- package/src/physics/detector.js +3 -3
- package/src/physics/quadtree.js +58 -29
- package/src/physics/world.js +32 -3
- package/src/polyfill/index.js +4 -0
- package/src/renderable/container.js +2 -2
- package/src/renderable/imagelayer.js +8 -8
- package/src/renderable/light2d.js +40 -11
- package/src/renderable/trigger.js +4 -4
- package/src/state/stage.js +1 -1
- package/src/state/state.js +50 -3
- package/src/system/device.js +808 -1039
- package/src/system/dom.js +69 -0
- package/src/system/event.js +13 -1
- package/src/system/platform.js +32 -0
- package/src/system/save.js +23 -14
- package/src/system/timer.js +12 -35
- package/src/text/bitmaptext.js +1 -2
- package/src/text/text.js +10 -14
- package/src/text/textmetrics.js +1 -2
- package/src/tweens/tween.js +6 -6
- package/src/utils/string.js +13 -24
- package/src/video/canvas/canvas_renderer.js +3 -2
- package/src/video/renderer.js +2 -2
- package/src/video/texture/canvas_texture.js +36 -2
- package/src/video/video.js +15 -9
- package/src/video/webgl/glshader.js +1 -1
- package/src/video/webgl/webgl_renderer.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "melonjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"description": "melonJS Game Engine",
|
|
5
5
|
"homepage": "http://www.melonjs.org/",
|
|
6
6
|
"keywords": [
|
|
@@ -31,13 +31,10 @@
|
|
|
31
31
|
"url": "https://github.com/melonjs/melonJS/issues"
|
|
32
32
|
},
|
|
33
33
|
"license": "MIT",
|
|
34
|
-
"author":
|
|
35
|
-
"name": "Olivier Biot",
|
|
36
|
-
"email": "contact@melonjs.org"
|
|
37
|
-
},
|
|
34
|
+
"author": "Olivier Biot (AltByte Pte Ltd)",
|
|
38
35
|
"funding": "https://github.com/sponsors/melonjs",
|
|
39
36
|
"engines": {
|
|
40
|
-
"node": ">=
|
|
37
|
+
"node": ">= 16"
|
|
41
38
|
},
|
|
42
39
|
"main": "dist/melonjs.module.js",
|
|
43
40
|
"type": "module",
|
|
@@ -55,21 +52,22 @@
|
|
|
55
52
|
],
|
|
56
53
|
"dependencies": {
|
|
57
54
|
"@teppeis/multimaps": "^2.0.0",
|
|
58
|
-
"core-js": "^3.23.
|
|
59
|
-
"earcut": "2.2.
|
|
55
|
+
"core-js": "^3.23.5",
|
|
56
|
+
"earcut": "2.2.4",
|
|
60
57
|
"eventemitter3": "^4.0.7",
|
|
61
58
|
"howler": "2.2.3"
|
|
62
59
|
},
|
|
63
60
|
"devDependencies": {
|
|
64
61
|
"@melonjs/webdoc-theme": "^1.1.1",
|
|
65
62
|
"@rollup/plugin-buble": "^0.21.3",
|
|
66
|
-
"@rollup/plugin-commonjs": "^22.0.
|
|
63
|
+
"@rollup/plugin-commonjs": "^22.0.1",
|
|
64
|
+
"@rollup/plugin-image": "^2.1.1",
|
|
67
65
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
|
68
66
|
"@rollup/plugin-replace": "^4.0.0",
|
|
69
67
|
"@types/offscreencanvas": "^2019.7.0",
|
|
70
|
-
"@webdoc/cli": "^
|
|
68
|
+
"@webdoc/cli": "^2.0.0",
|
|
71
69
|
"del-cli": "^4.0.1",
|
|
72
|
-
"eslint": "^8.
|
|
70
|
+
"eslint": "^8.20.0",
|
|
73
71
|
"jasmine-core": "^4.2.0",
|
|
74
72
|
"karma": "^6.4.0",
|
|
75
73
|
"karma-chrome-launcher": "^3.1.1",
|
|
@@ -77,10 +75,10 @@
|
|
|
77
75
|
"karma-html-detailed-reporter": "^2.1.0",
|
|
78
76
|
"karma-jasmine": "^5.1.0",
|
|
79
77
|
"karma-nyan-reporter": "0.2.5",
|
|
80
|
-
"rollup": "^2.
|
|
78
|
+
"rollup": "^2.77.0",
|
|
81
79
|
"rollup-plugin-bundle-size": "^1.0.3",
|
|
82
80
|
"rollup-plugin-string": "^3.0.0",
|
|
83
|
-
"terser": "^5.14.
|
|
81
|
+
"terser": "^5.14.2",
|
|
84
82
|
"typescript": "^4.7.4"
|
|
85
83
|
},
|
|
86
84
|
"scripts": {
|
|
@@ -90,7 +88,9 @@
|
|
|
90
88
|
"lint": "eslint src rollup.config.js",
|
|
91
89
|
"test": "npm run test-node && karma start tests/karma.conf.cjs",
|
|
92
90
|
"test-node": "node build/melonjs.module.js",
|
|
93
|
-
"doc": "mkdirp docs && webdoc --quiet --site-root docs -R README.md",
|
|
91
|
+
"doc-prod": "mkdirp docs && webdoc --quiet --site-root melonJS/docs -R README.md",
|
|
92
|
+
"doc-local": "mkdirp docs && webdoc --quiet --site-root /docs -R README.md",
|
|
93
|
+
"doc": "npm run doc-prod",
|
|
94
94
|
"serve": "python3 -m http.server",
|
|
95
95
|
"prepublishOnly": "npm run dist && npm run test",
|
|
96
96
|
"clean": "del-cli --force build/*.js dist/*.js dist/*.d.ts docs src/**/*.d.ts",
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { renderer } from "./../video/video.js";
|
|
2
|
+
import * as event from "./../system/event.js";
|
|
3
|
+
import timer from "./../system/timer.js";
|
|
4
|
+
import state from "./../state/state.js";
|
|
5
|
+
import World from "./../physics/world.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @classdesc
|
|
9
|
+
* An Application represents a single melonJS game.
|
|
10
|
+
* An Application is responsible for updating (each frame) all the related object status and draw them.
|
|
11
|
+
* @see game
|
|
12
|
+
*/
|
|
13
|
+
class Application {
|
|
14
|
+
constructor() {
|
|
15
|
+
/**
|
|
16
|
+
* a reference to the current active stage "default" camera
|
|
17
|
+
* @public
|
|
18
|
+
* @type {Camera2d}
|
|
19
|
+
*/
|
|
20
|
+
this.viewport = null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* a reference to the game world, <br>
|
|
24
|
+
* a world is a virtual environment containing all the game objects
|
|
25
|
+
* @public
|
|
26
|
+
* @type {World}
|
|
27
|
+
*/
|
|
28
|
+
this.world = null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* when true, all objects will be added under the root world container.<br>
|
|
32
|
+
* When false, a `me.Container` object will be created for each corresponding groups
|
|
33
|
+
* @public
|
|
34
|
+
* @type {boolean}
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
this.mergeGroup = true;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Specify the property to be used when sorting renderables.
|
|
41
|
+
* Accepted values : "x", "y", "z"
|
|
42
|
+
* @public
|
|
43
|
+
* @type {string}
|
|
44
|
+
* @default "z"
|
|
45
|
+
*/
|
|
46
|
+
this.sortOn = "z";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Last time the game update loop was executed. <br>
|
|
50
|
+
* Use this value to implement frame prediction in drawing events,
|
|
51
|
+
* for creating smooth motion while running game update logic at
|
|
52
|
+
* a lower fps.
|
|
53
|
+
* @public
|
|
54
|
+
* @type {DOMHighResTimeStamp}
|
|
55
|
+
* @name lastUpdate
|
|
56
|
+
* @memberof Application
|
|
57
|
+
*/
|
|
58
|
+
this.lastUpdate = 0;
|
|
59
|
+
|
|
60
|
+
// to know when we have to refresh the display
|
|
61
|
+
this.isDirty = true;
|
|
62
|
+
|
|
63
|
+
// always refresh the display when updatesPerSecond are lower than fps
|
|
64
|
+
this.isAlwaysDirty = false;
|
|
65
|
+
|
|
66
|
+
// frame counter for frameSkipping
|
|
67
|
+
// reset the frame counter
|
|
68
|
+
this.frameCounter = 0;
|
|
69
|
+
this.frameRate = 1;
|
|
70
|
+
|
|
71
|
+
// time accumulation for multiple update calls
|
|
72
|
+
this.accumulator = 0.0;
|
|
73
|
+
this.accumulatorMax = 0.0;
|
|
74
|
+
this.accumulatorUpdateDelta = 0;
|
|
75
|
+
|
|
76
|
+
// min update step size
|
|
77
|
+
this.stepSize = 1000 / 60;
|
|
78
|
+
this.updateDelta = 0;
|
|
79
|
+
this.lastUpdateStart = null;
|
|
80
|
+
this.updateAverageDelta = 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* init the game instance (create a physic world, update starting time, etc..)
|
|
85
|
+
*/
|
|
86
|
+
init() {
|
|
87
|
+
// create a new physic world
|
|
88
|
+
this.world = new World();
|
|
89
|
+
// set the reference to this application instance
|
|
90
|
+
this.world.app = this;
|
|
91
|
+
this.lastUpdate = globalThis.performance.now();
|
|
92
|
+
event.emit(event.GAME_INIT, this);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* reset the game Object manager
|
|
97
|
+
* destroy all current objects
|
|
98
|
+
*/
|
|
99
|
+
reset() {
|
|
100
|
+
// point to the current active stage "default" camera
|
|
101
|
+
var current = state.get();
|
|
102
|
+
if (typeof current !== "undefined") {
|
|
103
|
+
this.viewport = current.cameras.get("default");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// publish reset notification
|
|
107
|
+
event.emit(event.GAME_RESET);
|
|
108
|
+
|
|
109
|
+
// Refresh internal variables for framerate limiting
|
|
110
|
+
this.updateFrameRate();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Fired when a level is fully loaded and all renderable instantiated. <br>
|
|
115
|
+
* Additionnaly the level id will also be passed to the called function.
|
|
116
|
+
* @example
|
|
117
|
+
* // call myFunction () everytime a level is loaded
|
|
118
|
+
* me.game.onLevelLoaded = this.myFunction.bind(this);
|
|
119
|
+
*/
|
|
120
|
+
onLevelLoaded() {};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Update the renderer framerate using the system config variables.
|
|
124
|
+
* @see timer.maxfps
|
|
125
|
+
* @see World.fps
|
|
126
|
+
*/
|
|
127
|
+
updateFrameRate() {
|
|
128
|
+
// reset the frame counter
|
|
129
|
+
this.frameCounter = 0;
|
|
130
|
+
this.frameRate = ~~(0.5 + 60 / timer.maxfps);
|
|
131
|
+
|
|
132
|
+
// set step size based on the updatesPerSecond
|
|
133
|
+
this.stepSize = (1000 / this.world.fps);
|
|
134
|
+
this.accumulator = 0.0;
|
|
135
|
+
this.accumulatorMax = this.stepSize * 10;
|
|
136
|
+
|
|
137
|
+
// display should always re-draw when update speed doesn't match fps
|
|
138
|
+
// this means the user intends to write position prediction drawing logic
|
|
139
|
+
this.isAlwaysDirty = (timer.maxfps > this.world.fps);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Returns the parent container of the specified Child in the game world
|
|
144
|
+
* @param {Renderable} child
|
|
145
|
+
* @returns {Container}
|
|
146
|
+
*/
|
|
147
|
+
getParentContainer(child) {
|
|
148
|
+
return child.ancestor;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* force the redraw (not update) of all objects
|
|
153
|
+
*/
|
|
154
|
+
repaint() {
|
|
155
|
+
this.isDirty = true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* update all objects related to this game active scene/stage
|
|
160
|
+
* @param {number} time current timestamp as provided by the RAF callback
|
|
161
|
+
* @param {Stage} stage the current stage
|
|
162
|
+
*/
|
|
163
|
+
update(time, stage) {
|
|
164
|
+
// handle frame skipping if required
|
|
165
|
+
if ((++this.frameCounter % this.frameRate) === 0) {
|
|
166
|
+
// reset the frame counter
|
|
167
|
+
this.frameCounter = 0;
|
|
168
|
+
|
|
169
|
+
// publish notification
|
|
170
|
+
event.emit(event.GAME_BEFORE_UPDATE, time);
|
|
171
|
+
|
|
172
|
+
this.accumulator += timer.getDelta();
|
|
173
|
+
this.accumulator = Math.min(this.accumulator, this.accumulatorMax);
|
|
174
|
+
|
|
175
|
+
this.updateDelta = (timer.interpolation) ? timer.getDelta() : this.stepSize;
|
|
176
|
+
this.accumulatorUpdateDelta = (timer.interpolation) ? this.updateDelta : Math.max(this.updateDelta, this.updateAverageDelta);
|
|
177
|
+
|
|
178
|
+
while (this.accumulator >= this.accumulatorUpdateDelta || timer.interpolation) {
|
|
179
|
+
this.lastUpdateStart = globalThis.performance.now();
|
|
180
|
+
|
|
181
|
+
// game update event
|
|
182
|
+
if (state.isPaused() !== true) {
|
|
183
|
+
event.emit(event.GAME_UPDATE, time);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// update all objects (and pass the elapsed time since last frame)
|
|
187
|
+
this.isDirty = stage.update(this.updateDelta) || this.isDirty;
|
|
188
|
+
|
|
189
|
+
this.lastUpdate = globalThis.performance.now();
|
|
190
|
+
this.updateAverageDelta = this.lastUpdate - this.lastUpdateStart;
|
|
191
|
+
|
|
192
|
+
this.accumulator -= this.accumulatorUpdateDelta;
|
|
193
|
+
if (timer.interpolation) {
|
|
194
|
+
this.accumulator = 0;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// publish notification
|
|
200
|
+
event.emit(event.GAME_AFTER_UPDATE, this.lastUpdate);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* draw the active scene/stage associated to this game
|
|
206
|
+
* @param {Stage} stage the current stage
|
|
207
|
+
*/
|
|
208
|
+
draw(stage) {
|
|
209
|
+
if (renderer.isContextValid === true && (this.isDirty || this.isAlwaysDirty)) {
|
|
210
|
+
// publish notification
|
|
211
|
+
event.emit(event.GAME_BEFORE_DRAW, globalThis.performance.now());
|
|
212
|
+
|
|
213
|
+
// prepare renderer to draw a new frame
|
|
214
|
+
renderer.clear();
|
|
215
|
+
|
|
216
|
+
// render the stage
|
|
217
|
+
stage.draw(renderer);
|
|
218
|
+
|
|
219
|
+
// set back to flag
|
|
220
|
+
this.isDirty = false;
|
|
221
|
+
|
|
222
|
+
// flush/render our frame
|
|
223
|
+
renderer.flush();
|
|
224
|
+
|
|
225
|
+
// publish notification
|
|
226
|
+
event.emit(event.GAME_AFTER_DRAW, globalThis.performance.now());
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export default Application;
|
package/src/audio/audio.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {Howl, Howler} from "howler";
|
|
3
3
|
import {clamp} from "./../math/math.js";
|
|
4
4
|
import loader from "./../loader/loader.js";
|
|
5
|
+
import { isDataUrl } from "./../utils/string.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @namespace audio
|
|
@@ -11,31 +12,31 @@ import loader from "./../loader/loader.js";
|
|
|
11
12
|
* audio channel list
|
|
12
13
|
* @ignore
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
+
let audioTracks = {};
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* current active track
|
|
18
19
|
* @ignore
|
|
19
20
|
*/
|
|
20
|
-
|
|
21
|
+
let current_track_id = null;
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* error retry counter
|
|
24
25
|
* @ignore
|
|
25
26
|
*/
|
|
26
|
-
|
|
27
|
+
let retry_counter = 0;
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* list of active audio formats
|
|
30
31
|
* @ignore
|
|
31
32
|
*/
|
|
32
|
-
|
|
33
|
+
let audioExts = [];
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* event listener callback on load error
|
|
36
37
|
* @ignore
|
|
37
38
|
*/
|
|
38
|
-
|
|
39
|
+
let soundLoadError = function (sound_name, onerror_cb) {
|
|
39
40
|
// check the retry counter
|
|
40
41
|
if (retry_counter++ > 3) {
|
|
41
42
|
// something went wrong
|
|
@@ -149,9 +150,14 @@ export function load(sound, html5, onload_cb, onerror_cb) {
|
|
|
149
150
|
if (audioExts.length === 0) {
|
|
150
151
|
throw new Error("target audio extension(s) should be set through me.audio.init() before calling the preloader.");
|
|
151
152
|
}
|
|
152
|
-
|
|
153
|
-
urls.push(sound.src
|
|
153
|
+
if (isDataUrl(sound.src) === true) {
|
|
154
|
+
urls.push(sound.src);
|
|
155
|
+
} else {
|
|
156
|
+
for (var i = 0; i < audioExts.length; i++) {
|
|
157
|
+
urls.push(sound.src + sound.name + "." + audioExts[i] + loader.nocache);
|
|
158
|
+
}
|
|
154
159
|
}
|
|
160
|
+
|
|
155
161
|
audioTracks[sound.name] = new Howl({
|
|
156
162
|
src : urls,
|
|
157
163
|
volume : Howler.volume(),
|
package/src/camera/camera2d.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as event from "./../system/event.js";
|
|
|
10
10
|
import pool from "./../system/pooling.js";
|
|
11
11
|
import Renderable from "./../renderable/renderable.js";
|
|
12
12
|
import {clamp, toBeCloseTo} from "./../math/math.js";
|
|
13
|
-
import
|
|
13
|
+
import game from "./../game.js";
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
// some ref shortcut
|
|
@@ -285,7 +285,7 @@ class Camera2d extends Renderable {
|
|
|
285
285
|
this._updateProjectionMatrix();
|
|
286
286
|
|
|
287
287
|
// publish the viewport resize event
|
|
288
|
-
event.emit(event.VIEWPORT_ONRESIZE, this.width, this.height);
|
|
288
|
+
event.emit(event.VIEWPORT_ONRESIZE, this.width, this.height, this);
|
|
289
289
|
|
|
290
290
|
return this;
|
|
291
291
|
}
|
|
@@ -615,7 +615,7 @@ class Camera2d extends Renderable {
|
|
|
615
615
|
localToWorld(x, y, v) {
|
|
616
616
|
// TODO memoization for one set of coords (multitouch)
|
|
617
617
|
v = v || pool.pull("Vector2d");
|
|
618
|
-
v.set(x, y).add(this.pos).sub(world.pos);
|
|
618
|
+
v.set(x, y).add(this.pos).sub(game.world.pos);
|
|
619
619
|
if (!this.currentTransform.isIdentity()) {
|
|
620
620
|
this.invCurrentTransform.apply(v);
|
|
621
621
|
}
|
|
@@ -639,7 +639,7 @@ class Camera2d extends Renderable {
|
|
|
639
639
|
if (!this.currentTransform.isIdentity()) {
|
|
640
640
|
this.currentTransform.apply(v);
|
|
641
641
|
}
|
|
642
|
-
return v.sub(this.pos).add(world.pos);
|
|
642
|
+
return v.sub(this.pos).add(game.world.pos);
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
/**
|
|
@@ -706,7 +706,7 @@ class Camera2d extends Renderable {
|
|
|
706
706
|
|
|
707
707
|
this.preDraw(renderer);
|
|
708
708
|
|
|
709
|
-
container.preDraw(renderer);
|
|
709
|
+
container.preDraw(renderer, this);
|
|
710
710
|
|
|
711
711
|
// draw all objects,
|
|
712
712
|
// specifying the viewport as the rectangle area to redraw
|
|
@@ -715,7 +715,7 @@ class Camera2d extends Renderable {
|
|
|
715
715
|
// draw the viewport/camera effects
|
|
716
716
|
this.drawFX(renderer);
|
|
717
717
|
|
|
718
|
-
container.postDraw(renderer);
|
|
718
|
+
container.postDraw(renderer, this);
|
|
719
719
|
|
|
720
720
|
this.postDraw(renderer);
|
|
721
721
|
|
package/src/game.js
CHANGED
|
@@ -1,243 +1,20 @@
|
|
|
1
|
-
import { renderer } from "./video/video.js";
|
|
2
1
|
import * as event from "./system/event.js";
|
|
3
|
-
import
|
|
4
|
-
import state from "./state/state.js";
|
|
5
|
-
import World from "./physics/world.js";
|
|
2
|
+
import Application from "./application/application.js";
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
|
-
* game
|
|
9
|
-
* tilemap layers, current viewport, collision map, etc...<br>
|
|
10
|
-
* game is also responsible for updating (each frame) the object status and draw them.
|
|
5
|
+
* game is a default instance of a melonJS Application and represents your current game,
|
|
6
|
+
* it contains all the objects, tilemap layers, current viewport, collision map, etc...<br>
|
|
11
7
|
* @namespace game
|
|
8
|
+
* @see Application
|
|
12
9
|
*/
|
|
13
10
|
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// always refresh the display when updatesPerSecond are lower than fps
|
|
18
|
-
var isAlwaysDirty = false;
|
|
19
|
-
|
|
20
|
-
// frame counter for frameSkipping
|
|
21
|
-
// reset the frame counter
|
|
22
|
-
var frameCounter = 0;
|
|
23
|
-
var frameRate = 1;
|
|
24
|
-
|
|
25
|
-
// time accumulation for multiple update calls
|
|
26
|
-
var accumulator = 0.0;
|
|
27
|
-
var accumulatorMax = 0.0;
|
|
28
|
-
var accumulatorUpdateDelta = 0;
|
|
29
|
-
|
|
30
|
-
// min update step size
|
|
31
|
-
var stepSize = 1000 / 60;
|
|
32
|
-
var updateDelta = 0;
|
|
33
|
-
var lastUpdateStart = null;
|
|
34
|
-
var updateAverageDelta = 0;
|
|
35
|
-
|
|
11
|
+
// create a default melonJS application instance
|
|
12
|
+
let game = new Application();
|
|
36
13
|
|
|
37
14
|
// initialize the game manager on system boot
|
|
38
15
|
event.on(event.BOOT, () => {
|
|
39
|
-
// the
|
|
40
|
-
|
|
41
|
-
// publish init notification
|
|
42
|
-
event.emit(event.GAME_INIT);
|
|
16
|
+
// initialize the default game instance
|
|
17
|
+
game.init();
|
|
43
18
|
});
|
|
44
19
|
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* a reference to the current active stage "default" camera
|
|
48
|
-
* @public
|
|
49
|
-
* @type {Camera2d}
|
|
50
|
-
* @name viewport
|
|
51
|
-
* @memberof game
|
|
52
|
-
*/
|
|
53
|
-
export let viewport;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* a reference to the game world, <br>
|
|
57
|
-
* a world is a virtual environment containing all the game objects
|
|
58
|
-
* @public
|
|
59
|
-
* @type {World}
|
|
60
|
-
* @name world
|
|
61
|
-
* @memberof game
|
|
62
|
-
*/
|
|
63
|
-
export let world;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* when true, all objects will be added under the root world container.<br>
|
|
67
|
-
* When false, a `me.Container` object will be created for each corresponding groups
|
|
68
|
-
* @public
|
|
69
|
-
* @type {boolean}
|
|
70
|
-
* @default true
|
|
71
|
-
* @name mergeGroup
|
|
72
|
-
* @memberof game
|
|
73
|
-
*/
|
|
74
|
-
export let mergeGroup = true;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Specify the property to be used when sorting entities.
|
|
78
|
-
* Accepted values : "x", "y", "z"
|
|
79
|
-
* @public
|
|
80
|
-
* @type {string}
|
|
81
|
-
* @default "z"
|
|
82
|
-
* @name sortOn
|
|
83
|
-
* @memberof game
|
|
84
|
-
*/
|
|
85
|
-
export let sortOn = "z";
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Last time the game update loop was executed. <br>
|
|
89
|
-
* Use this value to implement frame prediction in drawing events,
|
|
90
|
-
* for creating smooth motion while running game update logic at
|
|
91
|
-
* a lower fps.
|
|
92
|
-
* @public
|
|
93
|
-
* @type {DOMHighResTimeStamp}
|
|
94
|
-
* @name lastUpdate
|
|
95
|
-
* @memberof game
|
|
96
|
-
*/
|
|
97
|
-
export let lastUpdate = globalThis.performance.now();
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Fired when a level is fully loaded and all entities instantiated. <br>
|
|
101
|
-
* Additionnaly the level id will also be passed to the called function.
|
|
102
|
-
* @function game.onLevelLoaded
|
|
103
|
-
* @example
|
|
104
|
-
* // call myFunction () everytime a level is loaded
|
|
105
|
-
* me.game.onLevelLoaded = this.myFunction.bind(this);
|
|
106
|
-
*/
|
|
107
|
-
export function onLevelLoaded() {};
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* reset the game Object manager<br>
|
|
111
|
-
* destroy all current objects
|
|
112
|
-
* @function game.reset
|
|
113
|
-
*/
|
|
114
|
-
export function reset () {
|
|
115
|
-
// point to the current active stage "default" camera
|
|
116
|
-
var current = state.current();
|
|
117
|
-
if (typeof current !== "undefined") {
|
|
118
|
-
viewport = current.cameras.get("default");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// publish reset notification
|
|
122
|
-
event.emit(event.GAME_RESET);
|
|
123
|
-
|
|
124
|
-
// Refresh internal variables for framerate limiting
|
|
125
|
-
updateFrameRate();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Update the renderer framerate using the system config variables.
|
|
130
|
-
* @function game.updateFrameRate
|
|
131
|
-
* @see timer.maxfps
|
|
132
|
-
* @see World.fps
|
|
133
|
-
*/
|
|
134
|
-
export function updateFrameRate() {
|
|
135
|
-
// reset the frame counter
|
|
136
|
-
frameCounter = 0;
|
|
137
|
-
frameRate = ~~(0.5 + 60 / timer.maxfps);
|
|
138
|
-
|
|
139
|
-
// set step size based on the updatesPerSecond
|
|
140
|
-
stepSize = (1000 / world.fps);
|
|
141
|
-
accumulator = 0.0;
|
|
142
|
-
accumulatorMax = stepSize * 10;
|
|
143
|
-
|
|
144
|
-
// display should always re-draw when update speed doesn't match fps
|
|
145
|
-
// this means the user intends to write position prediction drawing logic
|
|
146
|
-
isAlwaysDirty = (timer.maxfps > world.fps);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Returns the parent container of the specified Child in the game world
|
|
151
|
-
* @function game.getParentContainer
|
|
152
|
-
* @param {Renderable} child
|
|
153
|
-
* @returns {Container}
|
|
154
|
-
*/
|
|
155
|
-
export function getParentContainer(child) {
|
|
156
|
-
return child.ancestor;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* force the redraw (not update) of all objects
|
|
161
|
-
* @function game.repaint
|
|
162
|
-
*/
|
|
163
|
-
export function repaint() {
|
|
164
|
-
isDirty = true;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* update all objects of the game manager
|
|
170
|
-
* @ignore
|
|
171
|
-
* @function game.update
|
|
172
|
-
* @param {number} time current timestamp as provided by the RAF callback
|
|
173
|
-
* @param {Stage} stage the current stage
|
|
174
|
-
*/
|
|
175
|
-
export function update(time, stage) {
|
|
176
|
-
// handle frame skipping if required
|
|
177
|
-
if ((++frameCounter % frameRate) === 0) {
|
|
178
|
-
// reset the frame counter
|
|
179
|
-
frameCounter = 0;
|
|
180
|
-
|
|
181
|
-
// publish notification
|
|
182
|
-
event.emit(event.GAME_BEFORE_UPDATE, time);
|
|
183
|
-
|
|
184
|
-
accumulator += timer.getDelta();
|
|
185
|
-
accumulator = Math.min(accumulator, accumulatorMax);
|
|
186
|
-
|
|
187
|
-
updateDelta = (timer.interpolation) ? timer.getDelta() : stepSize;
|
|
188
|
-
accumulatorUpdateDelta = (timer.interpolation) ? updateDelta : Math.max(updateDelta, updateAverageDelta);
|
|
189
|
-
|
|
190
|
-
while (accumulator >= accumulatorUpdateDelta || timer.interpolation) {
|
|
191
|
-
lastUpdateStart = globalThis.performance.now();
|
|
192
|
-
|
|
193
|
-
// game update event
|
|
194
|
-
if (state.isPaused() !== true) {
|
|
195
|
-
event.emit(event.GAME_UPDATE, time);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// update all objects (and pass the elapsed time since last frame)
|
|
199
|
-
isDirty = stage.update(updateDelta) || isDirty;
|
|
200
|
-
|
|
201
|
-
lastUpdate = globalThis.performance.now();
|
|
202
|
-
updateAverageDelta = lastUpdate - lastUpdateStart;
|
|
203
|
-
|
|
204
|
-
accumulator -= accumulatorUpdateDelta;
|
|
205
|
-
if (timer.interpolation) {
|
|
206
|
-
accumulator = 0;
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// publish notification
|
|
212
|
-
event.emit(event.GAME_AFTER_UPDATE, lastUpdate);
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* draw the current scene/stage
|
|
218
|
-
* @function game.draw
|
|
219
|
-
* @ignore
|
|
220
|
-
* @param {Stage} stage the current stage
|
|
221
|
-
*/
|
|
222
|
-
export function draw(stage) {
|
|
223
|
-
|
|
224
|
-
if (renderer.isContextValid === true && (isDirty || isAlwaysDirty)) {
|
|
225
|
-
// publish notification
|
|
226
|
-
event.emit(event.GAME_BEFORE_DRAW, globalThis.performance.now());
|
|
227
|
-
|
|
228
|
-
// prepare renderer to draw a new frame
|
|
229
|
-
renderer.clear();
|
|
230
|
-
|
|
231
|
-
// render the stage
|
|
232
|
-
stage.draw(renderer);
|
|
233
|
-
|
|
234
|
-
// set back to flag
|
|
235
|
-
isDirty = false;
|
|
236
|
-
|
|
237
|
-
// flush/render our frame
|
|
238
|
-
renderer.flush();
|
|
239
|
-
|
|
240
|
-
// publish notification
|
|
241
|
-
event.emit(event.GAME_AFTER_DRAW, globalThis.performance.now());
|
|
242
|
-
}
|
|
243
|
-
};
|
|
20
|
+
export default game;
|
package/src/index.js
CHANGED
|
@@ -4,9 +4,9 @@ import "./polyfill/index.js";
|
|
|
4
4
|
// utility classes
|
|
5
5
|
import * as audio from "./audio/audio.js";
|
|
6
6
|
import collision from "./physics/collision.js";
|
|
7
|
-
import device from "./system/device.js";
|
|
8
7
|
import * as event from "./system/event.js";
|
|
9
|
-
import * as
|
|
8
|
+
import * as device from "./system/device.js";
|
|
9
|
+
import game from "./game.js";
|
|
10
10
|
import loader from "./loader/loader.js";
|
|
11
11
|
import * as Math from "./math/math.js";
|
|
12
12
|
import utils from "./utils/utils.js";
|
|
@@ -273,7 +273,7 @@ export function boot() {
|
|
|
273
273
|
|
|
274
274
|
/// if auto init is disable and this function was called manually
|
|
275
275
|
if (skipAutoInit === true) {
|
|
276
|
-
|
|
276
|
+
event.emit(event.DOM_READY);
|
|
277
277
|
}
|
|
278
278
|
};
|
|
279
279
|
|