minimojs 1.0.0-alpha.4 → 1.0.0-alpha.40
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/README.md +58 -20
- package/dist/internal/AnimationSystem.js +345 -0
- package/dist/internal/AssetSystem.d.ts +6 -0
- package/dist/internal/AssetSystem.js +281 -0
- package/dist/internal/BackgroundSystem.d.ts +1 -0
- package/dist/internal/BackgroundSystem.js +26 -0
- package/dist/internal/CanvasSystem.d.ts +1 -0
- package/dist/internal/CanvasSystem.js +51 -0
- package/dist/internal/DesignNamingCheck.d.ts +1 -0
- package/dist/internal/DesignNamingCheck.js +7 -0
- package/dist/internal/ExplosionSystem.d.ts +1 -0
- package/dist/internal/ExplosionSystem.js +540 -0
- package/dist/internal/InputSystem.d.ts +1 -0
- package/dist/internal/InputSystem.js +292 -0
- package/dist/internal/LoopSystem.d.ts +1 -0
- package/dist/internal/LoopSystem.js +61 -0
- package/dist/internal/PhysicsSystem.d.ts +1 -0
- package/dist/internal/PhysicsSystem.js +174 -0
- package/dist/internal/RenderSystem.d.ts +1 -0
- package/dist/internal/RenderSystem.js +1021 -0
- package/dist/internal/SoundSystem.d.ts +39 -0
- package/dist/internal/SoundSystem.js +292 -0
- package/dist/internal/SpriteSystem.d.ts +1 -0
- package/dist/internal/SpriteSystem.js +32 -0
- package/dist/internal/TextSystem.d.ts +1 -0
- package/dist/internal/TextSystem.js +16 -0
- package/dist/internal/TimerSystem.d.ts +1 -0
- package/dist/internal/TimerSystem.js +43 -0
- package/dist/internal/TrailSystem.d.ts +1 -0
- package/dist/internal/TrailSystem.js +116 -0
- package/dist/internal/TransitionSystem.d.ts +1 -0
- package/dist/internal/TransitionSystem.js +74 -0
- package/dist/internal/arcade-racer/ArcadeRacerCollisionSystem.d.ts +1 -0
- package/dist/internal/arcade-racer/ArcadeRacerCollisionSystem.js +198 -0
- package/dist/internal/arcade-racer/ArcadeRacerLaneSystem.d.ts +1 -0
- package/dist/internal/arcade-racer/ArcadeRacerLaneSystem.js +120 -0
- package/dist/internal/arcade-racer/ArcadeRacerRenderSystem.d.ts +1 -0
- package/dist/internal/arcade-racer/ArcadeRacerRenderSystem.js +599 -0
- package/dist/internal/arcade-racer/ArcadeRacerRoadSprite.d.ts +1 -0
- package/dist/internal/arcade-racer/ArcadeRacerRoadSprite.js +13 -0
- package/dist/internal/arcade-racer/ArcadeRacerTrackSystem.d.ts +1 -0
- package/dist/internal/arcade-racer/ArcadeRacerTrackSystem.js +447 -0
- package/dist/minimo-ambient.d.ts +299 -0
- package/dist/minimo-ambient.js +1445 -0
- package/dist/minimo-arcaderacer.d.ts +1431 -0
- package/dist/minimo-arcaderacer.js +2060 -0
- package/dist/minimo-cards.d.ts +359 -0
- package/dist/minimo-cards.js +819 -0
- package/dist/minimo-dice.d.ts +206 -0
- package/dist/minimo-dice.js +719 -0
- package/dist/minimo-fire.d.ts +282 -0
- package/dist/minimo-fire.js +930 -0
- package/dist/minimo-format.d.ts +632 -0
- package/dist/minimo-format.js +669 -0
- package/dist/minimo-leaderboard.d.ts +199 -0
- package/dist/minimo-leaderboard.js +566 -0
- package/dist/minimo-multiplayer.d.ts +374 -0
- package/dist/minimo-multiplayer.js +1310 -0
- package/dist/minimo.d.ts +1690 -233
- package/dist/minimo.js +2587 -892
- package/package.json +10 -3
- package/dist/animations.js +0 -30
- package/dist/audio.js +0 -17
- package/dist/game.js +0 -1105
- package/dist/input.js +0 -185
- package/dist/internal-types.js +0 -4
- package/dist/physics.js +0 -10
- package/dist/render.js +0 -75
- package/dist/sprite.js +0 -149
- package/dist/timers.js +0 -23
- /package/dist/{pointer-info.js → internal/AnimationSystem.d.ts} +0 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
export class InputSystem {
|
|
3
|
+
constructor(
|
|
4
|
+
/** @internal */ game,
|
|
5
|
+
/** @internal */ canvas) {
|
|
6
|
+
this.game = game;
|
|
7
|
+
this.canvas = canvas;
|
|
8
|
+
/** @internal */
|
|
9
|
+
this._keysDown = new Set();
|
|
10
|
+
/** @internal */
|
|
11
|
+
this._keysPressed = new Set();
|
|
12
|
+
/** @internal */
|
|
13
|
+
this._pointerDown = false;
|
|
14
|
+
/** @internal */
|
|
15
|
+
this._pointerPressed = false;
|
|
16
|
+
/** @internal */
|
|
17
|
+
this._pointerX = 0;
|
|
18
|
+
/** @internal */
|
|
19
|
+
this._pointerY = 0;
|
|
20
|
+
/** @internal */
|
|
21
|
+
this._mouseDown = false;
|
|
22
|
+
/** @internal */
|
|
23
|
+
this._mousePressed = false;
|
|
24
|
+
/** @internal */
|
|
25
|
+
this._mouseX = 0;
|
|
26
|
+
/** @internal */
|
|
27
|
+
this._mouseY = 0;
|
|
28
|
+
/** @internal */
|
|
29
|
+
this._touchPointers = new Map();
|
|
30
|
+
/** @internal */
|
|
31
|
+
this._primaryTouchId = null;
|
|
32
|
+
/**
|
|
33
|
+
* The primary touch after it lifted, kept until the mouse or a new touch
|
|
34
|
+
* takes over, so the pointer stays where the finger left the screen instead
|
|
35
|
+
* of jumping to the mouse's last position.
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
this._releasedTouch = null;
|
|
40
|
+
/** @internal True on the frame the primary touch ended in a `touchcancel`. */
|
|
41
|
+
this._pointerCancelled = false;
|
|
42
|
+
}
|
|
43
|
+
get pointerX() {
|
|
44
|
+
return this._pointerX;
|
|
45
|
+
}
|
|
46
|
+
get pointerY() {
|
|
47
|
+
return this._pointerY;
|
|
48
|
+
}
|
|
49
|
+
bindInputEvents() {
|
|
50
|
+
window.addEventListener("keydown", (e) => {
|
|
51
|
+
if (!this._keysDown.has(e.key)) {
|
|
52
|
+
this._keysPressed.add(e.key);
|
|
53
|
+
}
|
|
54
|
+
this._keysDown.add(e.key);
|
|
55
|
+
});
|
|
56
|
+
window.addEventListener("keyup", (e) => {
|
|
57
|
+
this._keysDown.delete(e.key);
|
|
58
|
+
});
|
|
59
|
+
this.canvas.addEventListener("mousedown", (e) => {
|
|
60
|
+
const p = this.getCanvasPoint(e.clientX, e.clientY);
|
|
61
|
+
this._mouseX = p.x;
|
|
62
|
+
this._mouseY = p.y;
|
|
63
|
+
this._mouseDown = true;
|
|
64
|
+
this._mousePressed = true;
|
|
65
|
+
this._releasedTouch = null;
|
|
66
|
+
this.syncPrimaryPointer();
|
|
67
|
+
});
|
|
68
|
+
this.canvas.addEventListener("mouseup", () => {
|
|
69
|
+
this._mouseDown = false;
|
|
70
|
+
this._releasedTouch = null;
|
|
71
|
+
this.syncPrimaryPointer();
|
|
72
|
+
});
|
|
73
|
+
this.canvas.addEventListener("mousemove", (e) => {
|
|
74
|
+
const p = this.getCanvasPoint(e.clientX, e.clientY);
|
|
75
|
+
this._mouseX = p.x;
|
|
76
|
+
this._mouseY = p.y;
|
|
77
|
+
this._releasedTouch = null;
|
|
78
|
+
this.syncPrimaryPointer();
|
|
79
|
+
});
|
|
80
|
+
this.canvas.addEventListener("touchstart", (e) => {
|
|
81
|
+
for (const touch of Array.from(e.changedTouches)) {
|
|
82
|
+
const p = this.getCanvasPoint(touch.clientX, touch.clientY);
|
|
83
|
+
this._touchPointers.set(touch.identifier, {
|
|
84
|
+
x: p.x,
|
|
85
|
+
y: p.y,
|
|
86
|
+
pressed: true,
|
|
87
|
+
});
|
|
88
|
+
// A finger that touches while none is followed becomes the pointer,
|
|
89
|
+
// even when other fingers are still down.
|
|
90
|
+
if (this._primaryTouchId === null) {
|
|
91
|
+
this._primaryTouchId = touch.identifier;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
this._releasedTouch = null;
|
|
95
|
+
this._pointerCancelled = false;
|
|
96
|
+
this.syncPrimaryPointer();
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
}, { passive: false });
|
|
99
|
+
this.canvas.addEventListener("touchend", (e) => {
|
|
100
|
+
this.endTouches(e, false);
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
}, { passive: false });
|
|
103
|
+
this.canvas.addEventListener("touchcancel", (e) => {
|
|
104
|
+
this.endTouches(e, true);
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
}, { passive: false });
|
|
107
|
+
this.canvas.addEventListener("touchmove", (e) => {
|
|
108
|
+
for (const touch of Array.from(e.changedTouches)) {
|
|
109
|
+
const p = this.getCanvasPoint(touch.clientX, touch.clientY);
|
|
110
|
+
const existing = this._touchPointers.get(touch.identifier);
|
|
111
|
+
if (existing) {
|
|
112
|
+
existing.x = p.x;
|
|
113
|
+
existing.y = p.y;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this._touchPointers.set(touch.identifier, {
|
|
117
|
+
x: p.x,
|
|
118
|
+
y: p.y,
|
|
119
|
+
pressed: false,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
this.syncPrimaryPointer();
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
}, { passive: false });
|
|
126
|
+
}
|
|
127
|
+
isKeyDown(key) {
|
|
128
|
+
return this._keysDown.has(key);
|
|
129
|
+
}
|
|
130
|
+
isKeyPressed(key) {
|
|
131
|
+
return this._keysPressed.has(key);
|
|
132
|
+
}
|
|
133
|
+
isPointerDown() {
|
|
134
|
+
return this._pointerDown;
|
|
135
|
+
}
|
|
136
|
+
isPointerPressed() {
|
|
137
|
+
return this._pointerPressed;
|
|
138
|
+
}
|
|
139
|
+
isPointerCancelled() {
|
|
140
|
+
return this._pointerCancelled;
|
|
141
|
+
}
|
|
142
|
+
isPointerDownOverSprite(sprite) {
|
|
143
|
+
if (!sprite)
|
|
144
|
+
return false;
|
|
145
|
+
if (this._mouseDown &&
|
|
146
|
+
this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite)) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
for (const pointer of this._touchPointers.values()) {
|
|
150
|
+
if (this.isScreenPointOverSprite(pointer.x, pointer.y, sprite)) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
isPointerPressedOverSprite(sprite) {
|
|
157
|
+
if (!sprite)
|
|
158
|
+
return false;
|
|
159
|
+
if (this._mousePressed &&
|
|
160
|
+
this.isScreenPointOverSprite(this._mouseX, this._mouseY, sprite)) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
for (const pointer of this._touchPointers.values()) {
|
|
164
|
+
if (pointer.pressed &&
|
|
165
|
+
this.isScreenPointOverSprite(pointer.x, pointer.y, sprite)) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
getPointers() {
|
|
172
|
+
return this.collectPointers();
|
|
173
|
+
}
|
|
174
|
+
getPointersOverSprite(sprite) {
|
|
175
|
+
if (!sprite)
|
|
176
|
+
return [];
|
|
177
|
+
return this.collectPointers().filter((pointer) => this.isScreenPointOverSprite(pointer.x, pointer.y, sprite));
|
|
178
|
+
}
|
|
179
|
+
isMobileDevice() {
|
|
180
|
+
if (typeof navigator === "undefined")
|
|
181
|
+
return false;
|
|
182
|
+
const ua = navigator.userAgent ?? "";
|
|
183
|
+
const mobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(ua);
|
|
184
|
+
const iPadOS = navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
|
|
185
|
+
const coarsePointer = typeof window !== "undefined" &&
|
|
186
|
+
typeof window.matchMedia === "function" &&
|
|
187
|
+
window.matchMedia("(pointer: coarse)").matches;
|
|
188
|
+
return mobileUA || iPadOS || coarsePointer;
|
|
189
|
+
}
|
|
190
|
+
clearFramePressedState() {
|
|
191
|
+
this._keysPressed.clear();
|
|
192
|
+
this._mousePressed = false;
|
|
193
|
+
for (const pointer of this._touchPointers.values()) {
|
|
194
|
+
pointer.pressed = false;
|
|
195
|
+
}
|
|
196
|
+
if (this._releasedTouch)
|
|
197
|
+
this._releasedTouch.pressed = false;
|
|
198
|
+
this._pointerCancelled = false;
|
|
199
|
+
this.syncPrimaryPointer();
|
|
200
|
+
}
|
|
201
|
+
resetPressedState() {
|
|
202
|
+
this.clearFramePressedState();
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Removes the touches an event ended. When the primary one lifts, it is kept
|
|
206
|
+
* as the released touch at the point it lifted, carrying its frame-press flag
|
|
207
|
+
* so a tap that starts and ends between two frames still reads as pressed.
|
|
208
|
+
*
|
|
209
|
+
* @internal
|
|
210
|
+
*/
|
|
211
|
+
endTouches(e, cancelled) {
|
|
212
|
+
for (const touch of Array.from(e.changedTouches)) {
|
|
213
|
+
const pointer = this._touchPointers.get(touch.identifier);
|
|
214
|
+
this._touchPointers.delete(touch.identifier);
|
|
215
|
+
if (this._primaryTouchId !== touch.identifier || !pointer)
|
|
216
|
+
continue;
|
|
217
|
+
this._primaryTouchId = null;
|
|
218
|
+
if (!cancelled) {
|
|
219
|
+
const p = this.getCanvasPoint(touch.clientX, touch.clientY);
|
|
220
|
+
pointer.x = p.x;
|
|
221
|
+
pointer.y = p.y;
|
|
222
|
+
}
|
|
223
|
+
this._releasedTouch = pointer;
|
|
224
|
+
this._pointerCancelled = cancelled;
|
|
225
|
+
}
|
|
226
|
+
this.syncPrimaryPointer();
|
|
227
|
+
}
|
|
228
|
+
/** @internal */
|
|
229
|
+
getCanvasPoint(clientX, clientY) {
|
|
230
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
231
|
+
const scaleX = this.canvas.width / rect.width;
|
|
232
|
+
const scaleY = this.canvas.height / rect.height;
|
|
233
|
+
return {
|
|
234
|
+
x: (clientX - rect.left) * scaleX,
|
|
235
|
+
y: (clientY - rect.top) * scaleY,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/** @internal */
|
|
239
|
+
isScreenPointOverSprite(x, y, sprite) {
|
|
240
|
+
const halfWidth = sprite.displayWidth / 2;
|
|
241
|
+
const halfHeight = sprite.displayHeight / 2;
|
|
242
|
+
const drawX = sprite.ignoreScroll ? sprite.renderX : sprite.renderX - this.game.scrollX;
|
|
243
|
+
const drawY = sprite.ignoreScroll ? sprite.renderY : sprite.renderY - this.game.scrollY;
|
|
244
|
+
return Math.abs(x - drawX) <= halfWidth && Math.abs(y - drawY) <= halfHeight;
|
|
245
|
+
}
|
|
246
|
+
/** @internal */
|
|
247
|
+
collectPointers() {
|
|
248
|
+
const pointers = [];
|
|
249
|
+
if (this._mouseDown) {
|
|
250
|
+
pointers.push({
|
|
251
|
+
id: "mouse",
|
|
252
|
+
kind: "mouse",
|
|
253
|
+
x: this._mouseX,
|
|
254
|
+
y: this._mouseY,
|
|
255
|
+
pressed: this._mousePressed,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
for (const [id, pointer] of this._touchPointers.entries()) {
|
|
259
|
+
pointers.push({
|
|
260
|
+
id,
|
|
261
|
+
kind: "touch",
|
|
262
|
+
x: pointer.x,
|
|
263
|
+
y: pointer.y,
|
|
264
|
+
pressed: pointer.pressed,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
return pointers;
|
|
268
|
+
}
|
|
269
|
+
/** @internal */
|
|
270
|
+
syncPrimaryPointer() {
|
|
271
|
+
if (this._primaryTouchId !== null) {
|
|
272
|
+
const primaryTouch = this._touchPointers.get(this._primaryTouchId);
|
|
273
|
+
if (primaryTouch) {
|
|
274
|
+
this._pointerX = primaryTouch.x;
|
|
275
|
+
this._pointerY = primaryTouch.y;
|
|
276
|
+
this._pointerDown = true;
|
|
277
|
+
this._pointerPressed = primaryTouch.pressed;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
this._primaryTouchId = null;
|
|
281
|
+
}
|
|
282
|
+
// No finger is followed. Fingers still down are not adopted: the pointer
|
|
283
|
+
// reports the followed finger's release, and the next finger to touch
|
|
284
|
+
// is followed instead.
|
|
285
|
+
this._primaryTouchId = null;
|
|
286
|
+
const released = this._releasedTouch;
|
|
287
|
+
this._pointerX = released ? released.x : this._mouseX;
|
|
288
|
+
this._pointerY = released ? released.y : this._mouseY;
|
|
289
|
+
this._pointerDown = this._mouseDown;
|
|
290
|
+
this._pointerPressed = this._mousePressed || (released?.pressed ?? false);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
export class LoopSystem {
|
|
3
|
+
constructor(/** @internal */ onFrame) {
|
|
4
|
+
this.onFrame = onFrame;
|
|
5
|
+
/** @internal */
|
|
6
|
+
this._rafId = null;
|
|
7
|
+
/** @internal */
|
|
8
|
+
this._lastTimestamp = null;
|
|
9
|
+
/** @internal */
|
|
10
|
+
this._running = false;
|
|
11
|
+
/** @internal */
|
|
12
|
+
this._hasCreated = false;
|
|
13
|
+
/** @internal */
|
|
14
|
+
this._onCreate = null;
|
|
15
|
+
/** @internal */
|
|
16
|
+
this.loop = (timestamp) => {
|
|
17
|
+
if (!this._running)
|
|
18
|
+
return;
|
|
19
|
+
if (this._lastTimestamp === null) {
|
|
20
|
+
this._lastTimestamp = timestamp;
|
|
21
|
+
}
|
|
22
|
+
let dt = (timestamp - this._lastTimestamp) / 1000;
|
|
23
|
+
this._lastTimestamp = timestamp;
|
|
24
|
+
if (dt > 0.1)
|
|
25
|
+
dt = 0.1;
|
|
26
|
+
this.onFrame(dt, dt * 1000);
|
|
27
|
+
this._rafId = requestAnimationFrame(this.loop);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
get onCreate() {
|
|
31
|
+
return this._onCreate;
|
|
32
|
+
}
|
|
33
|
+
set onCreate(callback) {
|
|
34
|
+
this._onCreate = callback;
|
|
35
|
+
}
|
|
36
|
+
get isRunning() {
|
|
37
|
+
return this._running;
|
|
38
|
+
}
|
|
39
|
+
start() {
|
|
40
|
+
if (this._running)
|
|
41
|
+
return;
|
|
42
|
+
if (!this._hasCreated) {
|
|
43
|
+
this.invokeCreate();
|
|
44
|
+
}
|
|
45
|
+
this._running = true;
|
|
46
|
+
this._lastTimestamp = null;
|
|
47
|
+
this._rafId = requestAnimationFrame(this.loop);
|
|
48
|
+
}
|
|
49
|
+
stop() {
|
|
50
|
+
this._running = false;
|
|
51
|
+
if (this._rafId !== null) {
|
|
52
|
+
cancelAnimationFrame(this._rafId);
|
|
53
|
+
this._rafId = null;
|
|
54
|
+
}
|
|
55
|
+
this._lastTimestamp = null;
|
|
56
|
+
}
|
|
57
|
+
invokeCreate() {
|
|
58
|
+
this._hasCreated = true;
|
|
59
|
+
this._onCreate?.();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
export class PhysicsSystem {
|
|
3
|
+
constructor() {
|
|
4
|
+
/** @internal */
|
|
5
|
+
this._gravityX = 0;
|
|
6
|
+
/** @internal */
|
|
7
|
+
this._gravityY = 0;
|
|
8
|
+
/** @internal */
|
|
9
|
+
this._enabled = false;
|
|
10
|
+
}
|
|
11
|
+
get gravityX() {
|
|
12
|
+
return this._gravityX;
|
|
13
|
+
}
|
|
14
|
+
set gravityX(value) {
|
|
15
|
+
this._gravityX = value;
|
|
16
|
+
}
|
|
17
|
+
get gravityY() {
|
|
18
|
+
return this._gravityY;
|
|
19
|
+
}
|
|
20
|
+
set gravityY(value) {
|
|
21
|
+
this._gravityY = value;
|
|
22
|
+
}
|
|
23
|
+
get enabled() {
|
|
24
|
+
return this._enabled;
|
|
25
|
+
}
|
|
26
|
+
set enabled(value) {
|
|
27
|
+
this._enabled = value;
|
|
28
|
+
}
|
|
29
|
+
update(sprites, dt) {
|
|
30
|
+
for (const sprite of sprites) {
|
|
31
|
+
if (sprite.isStatic) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (sprite.gravityScale !== 0) {
|
|
35
|
+
sprite.vx += this._gravityX * sprite.gravityScale * dt;
|
|
36
|
+
sprite.vy += this._gravityY * sprite.gravityScale * dt;
|
|
37
|
+
}
|
|
38
|
+
sprite.x += sprite.vx * dt;
|
|
39
|
+
sprite.y += sprite.vy * dt;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
overlap(a, b) {
|
|
43
|
+
const halfWidthA = a.bodyDisplayWidth / 2;
|
|
44
|
+
const halfHeightA = a.bodyDisplayHeight / 2;
|
|
45
|
+
const halfWidthB = b.bodyDisplayWidth / 2;
|
|
46
|
+
const halfHeightB = b.bodyDisplayHeight / 2;
|
|
47
|
+
return (Math.abs(a.bodyCenterX - b.bodyCenterX) < halfWidthA + halfWidthB &&
|
|
48
|
+
Math.abs(a.bodyCenterY - b.bodyCenterY) < halfHeightA + halfHeightB);
|
|
49
|
+
}
|
|
50
|
+
overlapAny(listA, listB) {
|
|
51
|
+
for (const a of listA) {
|
|
52
|
+
for (const b of listB) {
|
|
53
|
+
if (this.overlap(a, b)) {
|
|
54
|
+
return [a, b];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
collide(a, b) {
|
|
61
|
+
this.assertEnabled();
|
|
62
|
+
if (a === b)
|
|
63
|
+
return null;
|
|
64
|
+
const collision = this.getCollisionResolution(a, b);
|
|
65
|
+
if (!collision)
|
|
66
|
+
return null;
|
|
67
|
+
if (!a.isStatic) {
|
|
68
|
+
a.x += collision.separationX;
|
|
69
|
+
a.y += collision.separationY;
|
|
70
|
+
if (collision.axis === "x") {
|
|
71
|
+
a.vx = 0;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
a.vy = 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (!b.isStatic) {
|
|
78
|
+
b.x -= collision.separationX;
|
|
79
|
+
b.y -= collision.separationY;
|
|
80
|
+
if (collision.axis === "x") {
|
|
81
|
+
b.vx = 0;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
b.vy = 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return collision.info;
|
|
88
|
+
}
|
|
89
|
+
collideAny(listA, listB) {
|
|
90
|
+
this.assertEnabled();
|
|
91
|
+
for (const a of listA) {
|
|
92
|
+
for (const b of listB) {
|
|
93
|
+
const info = this.collide(a, b);
|
|
94
|
+
if (info)
|
|
95
|
+
return [a, b, info];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/** @internal */
|
|
101
|
+
assertEnabled() {
|
|
102
|
+
if (!this._enabled) {
|
|
103
|
+
throw new Error("MinimoJS: collide() and collideAny() require game.physics = true.");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** @internal */
|
|
107
|
+
getCollisionResolution(a, b) {
|
|
108
|
+
const halfWidthA = a.bodyDisplayWidth / 2;
|
|
109
|
+
const halfHeightA = a.bodyDisplayHeight / 2;
|
|
110
|
+
const halfWidthB = b.bodyDisplayWidth / 2;
|
|
111
|
+
const halfHeightB = b.bodyDisplayHeight / 2;
|
|
112
|
+
const dx = b.bodyCenterX - a.bodyCenterX;
|
|
113
|
+
const dy = b.bodyCenterY - a.bodyCenterY;
|
|
114
|
+
const overlapX = halfWidthA + halfWidthB - Math.abs(dx);
|
|
115
|
+
const overlapY = halfHeightA + halfHeightB - Math.abs(dy);
|
|
116
|
+
if (overlapX <= 0 || overlapY <= 0) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
let axis;
|
|
120
|
+
if (overlapX < overlapY) {
|
|
121
|
+
axis = "x";
|
|
122
|
+
}
|
|
123
|
+
else if (overlapY < overlapX) {
|
|
124
|
+
axis = "y";
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const relVX = Math.abs(a.vx - b.vx);
|
|
128
|
+
const relVY = Math.abs(a.vy - b.vy);
|
|
129
|
+
axis = relVX > relVY ? "x" : "y";
|
|
130
|
+
}
|
|
131
|
+
const info = {
|
|
132
|
+
left: false,
|
|
133
|
+
right: false,
|
|
134
|
+
top: false,
|
|
135
|
+
bottom: false,
|
|
136
|
+
grounded: false,
|
|
137
|
+
};
|
|
138
|
+
if (axis === "x") {
|
|
139
|
+
if (dx >= 0) {
|
|
140
|
+
info.right = true;
|
|
141
|
+
return {
|
|
142
|
+
info,
|
|
143
|
+
axis,
|
|
144
|
+
separationX: -overlapX,
|
|
145
|
+
separationY: 0,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
info.left = true;
|
|
149
|
+
return {
|
|
150
|
+
info,
|
|
151
|
+
axis,
|
|
152
|
+
separationX: overlapX,
|
|
153
|
+
separationY: 0,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (dy >= 0) {
|
|
157
|
+
info.bottom = true;
|
|
158
|
+
info.grounded = true;
|
|
159
|
+
return {
|
|
160
|
+
info,
|
|
161
|
+
axis,
|
|
162
|
+
separationX: 0,
|
|
163
|
+
separationY: -overlapY,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
info.top = true;
|
|
167
|
+
return {
|
|
168
|
+
info,
|
|
169
|
+
axis,
|
|
170
|
+
separationX: 0,
|
|
171
|
+
separationY: overlapY,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|