minimojs 1.0.0-alpha.10 → 1.0.0-alpha.11
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 +22 -14
- package/dist/internal/RenderSystem.js +22 -1
- package/dist/internal/SpriteSystem.js +3 -1
- package/dist/minimo.d.ts +141 -49
- package/dist/minimo.js +197 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ This README is intentionally high-level. It explains what the project is and how
|
|
|
24
24
|
|
|
25
25
|
## What It Intentionally Avoids
|
|
26
26
|
|
|
27
|
-
-
|
|
27
|
+
- Heavy scene-manager/ECS architecture
|
|
28
28
|
- Heavy physics engine features
|
|
29
29
|
- Image spritesheets and asset pipelines
|
|
30
30
|
- Nested subsystem APIs
|
|
@@ -39,26 +39,32 @@ npm install minimojs
|
|
|
39
39
|
## Quick Start
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
|
-
import { Game, Sprite } from "minimojs";
|
|
42
|
+
import { Game, Sprite, type IScene } from "minimojs";
|
|
43
43
|
|
|
44
44
|
const game = new Game(800, 600);
|
|
45
45
|
game.gravityY = 980;
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
player
|
|
49
|
-
player.y = 300;
|
|
50
|
-
player.gravityScale = 1;
|
|
47
|
+
class DemoScene implements IScene {
|
|
48
|
+
private player: Sprite | null = null;
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
onCreate() {
|
|
51
|
+
this.player = game.add(new Sprite("🐢", 400, 300, 48));
|
|
52
|
+
this.player.gravityScale = 1;
|
|
53
|
+
}
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
};
|
|
55
|
+
onUpdate(dt: number) {
|
|
56
|
+
if (!this.player) return;
|
|
60
57
|
|
|
61
|
-
game.
|
|
58
|
+
if (game.isKeyDown("ArrowLeft")) this.player.vx = -200;
|
|
59
|
+
else if (game.isKeyDown("ArrowRight")) this.player.vx = 200;
|
|
60
|
+
else this.player.vx = 0;
|
|
61
|
+
|
|
62
|
+
if (game.isKeyPressed(" ")) this.player.vy = -600;
|
|
63
|
+
game.drawText("MinimoJS", 10, 10, 16);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
game.start(new DemoScene());
|
|
62
68
|
```
|
|
63
69
|
|
|
64
70
|
Note: `drawText()` uses `"Press Start 2P", monospace`. Load the font in your HTML if you want pixel-font styling.
|
|
@@ -71,6 +77,8 @@ Note: `drawText()` uses `"Press Start 2P", monospace`. Load the font in your HTM
|
|
|
71
77
|
- `examples/super-minimo-bros/`
|
|
72
78
|
- `examples/scale-shift/`
|
|
73
79
|
- `examples/background-desert/`
|
|
80
|
+
- `examples/scene-lab/`
|
|
81
|
+
- `examples/image-sprite-sad-plush/`
|
|
74
82
|
- `examples/animations/`
|
|
75
83
|
|
|
76
84
|
Run locally from the `minimojs` directory:
|
|
@@ -159,7 +159,9 @@ export class RenderSystem {
|
|
|
159
159
|
return cached;
|
|
160
160
|
const surface = this.isTextSprite(sprite)
|
|
161
161
|
? this.createTextSurface(sprite)
|
|
162
|
-
: this.
|
|
162
|
+
: this.isImageSprite(sprite)
|
|
163
|
+
? this.createImageSurface(sprite)
|
|
164
|
+
: this.createEmojiSurface(this.asEmojiSprite(sprite));
|
|
163
165
|
this._surfaceCache.set(cacheKey, surface);
|
|
164
166
|
return surface;
|
|
165
167
|
}
|
|
@@ -254,9 +256,28 @@ export class RenderSystem {
|
|
|
254
256
|
}
|
|
255
257
|
return canvas;
|
|
256
258
|
}
|
|
259
|
+
createImageSurface(sprite) {
|
|
260
|
+
const image = sprite.game?.getImage(sprite.imageKey);
|
|
261
|
+
if (!image) {
|
|
262
|
+
throw new Error(`MinimoJS: Image '${sprite.imageKey}' is not loaded.`);
|
|
263
|
+
}
|
|
264
|
+
const canvas = document.createElement("canvas");
|
|
265
|
+
canvas.width = Math.max(1, image.naturalWidth || image.width);
|
|
266
|
+
canvas.height = Math.max(1, image.naturalHeight || image.height);
|
|
267
|
+
const ctx = canvas.getContext("2d");
|
|
268
|
+
if (!ctx) {
|
|
269
|
+
throw new Error("MinimoJS: Could not acquire an image rendering context.");
|
|
270
|
+
}
|
|
271
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
272
|
+
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
273
|
+
return canvas;
|
|
274
|
+
}
|
|
257
275
|
isTextSprite(sprite) {
|
|
258
276
|
return "text" in sprite && "fontFamily" in sprite && "fontSize" in sprite;
|
|
259
277
|
}
|
|
278
|
+
isImageSprite(sprite) {
|
|
279
|
+
return "imageKey" in sprite && "setTexture" in sprite;
|
|
280
|
+
}
|
|
260
281
|
asEmojiSprite(sprite) {
|
|
261
282
|
if ("sprite" in sprite && "size" in sprite) {
|
|
262
283
|
return sprite;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/** @internal */
|
|
2
2
|
export class SpriteSystem {
|
|
3
|
-
constructor(animationSystem) {
|
|
3
|
+
constructor(animationSystem, game) {
|
|
4
4
|
this.animationSystem = animationSystem;
|
|
5
|
+
this.game = game;
|
|
5
6
|
this._sprites = [];
|
|
6
7
|
}
|
|
7
8
|
add(sprite) {
|
|
9
|
+
sprite._setGame(this.game);
|
|
8
10
|
this._sprites.push(sprite);
|
|
9
11
|
return sprite;
|
|
10
12
|
}
|
package/dist/minimo.d.ts
CHANGED
|
@@ -56,6 +56,25 @@ export interface TrailOptions {
|
|
|
56
56
|
* - `"cover"`: preserves aspect ratio and fully covers the destination, cropping if needed.
|
|
57
57
|
*/
|
|
58
58
|
export type BackgroundFit = "none" | "stretch" | "contain" | "cover";
|
|
59
|
+
/**
|
|
60
|
+
* Minimal scene contract understood by {@link Game.start} and {@link Game.reset}.
|
|
61
|
+
*
|
|
62
|
+
* Scene methods are invoked directly on the scene instance, so class-based
|
|
63
|
+
* scenes keep their expected `this` value.
|
|
64
|
+
*/
|
|
65
|
+
export interface IScene {
|
|
66
|
+
/**
|
|
67
|
+
* Called when the scene is created, before the first frame and again after
|
|
68
|
+
* each {@link Game.reset} that targets this scene.
|
|
69
|
+
*/
|
|
70
|
+
onCreate?(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Called once per frame after timers, animation, and physics updates.
|
|
73
|
+
*
|
|
74
|
+
* @param dt - Delta time in seconds since the previous frame.
|
|
75
|
+
*/
|
|
76
|
+
onUpdate?(dt: number): void;
|
|
77
|
+
}
|
|
59
78
|
/**
|
|
60
79
|
* Base class for all renderable MinimoJS actors.
|
|
61
80
|
*
|
|
@@ -64,6 +83,11 @@ export type BackgroundFit = "none" | "stretch" | "contain" | "cover";
|
|
|
64
83
|
* engine.
|
|
65
84
|
*/
|
|
66
85
|
export declare abstract class BaseSprite {
|
|
86
|
+
protected constructor(game?: Game | null);
|
|
87
|
+
/**
|
|
88
|
+
* Game instance associated with this sprite, if any.
|
|
89
|
+
*/
|
|
90
|
+
get game(): Game | null;
|
|
67
91
|
/**
|
|
68
92
|
* X position in world space (horizontal center of the sprite), in pixels.
|
|
69
93
|
* Positive X points right. Updated each frame by: `x += vx * dt`.
|
|
@@ -233,7 +257,7 @@ export declare class Sprite extends BaseSprite {
|
|
|
233
257
|
* All other properties use their defaults and can be set after construction.
|
|
234
258
|
*
|
|
235
259
|
* @param sprite - The emoji character to render. Must be a single emoji.
|
|
236
|
-
*
|
|
260
|
+
* Use {@link ImageSprite} for preloaded bitmap textures.
|
|
237
261
|
* @example "🔥", "⭐", "🐢", "💣", "👾"
|
|
238
262
|
* @param x - Initial X position in world space (center), in pixels. Default: `0`.
|
|
239
263
|
* @param y - Initial Y position in world space (center), in pixels. Default: `0`.
|
|
@@ -248,9 +272,47 @@ export declare class Sprite extends BaseSprite {
|
|
|
248
272
|
constructor(sprite: string, x?: number, y?: number, size?: number);
|
|
249
273
|
getRenderCacheKey(): string;
|
|
250
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* A renderable sprite backed by a preloaded image asset.
|
|
277
|
+
*
|
|
278
|
+
* Width and height are always resolved from the current texture. To resize the
|
|
279
|
+
* sprite visually, use {@link BaseSprite.scale}.
|
|
280
|
+
*/
|
|
281
|
+
export declare class ImageSprite extends BaseSprite {
|
|
282
|
+
private _imageKey;
|
|
283
|
+
get imageKey(): string;
|
|
284
|
+
get width(): number;
|
|
285
|
+
get height(): number;
|
|
286
|
+
/**
|
|
287
|
+
* Creates a new image-backed sprite.
|
|
288
|
+
*
|
|
289
|
+
* @param game - Game instance used to resolve the texture key.
|
|
290
|
+
* @param imageKey - Preloaded image key previously registered with {@link Game.loadImage}.
|
|
291
|
+
* @param x - Initial X position in world space (center), in pixels. Default: `0`.
|
|
292
|
+
* @param y - Initial Y position in world space (center), in pixels. Default: `0`.
|
|
293
|
+
*/
|
|
294
|
+
constructor(game: Game, imageKey: string, x?: number, y?: number);
|
|
295
|
+
/**
|
|
296
|
+
* Replaces the current texture with another preloaded image.
|
|
297
|
+
*
|
|
298
|
+
* @param imageKey - New preloaded image key to render.
|
|
299
|
+
*/
|
|
300
|
+
setTexture(imageKey: string): void;
|
|
301
|
+
getRenderCacheKey(): string;
|
|
302
|
+
private getResolvedImage;
|
|
303
|
+
private assertTextureAvailable;
|
|
304
|
+
}
|
|
251
305
|
/**
|
|
252
306
|
* A renderable text actor that participates in the same animation/effects
|
|
253
307
|
* pipeline as regular sprites.
|
|
308
|
+
*
|
|
309
|
+
* Use `TextSprite` when the content is primarily text, or when you need text
|
|
310
|
+
* layout features such as wrapping, fixed button sizes, background/border, or
|
|
311
|
+
* text stroke.
|
|
312
|
+
*
|
|
313
|
+
* For controls that are only a single emoji, prefer {@link Sprite}. Emoji-only
|
|
314
|
+
* buttons usually behave better as sprites because they do not need text
|
|
315
|
+
* padding/layout and their bounds match the rendered emoji more directly.
|
|
254
316
|
*/
|
|
255
317
|
export declare class TextSprite extends BaseSprite {
|
|
256
318
|
private static _measurementCanvas;
|
|
@@ -491,6 +553,11 @@ export interface CollisionInfo {
|
|
|
491
553
|
* If you use additional families, register them with {@link Game.requireFont}
|
|
492
554
|
* before calling {@link Game.start}.
|
|
493
555
|
*
|
|
556
|
+
* Prefer {@link TextSprite} for persistent UI text, interactive labels,
|
|
557
|
+
* buttons, fixed-size text boxes, and styled text elements. Keep
|
|
558
|
+
* `drawText()` for simple screen-space overlays such as HUD counters, debug
|
|
559
|
+
* text, or other text that is redrawn every frame.
|
|
560
|
+
*
|
|
494
561
|
* You MUST still declare the fonts yourself in your `index.html`. MinimoJS
|
|
495
562
|
* does NOT download, inject, or manage web fonts for you. If a font is missing,
|
|
496
563
|
* the browser falls back to `monospace`.
|
|
@@ -636,10 +703,10 @@ export interface CollisionInfo {
|
|
|
636
703
|
*
|
|
637
704
|
* ---
|
|
638
705
|
*
|
|
639
|
-
* ## Scene Initialization with `
|
|
706
|
+
* ## Scene Initialization with `IScene`
|
|
640
707
|
*
|
|
641
|
-
* MinimoJS
|
|
642
|
-
*
|
|
708
|
+
* MinimoJS supports simple scene objects via {@link IScene}. The engine calls
|
|
709
|
+
* a scene's `onCreate()`:
|
|
643
710
|
* - Once before the first frame (when {@link Game.start} is called).
|
|
644
711
|
* - Again after each {@link Game.reset}.
|
|
645
712
|
*
|
|
@@ -651,25 +718,30 @@ export interface CollisionInfo {
|
|
|
651
718
|
* - Scroll position (scrollX and scrollY reset to 0)
|
|
652
719
|
* - Per-frame input state (pressed keys/pointer)
|
|
653
720
|
*
|
|
654
|
-
* After clearing, `reset()` calls `onCreate()` so
|
|
655
|
-
*
|
|
721
|
+
* After clearing, `reset()` calls the active scene's `onCreate()` so it can
|
|
722
|
+
* rebuild immediately. Simply re-add sprites and re-register timers.
|
|
656
723
|
*
|
|
657
724
|
* ```ts
|
|
658
|
-
*
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
725
|
+
* class SkullScene implements IScene {
|
|
726
|
+
* onCreate() {
|
|
727
|
+
* const skull = new Sprite("💀", 400, 300, 96);
|
|
728
|
+
* game.add(skull);
|
|
729
|
+
* }
|
|
730
|
+
*
|
|
731
|
+
* onUpdate(dt: number) {
|
|
732
|
+
* // normal per-frame update
|
|
733
|
+
* }
|
|
734
|
+
* }
|
|
735
|
+
*
|
|
736
|
+
* const scene = new SkullScene();
|
|
737
|
+
* game.start(scene); // calls onCreate() once before first frame
|
|
669
738
|
* // later:
|
|
670
739
|
* game.reset(); // clears + calls onCreate() again
|
|
671
740
|
* ```
|
|
672
741
|
*
|
|
742
|
+
* Legacy `game.onCreate` / `game.onUpdate` callbacks still work when no active
|
|
743
|
+
* {@link IScene} is set.
|
|
744
|
+
*
|
|
673
745
|
* ---
|
|
674
746
|
*
|
|
675
747
|
* ## Sprite Lifecycle Ownership
|
|
@@ -686,7 +758,7 @@ export interface CollisionInfo {
|
|
|
686
758
|
* ## Forbidden Features
|
|
687
759
|
*
|
|
688
760
|
* The following do NOT exist in MinimoJS v1. Do NOT attempt to use them:
|
|
689
|
-
* -
|
|
761
|
+
* - Full scene manager architecture (scene stacks, transitions, loaders, etc.)
|
|
690
762
|
* - Entity Component System (ECS)
|
|
691
763
|
* - Physics engine (no Box2D, Matter.js, etc.)
|
|
692
764
|
* - Camera zoom or scale
|
|
@@ -695,7 +767,6 @@ export interface CollisionInfo {
|
|
|
695
767
|
* - Parallax layers
|
|
696
768
|
* - Multiple cameras
|
|
697
769
|
* - Full physics engine (only basic explicit AABB collision helpers are provided)
|
|
698
|
-
* - Image sprites (PNG, SVG, canvas, etc.) as gameplay actors
|
|
699
770
|
* - `setTimeout` or `setInterval`
|
|
700
771
|
*/
|
|
701
772
|
export declare class Game {
|
|
@@ -819,10 +890,10 @@ export declare class Game {
|
|
|
819
890
|
*/
|
|
820
891
|
onPreload: (() => void) | null;
|
|
821
892
|
/**
|
|
822
|
-
*
|
|
893
|
+
* Legacy scene creation callback.
|
|
823
894
|
*
|
|
824
895
|
* Called once before the first frame on {@link Game.start}, and again after
|
|
825
|
-
* each {@link Game.reset}
|
|
896
|
+
* each {@link Game.reset} when no active {@link IScene} is set.
|
|
826
897
|
*
|
|
827
898
|
* @example
|
|
828
899
|
* ```ts
|
|
@@ -837,7 +908,9 @@ export declare class Game {
|
|
|
837
908
|
get onCreate(): (() => void) | null;
|
|
838
909
|
set onCreate(callback: (() => void) | null);
|
|
839
910
|
/**
|
|
840
|
-
*
|
|
911
|
+
* Legacy per-frame callback invoked after physics and timer updates.
|
|
912
|
+
*
|
|
913
|
+
* This is used only when no active {@link IScene} is set.
|
|
841
914
|
*
|
|
842
915
|
* @param dt - Delta time in **seconds** since the last frame.
|
|
843
916
|
* Use this for velocity-based movement: `sprite.x += speed * dt`.
|
|
@@ -852,6 +925,10 @@ export declare class Game {
|
|
|
852
925
|
* ```
|
|
853
926
|
*/
|
|
854
927
|
onUpdate: ((dt: number) => void) | null;
|
|
928
|
+
/**
|
|
929
|
+
* Currently active scene object, if any.
|
|
930
|
+
*/
|
|
931
|
+
get currentScene(): IScene | null;
|
|
855
932
|
/**
|
|
856
933
|
* Creates a new MinimoJS game instance.
|
|
857
934
|
*
|
|
@@ -894,7 +971,7 @@ export declare class Game {
|
|
|
894
971
|
*/
|
|
895
972
|
get pointerY(): number;
|
|
896
973
|
/**
|
|
897
|
-
* Registers a {@link
|
|
974
|
+
* Registers a {@link BaseSprite} (or subclass instance) with the engine.
|
|
898
975
|
*
|
|
899
976
|
* After calling `add`, the sprite is rendered and, if dynamic
|
|
900
977
|
* (`isStatic = false`), receives built-in velocity/gravity integration every
|
|
@@ -903,11 +980,11 @@ export declare class Game {
|
|
|
903
980
|
* **Ownership:** The game instance takes ownership of the sprite from this
|
|
904
981
|
* point forward. It will appear in {@link Game.getSprites} on the same frame.
|
|
905
982
|
*
|
|
906
|
-
* **Subclasses:** Any class that extends {@link
|
|
907
|
-
* The engine stores and processes it as a
|
|
983
|
+
* **Subclasses:** Any class that extends {@link BaseSprite} can be passed here.
|
|
984
|
+
* The engine stores and processes it as a live sprite; your custom properties
|
|
908
985
|
* are preserved on the instance.
|
|
909
986
|
*
|
|
910
|
-
* @param sprite - A {@link
|
|
987
|
+
* @param sprite - A {@link BaseSprite} instance (or subclass) to add.
|
|
911
988
|
* @returns The same sprite instance, for chaining or inline assignment.
|
|
912
989
|
*
|
|
913
990
|
* @example
|
|
@@ -922,7 +999,7 @@ export declare class Game {
|
|
|
922
999
|
* constructor(x: number, y: number) {
|
|
923
1000
|
* super("👾", x, y, 40);
|
|
924
1001
|
* }
|
|
925
|
-
*
|
|
1002
|
+
* }
|
|
926
1003
|
* const enemy = game.add(new Enemy(600, 100));
|
|
927
1004
|
* ```
|
|
928
1005
|
*/
|
|
@@ -1650,11 +1727,11 @@ export declare class Game {
|
|
|
1650
1727
|
*/
|
|
1651
1728
|
clearTimer(id: number): void;
|
|
1652
1729
|
/**
|
|
1653
|
-
* Draws text on screen as a **screen-space overlay** this frame.
|
|
1730
|
+
* Draws text on screen as a **simple screen-space overlay** this frame.
|
|
1654
1731
|
*
|
|
1655
1732
|
* **Overlay behavior:** Text is drawn in canvas/screen space — it ignores
|
|
1656
1733
|
* `scrollX` / `scrollY`. Position `(0, 0)` is always the top-left of the canvas.
|
|
1657
|
-
* Use this for HUD elements: score, lives, timer, debug info.
|
|
1734
|
+
* Use this for lightweight HUD elements: score, lives, timer, debug info.
|
|
1658
1735
|
*
|
|
1659
1736
|
* **Per-frame:** `drawText` must be called every frame to keep text visible.
|
|
1660
1737
|
* The text overlay list is cleared after each render. Call this inside `onUpdate`.
|
|
@@ -1669,6 +1746,14 @@ export declare class Game {
|
|
|
1669
1746
|
* registered with {@link Game.requireFont}. If a font is unavailable, the
|
|
1670
1747
|
* browser falls back to `monospace`.
|
|
1671
1748
|
*
|
|
1749
|
+
* Prefer {@link TextSprite} for UI buttons or labels that need persistent
|
|
1750
|
+
* bounds, hit testing, fixed sizes, backgrounds, borders, or text stroke.
|
|
1751
|
+
* `drawText()` is the lightweight overlay API, not the primary UI API.
|
|
1752
|
+
*
|
|
1753
|
+
* For controls that are only a single emoji, prefer {@link Sprite} over
|
|
1754
|
+
* {@link TextSprite}. Emoji-only buttons do not benefit from text padding and
|
|
1755
|
+
* usually fit more naturally in the sprite pipeline.
|
|
1756
|
+
*
|
|
1672
1757
|
* @param text - The string to render. Supports emoji and Unicode.
|
|
1673
1758
|
* @param x - X position in **screen space** (pixels from canvas left edge).
|
|
1674
1759
|
* @param y - Y position in **screen space** (pixels from canvas top edge).
|
|
@@ -1762,25 +1847,26 @@ export declare class Game {
|
|
|
1762
1847
|
* - Canvas dimensions
|
|
1763
1848
|
* - AudioContext
|
|
1764
1849
|
*
|
|
1765
|
-
* After clearing, `reset()` immediately calls
|
|
1766
|
-
*
|
|
1850
|
+
* After clearing, `reset()` immediately calls the active scene's
|
|
1851
|
+
* `onCreate()` (or legacy {@link Game.onCreate} if no scene is active) so it
|
|
1852
|
+
* can rebuild synchronously.
|
|
1853
|
+
*
|
|
1854
|
+
* @param scene - Optional new scene to make active before rebuilding.
|
|
1767
1855
|
*
|
|
1768
1856
|
* @example
|
|
1769
1857
|
* ```ts
|
|
1770
|
-
*
|
|
1771
|
-
*
|
|
1772
|
-
*
|
|
1773
|
-
*
|
|
1774
|
-
*
|
|
1775
|
-
*
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
*
|
|
1779
|
-
* game.addTimer(3000, false, () => game.reset()); // auto-restart
|
|
1780
|
-
* };
|
|
1858
|
+
* class GameOverScene {
|
|
1859
|
+
* onCreate() {
|
|
1860
|
+
* const skull = new Sprite("💀", 400, 300, 96);
|
|
1861
|
+
* game.add(skull);
|
|
1862
|
+
* game.addTimer(3000, false, () => game.reset());
|
|
1863
|
+
* }
|
|
1864
|
+
* }
|
|
1865
|
+
*
|
|
1866
|
+
* game.reset(new GameOverScene());
|
|
1781
1867
|
* ```
|
|
1782
1868
|
*/
|
|
1783
|
-
reset(): void;
|
|
1869
|
+
reset(scene?: IScene): void;
|
|
1784
1870
|
/**
|
|
1785
1871
|
* Starts the `requestAnimationFrame` game loop.
|
|
1786
1872
|
* Safe to call multiple times — does nothing if already running.
|
|
@@ -1788,10 +1874,12 @@ export declare class Game {
|
|
|
1788
1874
|
* asset registration, loads queued images, waits for all fonts registered via
|
|
1789
1875
|
* {@link Game.requireFont}, and only then, if images were queued, shows a
|
|
1790
1876
|
* default loading screen while those image assets are still loading.
|
|
1791
|
-
* After preload completes, `onCreate()` is called before
|
|
1877
|
+
* After preload completes, the active scene's `onCreate()` is called before
|
|
1878
|
+
* the first frame.
|
|
1792
1879
|
*
|
|
1793
|
-
* The loop calls `onUpdate`
|
|
1794
|
-
*
|
|
1880
|
+
* The loop calls the active scene's `onUpdate(dt)` (or legacy
|
|
1881
|
+
* {@link Game.onUpdate}) once per frame, then renders all sprites and text
|
|
1882
|
+
* overlays. Order per frame:
|
|
1795
1883
|
* 1. Accumulate timer elapsed time; fire ready callbacks.
|
|
1796
1884
|
* 2. Advance animations (linear interpolation).
|
|
1797
1885
|
* 3. Advance active explosion effects.
|
|
@@ -1805,11 +1893,15 @@ export declare class Game {
|
|
|
1805
1893
|
*
|
|
1806
1894
|
* @example
|
|
1807
1895
|
* ```ts
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1896
|
+
* class DemoScene {
|
|
1897
|
+
* onCreate() {}
|
|
1898
|
+
* onUpdate(dt: number) {}
|
|
1899
|
+
* }
|
|
1900
|
+
*
|
|
1901
|
+
* game.start(new DemoScene());
|
|
1810
1902
|
* ```
|
|
1811
1903
|
*/
|
|
1812
|
-
start(): void;
|
|
1904
|
+
start(scene?: IScene): void;
|
|
1813
1905
|
/**
|
|
1814
1906
|
* Stops the game loop. The canvas retains its last rendered frame.
|
|
1815
1907
|
* Call {@link Game.start} to resume.
|
package/dist/minimo.js
CHANGED
|
@@ -32,7 +32,7 @@ import { TimerSystem } from "./internal/TimerSystem.js";
|
|
|
32
32
|
* engine.
|
|
33
33
|
*/
|
|
34
34
|
export class BaseSprite {
|
|
35
|
-
constructor() {
|
|
35
|
+
constructor(game = null) {
|
|
36
36
|
/**
|
|
37
37
|
* X position in world space (horizontal center of the sprite), in pixels.
|
|
38
38
|
* Positive X points right. Updated each frame by: `x += vx * dt`.
|
|
@@ -137,6 +137,20 @@ export class BaseSprite {
|
|
|
137
137
|
* Values > 1 amplify gravity; negative values invert it.
|
|
138
138
|
*/
|
|
139
139
|
this.gravityScale = 0;
|
|
140
|
+
this._game = game;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Game instance associated with this sprite, if any.
|
|
144
|
+
*/
|
|
145
|
+
get game() {
|
|
146
|
+
return this._game;
|
|
147
|
+
}
|
|
148
|
+
/** @internal */
|
|
149
|
+
_setGame(game) {
|
|
150
|
+
if (this._game !== null && this._game !== game) {
|
|
151
|
+
throw new Error("MinimoJS: A sprite cannot be attached to multiple Game instances.");
|
|
152
|
+
}
|
|
153
|
+
this._game = game;
|
|
140
154
|
}
|
|
141
155
|
/**
|
|
142
156
|
* Resolved center X used internally by rendering, hit testing, and collisions.
|
|
@@ -207,7 +221,7 @@ export class Sprite extends BaseSprite {
|
|
|
207
221
|
* All other properties use their defaults and can be set after construction.
|
|
208
222
|
*
|
|
209
223
|
* @param sprite - The emoji character to render. Must be a single emoji.
|
|
210
|
-
*
|
|
224
|
+
* Use {@link ImageSprite} for preloaded bitmap textures.
|
|
211
225
|
* @example "🔥", "⭐", "🐢", "💣", "👾"
|
|
212
226
|
* @param x - Initial X position in world space (center), in pixels. Default: `0`.
|
|
213
227
|
* @param y - Initial Y position in world space (center), in pixels. Default: `0`.
|
|
@@ -231,9 +245,86 @@ export class Sprite extends BaseSprite {
|
|
|
231
245
|
return `emoji:${this.sprite}|size:${this._size}|color:${this.color}`;
|
|
232
246
|
}
|
|
233
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* A renderable sprite backed by a preloaded image asset.
|
|
250
|
+
*
|
|
251
|
+
* Width and height are always resolved from the current texture. To resize the
|
|
252
|
+
* sprite visually, use {@link BaseSprite.scale}.
|
|
253
|
+
*/
|
|
254
|
+
export class ImageSprite extends BaseSprite {
|
|
255
|
+
get imageKey() {
|
|
256
|
+
return this._imageKey;
|
|
257
|
+
}
|
|
258
|
+
get width() {
|
|
259
|
+
const image = this.getResolvedImage();
|
|
260
|
+
return image.naturalWidth || image.width;
|
|
261
|
+
}
|
|
262
|
+
get height() {
|
|
263
|
+
const image = this.getResolvedImage();
|
|
264
|
+
return image.naturalHeight || image.height;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Creates a new image-backed sprite.
|
|
268
|
+
*
|
|
269
|
+
* @param game - Game instance used to resolve the texture key.
|
|
270
|
+
* @param imageKey - Preloaded image key previously registered with {@link Game.loadImage}.
|
|
271
|
+
* @param x - Initial X position in world space (center), in pixels. Default: `0`.
|
|
272
|
+
* @param y - Initial Y position in world space (center), in pixels. Default: `0`.
|
|
273
|
+
*/
|
|
274
|
+
constructor(game, imageKey, x = 0, y = 0) {
|
|
275
|
+
super(game);
|
|
276
|
+
this._imageKey = imageKey;
|
|
277
|
+
this.x = x;
|
|
278
|
+
this.y = y;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Replaces the current texture with another preloaded image.
|
|
282
|
+
*
|
|
283
|
+
* @param imageKey - New preloaded image key to render.
|
|
284
|
+
*/
|
|
285
|
+
setTexture(imageKey) {
|
|
286
|
+
if (imageKey === this._imageKey)
|
|
287
|
+
return;
|
|
288
|
+
this.assertTextureAvailable(imageKey);
|
|
289
|
+
this._imageKey = imageKey;
|
|
290
|
+
}
|
|
291
|
+
getRenderCacheKey() {
|
|
292
|
+
const image = this.getResolvedImage();
|
|
293
|
+
return [
|
|
294
|
+
"image",
|
|
295
|
+
this._imageKey,
|
|
296
|
+
image.naturalWidth || image.width,
|
|
297
|
+
image.naturalHeight || image.height,
|
|
298
|
+
].join("|");
|
|
299
|
+
}
|
|
300
|
+
getResolvedImage() {
|
|
301
|
+
this.assertTextureAvailable(this._imageKey);
|
|
302
|
+
const image = this.game?.getImage(this._imageKey);
|
|
303
|
+
if (!image) {
|
|
304
|
+
throw new Error(`MinimoJS: Image '${this._imageKey}' is not loaded.`);
|
|
305
|
+
}
|
|
306
|
+
return image;
|
|
307
|
+
}
|
|
308
|
+
assertTextureAvailable(imageKey) {
|
|
309
|
+
if (!this.game) {
|
|
310
|
+
throw new Error("MinimoJS: ImageSprite requires an associated Game instance.");
|
|
311
|
+
}
|
|
312
|
+
if (!this.game.hasImage(imageKey)) {
|
|
313
|
+
throw new Error(`MinimoJS: Image '${imageKey}' is not loaded.`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
234
317
|
/**
|
|
235
318
|
* A renderable text actor that participates in the same animation/effects
|
|
236
319
|
* pipeline as regular sprites.
|
|
320
|
+
*
|
|
321
|
+
* Use `TextSprite` when the content is primarily text, or when you need text
|
|
322
|
+
* layout features such as wrapping, fixed button sizes, background/border, or
|
|
323
|
+
* text stroke.
|
|
324
|
+
*
|
|
325
|
+
* For controls that are only a single emoji, prefer {@link Sprite}. Emoji-only
|
|
326
|
+
* buttons usually behave better as sprites because they do not need text
|
|
327
|
+
* padding/layout and their bounds match the rendered emoji more directly.
|
|
237
328
|
*/
|
|
238
329
|
export class TextSprite extends BaseSprite {
|
|
239
330
|
get width() {
|
|
@@ -254,8 +345,8 @@ export class TextSprite extends BaseSprite {
|
|
|
254
345
|
this.maxWidth = 0;
|
|
255
346
|
this.fixedWidth = 0;
|
|
256
347
|
this.fixedHeight = 0;
|
|
257
|
-
this.paddingX =
|
|
258
|
-
this.paddingY =
|
|
348
|
+
this.paddingX = 2;
|
|
349
|
+
this.paddingY = 2;
|
|
259
350
|
this.backgroundColor = null;
|
|
260
351
|
this.borderColor = null;
|
|
261
352
|
this.borderWidth = 0;
|
|
@@ -579,6 +670,11 @@ export class BackgroundLayer {
|
|
|
579
670
|
* If you use additional families, register them with {@link Game.requireFont}
|
|
580
671
|
* before calling {@link Game.start}.
|
|
581
672
|
*
|
|
673
|
+
* Prefer {@link TextSprite} for persistent UI text, interactive labels,
|
|
674
|
+
* buttons, fixed-size text boxes, and styled text elements. Keep
|
|
675
|
+
* `drawText()` for simple screen-space overlays such as HUD counters, debug
|
|
676
|
+
* text, or other text that is redrawn every frame.
|
|
677
|
+
*
|
|
582
678
|
* You MUST still declare the fonts yourself in your `index.html`. MinimoJS
|
|
583
679
|
* does NOT download, inject, or manage web fonts for you. If a font is missing,
|
|
584
680
|
* the browser falls back to `monospace`.
|
|
@@ -724,10 +820,10 @@ export class BackgroundLayer {
|
|
|
724
820
|
*
|
|
725
821
|
* ---
|
|
726
822
|
*
|
|
727
|
-
* ## Scene Initialization with `
|
|
823
|
+
* ## Scene Initialization with `IScene`
|
|
728
824
|
*
|
|
729
|
-
* MinimoJS
|
|
730
|
-
*
|
|
825
|
+
* MinimoJS supports simple scene objects via {@link IScene}. The engine calls
|
|
826
|
+
* a scene's `onCreate()`:
|
|
731
827
|
* - Once before the first frame (when {@link Game.start} is called).
|
|
732
828
|
* - Again after each {@link Game.reset}.
|
|
733
829
|
*
|
|
@@ -739,25 +835,30 @@ export class BackgroundLayer {
|
|
|
739
835
|
* - Scroll position (scrollX and scrollY reset to 0)
|
|
740
836
|
* - Per-frame input state (pressed keys/pointer)
|
|
741
837
|
*
|
|
742
|
-
* After clearing, `reset()` calls `onCreate()` so
|
|
743
|
-
*
|
|
838
|
+
* After clearing, `reset()` calls the active scene's `onCreate()` so it can
|
|
839
|
+
* rebuild immediately. Simply re-add sprites and re-register timers.
|
|
744
840
|
*
|
|
745
841
|
* ```ts
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
*
|
|
752
|
-
*
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
*
|
|
842
|
+
* class SkullScene implements IScene {
|
|
843
|
+
* onCreate() {
|
|
844
|
+
* const skull = new Sprite("💀", 400, 300, 96);
|
|
845
|
+
* game.add(skull);
|
|
846
|
+
* }
|
|
847
|
+
*
|
|
848
|
+
* onUpdate(dt: number) {
|
|
849
|
+
* // normal per-frame update
|
|
850
|
+
* }
|
|
851
|
+
* }
|
|
852
|
+
*
|
|
853
|
+
* const scene = new SkullScene();
|
|
854
|
+
* game.start(scene); // calls onCreate() once before first frame
|
|
757
855
|
* // later:
|
|
758
856
|
* game.reset(); // clears + calls onCreate() again
|
|
759
857
|
* ```
|
|
760
858
|
*
|
|
859
|
+
* Legacy `game.onCreate` / `game.onUpdate` callbacks still work when no active
|
|
860
|
+
* {@link IScene} is set.
|
|
861
|
+
*
|
|
761
862
|
* ---
|
|
762
863
|
*
|
|
763
864
|
* ## Sprite Lifecycle Ownership
|
|
@@ -774,7 +875,7 @@ export class BackgroundLayer {
|
|
|
774
875
|
* ## Forbidden Features
|
|
775
876
|
*
|
|
776
877
|
* The following do NOT exist in MinimoJS v1. Do NOT attempt to use them:
|
|
777
|
-
* -
|
|
878
|
+
* - Full scene manager architecture (scene stacks, transitions, loaders, etc.)
|
|
778
879
|
* - Entity Component System (ECS)
|
|
779
880
|
* - Physics engine (no Box2D, Matter.js, etc.)
|
|
780
881
|
* - Camera zoom or scale
|
|
@@ -783,7 +884,6 @@ export class BackgroundLayer {
|
|
|
783
884
|
* - Parallax layers
|
|
784
885
|
* - Multiple cameras
|
|
785
886
|
* - Full physics engine (only basic explicit AABB collision helpers are provided)
|
|
786
|
-
* - Image sprites (PNG, SVG, canvas, etc.) as gameplay actors
|
|
787
887
|
* - `setTimeout` or `setInterval`
|
|
788
888
|
*/
|
|
789
889
|
export class Game {
|
|
@@ -845,10 +945,10 @@ export class Game {
|
|
|
845
945
|
this._physicsSystem.enabled = value;
|
|
846
946
|
}
|
|
847
947
|
/**
|
|
848
|
-
*
|
|
948
|
+
* Legacy scene creation callback.
|
|
849
949
|
*
|
|
850
950
|
* Called once before the first frame on {@link Game.start}, and again after
|
|
851
|
-
* each {@link Game.reset}
|
|
951
|
+
* each {@link Game.reset} when no active {@link IScene} is set.
|
|
852
952
|
*
|
|
853
953
|
* @example
|
|
854
954
|
* ```ts
|
|
@@ -861,10 +961,16 @@ export class Game {
|
|
|
861
961
|
* ```
|
|
862
962
|
*/
|
|
863
963
|
get onCreate() {
|
|
864
|
-
return this.
|
|
964
|
+
return this._onCreate;
|
|
865
965
|
}
|
|
866
966
|
set onCreate(callback) {
|
|
867
|
-
this.
|
|
967
|
+
this._onCreate = callback;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Currently active scene object, if any.
|
|
971
|
+
*/
|
|
972
|
+
get currentScene() {
|
|
973
|
+
return this._currentScene;
|
|
868
974
|
}
|
|
869
975
|
// -------------------------------------------------------------------------
|
|
870
976
|
// Constructor
|
|
@@ -893,6 +999,8 @@ export class Game {
|
|
|
893
999
|
/** @internal */ this._isRegisteringPreloadAssets = false;
|
|
894
1000
|
/** @internal */ this._hasCompletedPreload = false;
|
|
895
1001
|
/** @internal */ this._preloadPromise = null;
|
|
1002
|
+
/** @internal */ this._onCreate = null;
|
|
1003
|
+
/** @internal */ this._currentScene = null;
|
|
896
1004
|
/**
|
|
897
1005
|
* Horizontal scroll offset of the world camera, in pixels.
|
|
898
1006
|
* The canvas viewport is shifted left by `scrollX` — sprites with higher `x`
|
|
@@ -968,7 +1076,9 @@ export class Game {
|
|
|
968
1076
|
*/
|
|
969
1077
|
this.onPreload = null;
|
|
970
1078
|
/**
|
|
971
|
-
*
|
|
1079
|
+
* Legacy per-frame callback invoked after physics and timer updates.
|
|
1080
|
+
*
|
|
1081
|
+
* This is used only when no active {@link IScene} is set.
|
|
972
1082
|
*
|
|
973
1083
|
* @param dt - Delta time in **seconds** since the last frame.
|
|
974
1084
|
* Use this for velocity-based movement: `sprite.x += speed * dt`.
|
|
@@ -1000,7 +1110,7 @@ export class Game {
|
|
|
1000
1110
|
this._timerSystem = new TimerSystem();
|
|
1001
1111
|
this._animationSystem = new AnimationSystem();
|
|
1002
1112
|
this._physicsSystem = new PhysicsSystem();
|
|
1003
|
-
this._spriteSystem = new SpriteSystem(this._animationSystem);
|
|
1113
|
+
this._spriteSystem = new SpriteSystem(this._animationSystem, this);
|
|
1004
1114
|
this._backgroundSystem = new BackgroundSystem();
|
|
1005
1115
|
this._assetSystem = new AssetSystem();
|
|
1006
1116
|
this._inputSystem = new InputSystem(this, this._canvas);
|
|
@@ -1010,6 +1120,7 @@ export class Game {
|
|
|
1010
1120
|
this._explosionSystem = new ExplosionSystem();
|
|
1011
1121
|
this._trailSystem = new TrailSystem();
|
|
1012
1122
|
this._loopSystem = new LoopSystem(this._onLoopFrameCallback.bind(this));
|
|
1123
|
+
this._loopSystem.onCreate = this._invokeCreate.bind(this);
|
|
1013
1124
|
this._inputSystem.bindInputEvents();
|
|
1014
1125
|
this.requireFont('"Press Start 2P"', {
|
|
1015
1126
|
sampleText: "Loading... SCORE LIVES GAME OVER YOU WIN",
|
|
@@ -1053,7 +1164,7 @@ export class Game {
|
|
|
1053
1164
|
// Sprite management
|
|
1054
1165
|
// -------------------------------------------------------------------------
|
|
1055
1166
|
/**
|
|
1056
|
-
* Registers a {@link
|
|
1167
|
+
* Registers a {@link BaseSprite} (or subclass instance) with the engine.
|
|
1057
1168
|
*
|
|
1058
1169
|
* After calling `add`, the sprite is rendered and, if dynamic
|
|
1059
1170
|
* (`isStatic = false`), receives built-in velocity/gravity integration every
|
|
@@ -1062,11 +1173,11 @@ export class Game {
|
|
|
1062
1173
|
* **Ownership:** The game instance takes ownership of the sprite from this
|
|
1063
1174
|
* point forward. It will appear in {@link Game.getSprites} on the same frame.
|
|
1064
1175
|
*
|
|
1065
|
-
* **Subclasses:** Any class that extends {@link
|
|
1066
|
-
* The engine stores and processes it as a
|
|
1176
|
+
* **Subclasses:** Any class that extends {@link BaseSprite} can be passed here.
|
|
1177
|
+
* The engine stores and processes it as a live sprite; your custom properties
|
|
1067
1178
|
* are preserved on the instance.
|
|
1068
1179
|
*
|
|
1069
|
-
* @param sprite - A {@link
|
|
1180
|
+
* @param sprite - A {@link BaseSprite} instance (or subclass) to add.
|
|
1070
1181
|
* @returns The same sprite instance, for chaining or inline assignment.
|
|
1071
1182
|
*
|
|
1072
1183
|
* @example
|
|
@@ -1081,7 +1192,7 @@ export class Game {
|
|
|
1081
1192
|
* constructor(x: number, y: number) {
|
|
1082
1193
|
* super("👾", x, y, 40);
|
|
1083
1194
|
* }
|
|
1084
|
-
*
|
|
1195
|
+
* }
|
|
1085
1196
|
* const enemy = game.add(new Enemy(600, 100));
|
|
1086
1197
|
* ```
|
|
1087
1198
|
*/
|
|
@@ -1926,11 +2037,11 @@ export class Game {
|
|
|
1926
2037
|
// Text
|
|
1927
2038
|
// -------------------------------------------------------------------------
|
|
1928
2039
|
/**
|
|
1929
|
-
* Draws text on screen as a **screen-space overlay** this frame.
|
|
2040
|
+
* Draws text on screen as a **simple screen-space overlay** this frame.
|
|
1930
2041
|
*
|
|
1931
2042
|
* **Overlay behavior:** Text is drawn in canvas/screen space — it ignores
|
|
1932
2043
|
* `scrollX` / `scrollY`. Position `(0, 0)` is always the top-left of the canvas.
|
|
1933
|
-
* Use this for HUD elements: score, lives, timer, debug info.
|
|
2044
|
+
* Use this for lightweight HUD elements: score, lives, timer, debug info.
|
|
1934
2045
|
*
|
|
1935
2046
|
* **Per-frame:** `drawText` must be called every frame to keep text visible.
|
|
1936
2047
|
* The text overlay list is cleared after each render. Call this inside `onUpdate`.
|
|
@@ -1945,6 +2056,14 @@ export class Game {
|
|
|
1945
2056
|
* registered with {@link Game.requireFont}. If a font is unavailable, the
|
|
1946
2057
|
* browser falls back to `monospace`.
|
|
1947
2058
|
*
|
|
2059
|
+
* Prefer {@link TextSprite} for UI buttons or labels that need persistent
|
|
2060
|
+
* bounds, hit testing, fixed sizes, backgrounds, borders, or text stroke.
|
|
2061
|
+
* `drawText()` is the lightweight overlay API, not the primary UI API.
|
|
2062
|
+
*
|
|
2063
|
+
* For controls that are only a single emoji, prefer {@link Sprite} over
|
|
2064
|
+
* {@link TextSprite}. Emoji-only buttons do not benefit from text padding and
|
|
2065
|
+
* usually fit more naturally in the sprite pipeline.
|
|
2066
|
+
*
|
|
1948
2067
|
* @param text - The string to render. Supports emoji and Unicode.
|
|
1949
2068
|
* @param x - X position in **screen space** (pixels from canvas left edge).
|
|
1950
2069
|
* @param y - Y position in **screen space** (pixels from canvas top edge).
|
|
@@ -2067,25 +2186,29 @@ export class Game {
|
|
|
2067
2186
|
* - Canvas dimensions
|
|
2068
2187
|
* - AudioContext
|
|
2069
2188
|
*
|
|
2070
|
-
* After clearing, `reset()` immediately calls
|
|
2071
|
-
*
|
|
2189
|
+
* After clearing, `reset()` immediately calls the active scene's
|
|
2190
|
+
* `onCreate()` (or legacy {@link Game.onCreate} if no scene is active) so it
|
|
2191
|
+
* can rebuild synchronously.
|
|
2192
|
+
*
|
|
2193
|
+
* @param scene - Optional new scene to make active before rebuilding.
|
|
2072
2194
|
*
|
|
2073
2195
|
* @example
|
|
2074
2196
|
* ```ts
|
|
2075
|
-
*
|
|
2076
|
-
*
|
|
2077
|
-
*
|
|
2078
|
-
*
|
|
2079
|
-
*
|
|
2080
|
-
*
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
*
|
|
2084
|
-
* game.addTimer(3000, false, () => game.reset()); // auto-restart
|
|
2085
|
-
* };
|
|
2197
|
+
* class GameOverScene {
|
|
2198
|
+
* onCreate() {
|
|
2199
|
+
* const skull = new Sprite("💀", 400, 300, 96);
|
|
2200
|
+
* game.add(skull);
|
|
2201
|
+
* game.addTimer(3000, false, () => game.reset());
|
|
2202
|
+
* }
|
|
2203
|
+
* }
|
|
2204
|
+
*
|
|
2205
|
+
* game.reset(new GameOverScene());
|
|
2086
2206
|
* ```
|
|
2087
2207
|
*/
|
|
2088
|
-
reset() {
|
|
2208
|
+
reset(scene) {
|
|
2209
|
+
if (scene !== undefined) {
|
|
2210
|
+
this._currentScene = scene;
|
|
2211
|
+
}
|
|
2089
2212
|
this._backgroundSystem.clearAll();
|
|
2090
2213
|
this._spriteSystem.clearAll();
|
|
2091
2214
|
this._timerSystem.clearAll();
|
|
@@ -2108,10 +2231,12 @@ export class Game {
|
|
|
2108
2231
|
* asset registration, loads queued images, waits for all fonts registered via
|
|
2109
2232
|
* {@link Game.requireFont}, and only then, if images were queued, shows a
|
|
2110
2233
|
* default loading screen while those image assets are still loading.
|
|
2111
|
-
* After preload completes, `onCreate()` is called before
|
|
2234
|
+
* After preload completes, the active scene's `onCreate()` is called before
|
|
2235
|
+
* the first frame.
|
|
2112
2236
|
*
|
|
2113
|
-
* The loop calls `onUpdate`
|
|
2114
|
-
*
|
|
2237
|
+
* The loop calls the active scene's `onUpdate(dt)` (or legacy
|
|
2238
|
+
* {@link Game.onUpdate}) once per frame, then renders all sprites and text
|
|
2239
|
+
* overlays. Order per frame:
|
|
2115
2240
|
* 1. Accumulate timer elapsed time; fire ready callbacks.
|
|
2116
2241
|
* 2. Advance animations (linear interpolation).
|
|
2117
2242
|
* 3. Advance active explosion effects.
|
|
@@ -2125,11 +2250,18 @@ export class Game {
|
|
|
2125
2250
|
*
|
|
2126
2251
|
* @example
|
|
2127
2252
|
* ```ts
|
|
2128
|
-
*
|
|
2129
|
-
*
|
|
2253
|
+
* class DemoScene {
|
|
2254
|
+
* onCreate() {}
|
|
2255
|
+
* onUpdate(dt: number) {}
|
|
2256
|
+
* }
|
|
2257
|
+
*
|
|
2258
|
+
* game.start(new DemoScene());
|
|
2130
2259
|
* ```
|
|
2131
2260
|
*/
|
|
2132
|
-
start() {
|
|
2261
|
+
start(scene) {
|
|
2262
|
+
if (scene !== undefined) {
|
|
2263
|
+
this._currentScene = scene;
|
|
2264
|
+
}
|
|
2133
2265
|
this.applyGlobalFontRequirements();
|
|
2134
2266
|
if (this._hasCompletedPreload) {
|
|
2135
2267
|
this._loopSystem.start();
|
|
@@ -2207,7 +2339,9 @@ export class Game {
|
|
|
2207
2339
|
this._animationSystem.update(dtMs);
|
|
2208
2340
|
this._explosionSystem.update(dt, dtMs);
|
|
2209
2341
|
this._physicsSystem.update(this._spriteSystem.getMutableSprites(), dt);
|
|
2210
|
-
if (this.onUpdate)
|
|
2342
|
+
if (this._currentScene?.onUpdate)
|
|
2343
|
+
this._currentScene.onUpdate(dt);
|
|
2344
|
+
else if (this.onUpdate)
|
|
2211
2345
|
this.onUpdate(dt);
|
|
2212
2346
|
this._trailSystem.update(dtMs, this._renderSystem.getSpriteRenderSnapshot.bind(this._renderSystem));
|
|
2213
2347
|
this._render();
|
|
@@ -2215,6 +2349,13 @@ export class Game {
|
|
|
2215
2349
|
this._textSystem.clear();
|
|
2216
2350
|
}
|
|
2217
2351
|
/** @internal */
|
|
2352
|
+
_invokeCreate() {
|
|
2353
|
+
if (this._currentScene?.onCreate)
|
|
2354
|
+
this._currentScene.onCreate();
|
|
2355
|
+
else
|
|
2356
|
+
this._onCreate?.();
|
|
2357
|
+
}
|
|
2358
|
+
/** @internal */
|
|
2218
2359
|
waitForDocumentFonts() {
|
|
2219
2360
|
if (typeof document === "undefined") {
|
|
2220
2361
|
return Promise.resolve();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minimojs",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.11",
|
|
4
4
|
"description": "MinimoJS v1 — ultra-minimal, flat, deterministic 2D web game engine. Emoji-only sprites, rAF loop, TypeScript-first, LLM-friendly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/minimo.js",
|