like2d 2.9.0 → 2.10.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 (103) hide show
  1. package/README.md +21 -17
  2. package/dist/__benchmarks__/vector2.bench.d.ts +2 -0
  3. package/dist/__benchmarks__/vector2.bench.d.ts.map +1 -0
  4. package/dist/__benchmarks__/vector2.bench.js +74 -0
  5. package/dist/{core → audio}/audio.d.ts +12 -3
  6. package/dist/audio/audio.d.ts.map +1 -0
  7. package/dist/{core → audio}/audio.js +10 -2
  8. package/dist/audio/index.d.ts +2 -0
  9. package/dist/audio/index.d.ts.map +1 -0
  10. package/dist/audio/index.js +1 -0
  11. package/dist/engine.d.ts +12 -42
  12. package/dist/engine.d.ts.map +1 -1
  13. package/dist/engine.js +34 -76
  14. package/dist/{core/events.d.ts → events.d.ts} +27 -50
  15. package/dist/events.d.ts.map +1 -0
  16. package/dist/events.js +5 -0
  17. package/dist/{core → graphics}/canvas.d.ts +18 -11
  18. package/dist/graphics/canvas.d.ts.map +1 -0
  19. package/dist/{core → graphics}/canvas.js +73 -58
  20. package/dist/{core/graphics.d.ts → graphics/drawing.d.ts} +25 -25
  21. package/dist/graphics/drawing.d.ts.map +1 -0
  22. package/dist/{core/graphics.js → graphics/drawing.js} +59 -52
  23. package/dist/graphics/index.d.ts +19 -0
  24. package/dist/graphics/index.d.ts.map +1 -0
  25. package/dist/graphics/index.js +13 -0
  26. package/dist/index.d.ts +3 -30
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +0 -21
  29. package/dist/input/controllerdb.json +1 -0
  30. package/dist/input/gamepad-mapping.d.ts +119 -0
  31. package/dist/input/gamepad-mapping.d.ts.map +1 -0
  32. package/dist/input/gamepad-mapping.js +114 -0
  33. package/dist/input/gamepad.d.ts +73 -0
  34. package/dist/input/gamepad.d.ts.map +1 -0
  35. package/dist/input/gamepad.js +291 -0
  36. package/dist/input/index.d.ts +6 -0
  37. package/dist/input/index.d.ts.map +1 -0
  38. package/dist/input/index.js +1 -0
  39. package/dist/input/input.d.ts +76 -0
  40. package/dist/input/input.d.ts.map +1 -0
  41. package/dist/input/input.js +163 -0
  42. package/dist/input/keyboard.d.ts +15 -0
  43. package/dist/input/keyboard.d.ts.map +1 -0
  44. package/dist/{core → input}/keyboard.js +11 -21
  45. package/dist/input/mouse.d.ts +108 -0
  46. package/dist/input/mouse.d.ts.map +1 -0
  47. package/dist/input/mouse.js +241 -0
  48. package/dist/like.d.ts +80 -0
  49. package/dist/like.d.ts.map +1 -0
  50. package/dist/like.js +5 -0
  51. package/dist/math/index.d.ts +16 -0
  52. package/dist/math/index.d.ts.map +1 -1
  53. package/dist/math/index.js +16 -0
  54. package/dist/math/rect.d.ts +24 -27
  55. package/dist/math/rect.d.ts.map +1 -1
  56. package/dist/math/rect.js +47 -73
  57. package/dist/math/vector2.d.ts +87 -32
  58. package/dist/math/vector2.d.ts.map +1 -1
  59. package/dist/math/vector2.js +92 -110
  60. package/dist/prefab-scenes/index.d.ts +1 -0
  61. package/dist/prefab-scenes/index.d.ts.map +1 -1
  62. package/dist/prefab-scenes/index.js +1 -0
  63. package/dist/prefab-scenes/mapGamepad.d.ts +30 -0
  64. package/dist/prefab-scenes/mapGamepad.d.ts.map +1 -0
  65. package/dist/prefab-scenes/mapGamepad.js +192 -0
  66. package/dist/prefab-scenes/startScreen.d.ts +2 -2
  67. package/dist/prefab-scenes/startScreen.js +2 -2
  68. package/dist/scene.d.ts +2 -2
  69. package/dist/scene.d.ts.map +1 -1
  70. package/dist/timer/index.d.ts +2 -0
  71. package/dist/timer/index.d.ts.map +1 -0
  72. package/dist/timer/index.js +1 -0
  73. package/dist/timer/timer.d.ts +32 -0
  74. package/dist/timer/timer.d.ts.map +1 -0
  75. package/dist/{core → timer}/timer.js +20 -3
  76. package/package.json +21 -12
  77. package/dist/core/audio.d.ts.map +0 -1
  78. package/dist/core/canvas.d.ts.map +0 -1
  79. package/dist/core/events.d.ts.map +0 -1
  80. package/dist/core/events.js +0 -21
  81. package/dist/core/gamepad-mapping.d.ts +0 -58
  82. package/dist/core/gamepad-mapping.d.ts.map +0 -1
  83. package/dist/core/gamepad-mapping.js +0 -23
  84. package/dist/core/gamepad.d.ts +0 -37
  85. package/dist/core/gamepad.d.ts.map +0 -1
  86. package/dist/core/gamepad.js +0 -103
  87. package/dist/core/graphics.d.ts.map +0 -1
  88. package/dist/core/input-state.d.ts +0 -14
  89. package/dist/core/input-state.d.ts.map +0 -1
  90. package/dist/core/input-state.js +0 -50
  91. package/dist/core/input.d.ts +0 -40
  92. package/dist/core/input.d.ts.map +0 -1
  93. package/dist/core/input.js +0 -105
  94. package/dist/core/keyboard.d.ts +0 -15
  95. package/dist/core/keyboard.d.ts.map +0 -1
  96. package/dist/core/like.d.ts +0 -113
  97. package/dist/core/like.d.ts.map +0 -1
  98. package/dist/core/like.js +0 -9
  99. package/dist/core/mouse.d.ts +0 -52
  100. package/dist/core/mouse.d.ts.map +0 -1
  101. package/dist/core/mouse.js +0 -142
  102. package/dist/core/timer.d.ts +0 -15
  103. package/dist/core/timer.d.ts.map +0 -1
package/README.md CHANGED
@@ -31,9 +31,10 @@
31
31
  </g>
32
32
  </svg>
33
33
 
34
- Lightweight Web framework inspired by [LÖVE](https://love2d.org/).
34
+ Lightweight web game framework inspired by [LÖVE](https://love2d.org/).
35
35
 
36
36
  ## <div style="color:red">During v2.x.x, LIKE's API will change.</div>
37
+
37
38
  ## What it is
38
39
 
39
40
  LIKE is a cozy way to make 2d games for browser.
@@ -43,11 +44,11 @@ LIKE is a cozy way to make 2d games for browser.
43
44
  - **🔥 Fire-and-forget Assets:** Graphics and audio that pretend to be synchronous.
44
45
  - **🎯 DWIM graphics:** Turns repetitive draw calls into one while removing state bleed for properties like `lineCap`.
45
46
  - **↔️ Two Canvas Modes:**
46
- - 🖊️ Audio-resize the canvas; sharp at any resolution.
47
- - 👾 For retro-style developers, pixels stay crisp but smooth via prescaling.
48
- - **⭕ Easier Geometry:** `Vector2` and `Rect` are just number tuples (arrays), but a pure-functional library makes them easy to work with and plays nice with `map` and `reduce`.
49
- - **🚲 Easy Input:** Keyboard, Mouse, and Gamepad all are given both event-based and tracking-based options. Choose what fits your architecture.
50
- - **👟 Consistent APIs:** Colors 0-1, not 0-255. Seconds, not milliseconds.
47
+ - 🖊️ Audio-resize the canvas; sharp at any resolution.
48
+ - 👾 For retro-style developers, pixels stay crisp but smooth via prescaling.
49
+ - **⭕ Easier Geometry:** `Vector2` and `Rect` are just number tuples (arrays), but a pure-functional library makes them easy to work with and plays nice with `map` and `reduce`.
50
+ - **🚲 Easy Input:** Keyboard, Mouse, and Gamepad all are given both event-based and query-based options. Choose what fits your architecture. Most gamepads get auto-mapped perfectly, are easy to remap, and LIKE can load and save user mappings automatically.
51
+ - **👟Consistent APIs:** Colors 0-1, not 0-255. Seconds, not milliseconds. Physical gamepad buttons, not "A" or "B".
51
52
  - **👉 Actions System:** An input layer maps inputs to actions, which fire usable events.
52
53
  - **🌎 Global control:** Choose how to handle LIKE events, and manage resources with centralized trackers. LIKE is a great foundation for your own engine.
53
54
  - **🐦 Light and Elegant:** Zero dependencies and less than 5000 lines of code -- focused entirely on what matters.
@@ -55,6 +56,7 @@ LIKE is a cozy way to make 2d games for browser.
55
56
  ## Installation
56
57
 
57
58
  Most package managers will work.
59
+
58
60
  ```bash
59
61
  npm install like2d
60
62
  # or ...
@@ -66,6 +68,7 @@ deno add jsr:@like2d/like
66
68
 
67
69
  To try Like2D quickly, use this starter with
68
70
  hot reloading and a basic webpage.
71
+
69
72
  ```bash
70
73
  npx degit 44100hertz/Like2D/examples/starter my-game
71
74
  ```
@@ -73,25 +76,25 @@ npx degit 44100hertz/Like2D/examples/starter my-game
73
76
  ## Usage Example
74
77
 
75
78
  ```typescript
76
- import { createLike } from 'like2d';
79
+ import { createLike } from "like2d";
77
80
 
78
81
  const like = createLike(document.body);
79
82
 
80
83
  like.load = () => {
81
84
  like.setMode([800, 600]);
82
- like.input.setAction('jump', ['Space', 'ButtonBottom']);
85
+ like.input.setAction("jump", ["Space", "BBottom"]);
83
86
  };
84
87
 
85
88
  like.update = (dt) => {
86
- if (like.input.justPressed('jump')) {
87
- console.log('Jump!');
89
+ if (like.input.justPressed("jump")) {
90
+ console.log("Jump!");
88
91
  }
89
92
  };
90
93
 
91
94
  like.draw = () => {
92
95
  like.gfx.clear([0.1, 0.1, 0.1, 1]);
93
- like.gfx.circle('fill', 'dodgerblue', [400, 300], 50);
94
- like.gfx.print('white', 'Hello Like2D!', [20, 20]);
96
+ like.gfx.circle("fill", "dodgerblue", [400, 300], 50);
97
+ like.gfx.print("white", "Hello Like2D!", [20, 20]);
95
98
  };
96
99
 
97
100
  await like.start();
@@ -100,11 +103,12 @@ await like.start();
100
103
  ## For Love2D Developers
101
104
 
102
105
  LIKE's API is not the same as LOVE, but similar in spirit. Notable differences:
103
- - Draw your graphics in one call, that's all. No setup or state bleed.
104
- - You manage your own instance of like in a big friendly object. This allows us to have multiple games on one page.
105
- - We use Vector2 and Rect tuples (like `[x, y]`) instead of loose coordinates.
106
- - Theres an actions system -- `input.setAction` / `actionpressed` and `actionreleased` callbacks.
107
- - Some things are missing either due to browser limitations or smaller scope.
106
+
107
+ - Draw your graphics in one call, that's all. No setup or state bleed.
108
+ - You manage your own instance of like in a big friendly object. This allows us to have multiple games on one page.
109
+ - We use Vector2 and Rect tuples (like `[x, y]`) instead of loose coordinates.
110
+ - Theres an actions system -- `input.setAction` / `actionpressed` and `actionreleased` callbacks.
111
+ - Some things are missing either due to browser limitations or smaller scope.
108
112
 
109
113
  ## Feedback welcome
110
114
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=vector2.bench.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector2.bench.d.ts","sourceRoot":"","sources":["../../src/__benchmarks__/vector2.bench.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { bench, describe } from 'vitest';
2
+ import { Vec2 } from '../math/vector2';
3
+ const VEC2_COUNT = 10000;
4
+ function makeVec2s(count) {
5
+ const vec2s = [];
6
+ for (let i = 0; i < count; i++) {
7
+ vec2s.push([Math.random() * 1000, Math.random() * 1000]);
8
+ }
9
+ return vec2s;
10
+ }
11
+ describe('Vector2', () => {
12
+ const a = makeVec2s(VEC2_COUNT);
13
+ const b = makeVec2s(VEC2_COUNT);
14
+ describe('binary operations', () => {
15
+ bench('add', () => {
16
+ let sum = 0;
17
+ for (let i = 0; i < VEC2_COUNT; i++) {
18
+ const result = Vec2.add(a[i], b[i]);
19
+ sum += result[0];
20
+ }
21
+ if (sum === -1)
22
+ console.log(sum);
23
+ });
24
+ bench('sub', () => {
25
+ let sum = 0;
26
+ for (let i = 0; i < VEC2_COUNT; i++) {
27
+ const result = Vec2.sub(a[i], b[i]);
28
+ sum += result[0];
29
+ }
30
+ if (sum === -1)
31
+ console.log(sum);
32
+ });
33
+ bench('mul', () => {
34
+ let sum = 0;
35
+ for (let i = 0; i < VEC2_COUNT; i++) {
36
+ const result = Vec2.mul(a[i], b[i]);
37
+ sum += result[0];
38
+ }
39
+ if (sum === -1)
40
+ console.log(sum);
41
+ });
42
+ bench('div', () => {
43
+ let sum = 0;
44
+ for (let i = 0; i < VEC2_COUNT; i++) {
45
+ const result = Vec2.div(a[i], b[i]);
46
+ sum += result[0];
47
+ }
48
+ if (sum === -1)
49
+ console.log(sum);
50
+ });
51
+ bench('dot', () => {
52
+ let sum = 0;
53
+ for (let i = 0; i < VEC2_COUNT; i++) {
54
+ const result = Vec2.dot(a[i], b[i]);
55
+ sum += result;
56
+ }
57
+ if (sum === -1)
58
+ console.log(sum);
59
+ });
60
+ bench('chained ops (add, sub, mul, div, dot)', () => {
61
+ let sum = 0;
62
+ for (let i = 0; i < VEC2_COUNT; i++) {
63
+ const r1 = Vec2.add(a[i], b[i]);
64
+ const r2 = Vec2.sub(r1, a[i]);
65
+ const r3 = Vec2.mul(r2, 2);
66
+ const r4 = Vec2.div(r3, b[i]);
67
+ const result = Vec2.dot(r4, a[i]);
68
+ sum += result;
69
+ }
70
+ if (sum === -1)
71
+ console.log(sum);
72
+ });
73
+ });
74
+ });
@@ -5,8 +5,8 @@
5
5
  *
6
6
  * ## Track and give global control to all audio objects
7
7
  * Start, stop, or set global volume for every currently playing sound.
8
- *
9
8
  */
9
+ /** Pass this into like.audio.newSource as config. */
10
10
  export type AudioSourceOptions = {
11
11
  volume?: number;
12
12
  };
@@ -21,12 +21,13 @@ export declare class AudioSource {
21
21
  readonly path: string;
22
22
  /** Underlying HTMLAudioElement. Modify directly for looping, pitch, etc. Use methods for playback control. Avoid setting volume directly. */
23
23
  readonly audio: HTMLAudioElement;
24
+ /** Avoid setting these directly. */
24
25
  readonly options: Required<AudioSourceOptions>;
25
26
  /** Resolves when the audio is loaded and ready to play. */
26
27
  readonly ready: Promise<void>;
27
28
  private loadState;
28
29
  private audioRef;
29
- constructor(path: string, audioRef: AudioInternal, options?: AudioSourceOptions);
30
+ constructor(path: string, audioRef: Audio, options?: AudioSourceOptions);
30
31
  isReady(): boolean;
31
32
  play(): void;
32
33
  stop(): void;
@@ -42,7 +43,15 @@ export declare class AudioSource {
42
43
  getVolume(): number;
43
44
  getDuration(): number;
44
45
  }
45
- export declare class AudioInternal {
46
+ /**
47
+ * The audio subsystem.
48
+ *
49
+ * Manages a handful of AudioSource objects, for things like global volume,
50
+ * global play/pause, etc..
51
+ *
52
+ * To make a new source, use `like.audio.newSource`.
53
+ */
54
+ export declare class Audio {
46
55
  private sources;
47
56
  private globalVolume;
48
57
  /**
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../src/audio/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qDAAqD;AACrD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAA;AAMD;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6IAA6I;IAC7I,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAC/C,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,CAAC,SAAS,CAAoE;IACrF,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,GAAE,kBAAuB;IAmC3E,OAAO,IAAI,OAAO;IAIlB,IAAI,IAAI,IAAI;IAUZ,IAAI,IAAI,IAAI;IAUZ,KAAK,IAAI,IAAI;IAQb,MAAM,IAAI,IAAI;IAYd,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ5B,IAAI,IAAI,MAAM;IAKd,SAAS,IAAI,OAAO;IAKpB,QAAQ,IAAI,OAAO;IAKnB,SAAS,IAAI,OAAO;IAKpB,+FAA+F;IAC/F,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK/B,SAAS,IAAI,MAAM;IAInB,WAAW,IAAI,MAAM;CAItB;AAED;;;;;;;GAOG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,YAAY,CAAK;IAEzB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW;IAMlE;;;QAGI;IACJ,aAAa,IAAI,WAAW,EAAE;IAS9B,OAAO,IAAI,IAAI;IAIf,QAAQ,IAAI,IAAI;IAIhB,SAAS,IAAI,IAAI;IAIjB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAO/B,SAAS,IAAI,MAAM;IAInB,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW;CAGxC"}
@@ -5,7 +5,6 @@
5
5
  *
6
6
  * ## Track and give global control to all audio objects
7
7
  * Start, stop, or set global volume for every currently playing sound.
8
- *
9
8
  */
10
9
  /**
11
10
  * Handle to a loaded audio resource, which pretends to be synchronous.
@@ -29,6 +28,7 @@ export class AudioSource {
29
28
  writable: true,
30
29
  value: void 0
31
30
  });
31
+ /** Avoid setting these directly. */
32
32
  Object.defineProperty(this, "options", {
33
33
  enumerable: true,
34
34
  configurable: true,
@@ -169,7 +169,15 @@ export class AudioSource {
169
169
  return 0;
170
170
  }
171
171
  }
172
- export class AudioInternal {
172
+ /**
173
+ * The audio subsystem.
174
+ *
175
+ * Manages a handful of AudioSource objects, for things like global volume,
176
+ * global play/pause, etc..
177
+ *
178
+ * To make a new source, use `like.audio.newSource`.
179
+ */
180
+ export class Audio {
173
181
  constructor() {
174
182
  Object.defineProperty(this, "sources", {
175
183
  enumerable: true,
@@ -0,0 +1,2 @@
1
+ export type { AudioSource, Audio, AudioSourceOptions } from "./audio";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audio/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/engine.d.ts CHANGED
@@ -1,69 +1,39 @@
1
1
  /**
2
2
  * @module engine
3
3
  * @description Core game engine - lifecycle management and event dispatch.
4
- *
5
- * You've reached the most evil part of the codebase -- the man
6
- * behind the curtain.
7
- *
8
- * The secret force gluing everything together.
9
- *
10
- * If you want to use modules independently, look here first.
11
- *
12
- * ## Memory Management
13
- *
14
- * Always call `dispose()` when destroying an engine instance:
15
- * - Removes all event listeners
16
- * - Stops the game loop
17
- * - Removes canvas from DOM
18
- * - Cleans up canvas manager resources
19
- *
20
4
  */
21
- import type { LikeInternal } from './core/like';
22
- export type EngineDispatch = Engine["dispatch"];
5
+ import type { EventType, EventMap, Dispatcher } from './events';
6
+ import type { Like } from './like';
7
+ export type EngineDispatcher = Dispatcher<EventType>;
8
+ export type EngineProps<T extends keyof EventMap> = {
9
+ canvas: HTMLCanvasElement;
10
+ abort: AbortSignal;
11
+ dispatch: Dispatcher<T>;
12
+ };
23
13
  /**
14
+ * @private
24
15
  * Core game engine managing the event loop and subsystems.
25
- *
26
- * Normally you don't instantiate this directly - use {@link createLike} instead.
27
- * The Engine class is exposed for advanced use cases like testing or
28
- * custom initialization sequences.
29
- *
30
- * All subsystems are accessible via the {@link like} property.
31
16
  */
32
17
  export declare class Engine {
33
18
  private container;
19
+ /** The canvas on which we bind all events. Not always the same canvas
20
+ * that we render to. */
34
21
  private canvas;
35
22
  private isRunning;
36
23
  private lastTime;
37
24
  private abort;
38
25
  /**
39
26
  * The Like interface providing access to all engine subsystems.
40
- * This object is passed to all scene callbacks and game code.
41
27
  */
42
- readonly like: LikeInternal;
28
+ readonly like: Like;
43
29
  constructor(container: HTMLElement);
44
30
  private dispatch;
45
31
  /**
46
32
  * Start the game loop.
47
- *
48
- * @remarks
49
- * This method:
50
- * 1. Dispatches the initial `load` event
51
- * 2. Starts the requestAnimationFrame loop
52
- *
53
- * The engine runs until dispose() is called.
54
33
  */
55
34
  start(): Promise<void>;
56
35
  /**
57
36
  * Clean up all resources and stop the engine.
58
- *
59
- * @remarks
60
- * This method:
61
- * - Stops the game loop
62
- * - Removes all event listeners (keyboard, mouse, window, fullscreen)
63
- * - Disposes canvas manager (removes resize observer)
64
- * - Removes the canvas element from the DOM
65
- *
66
- * The engine cannot be restarted after disposal - create a new instance.
67
37
  */
68
38
  dispose(): void;
69
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAUH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAEhD;;;;;;;;GAQG;AACH,qBAAa,MAAM;IAaL,OAAO,CAAC,SAAS;IAZ7B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAK;IAErB,OAAO,CAAC,KAAK,CAAyB;IAEtC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;gBAER,SAAS,EAAE,WAAW;IAmD1C,OAAO,CAAC,QAAQ;IAShB;;;;;;;;;OASG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B5B;;;;;;;;;;;OAWG;IACH,OAAO,IAAI,IAAI;CAahB"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,KAAK,EAAa,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAInC,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;AACrD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,QAAQ,IAAI;IAClD,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACzB,CAAA;AAED;;;GAGG;AACH,qBAAa,MAAM;IAaL,OAAO,CAAC,SAAS;IAZ7B;4BACwB;IACxB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,KAAK,CAAyB;IAEtC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;gBAEA,SAAS,EAAE,WAAW;IAuD1C,OAAO,CAAC,QAAQ;IAShB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B5B;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB"}
package/dist/engine.js CHANGED
@@ -1,40 +1,19 @@
1
1
  /**
2
2
  * @module engine
3
3
  * @description Core game engine - lifecycle management and event dispatch.
4
- *
5
- * You've reached the most evil part of the codebase -- the man
6
- * behind the curtain.
7
- *
8
- * The secret force gluing everything together.
9
- *
10
- * If you want to use modules independently, look here first.
11
- *
12
- * ## Memory Management
13
- *
14
- * Always call `dispose()` when destroying an engine instance:
15
- * - Removes all event listeners
16
- * - Stops the game loop
17
- * - Removes canvas from DOM
18
- * - Cleans up canvas manager resources
19
- *
20
4
  */
21
- import { AudioInternal } from './core/audio';
22
- import { InputInternal } from './core/input';
23
- import { TimerInternal } from './core/timer';
24
- import { KeyboardInternal } from './core/keyboard';
25
- import { MouseInternal } from './core/mouse';
26
- import { GamepadInternal } from './core/gamepad';
27
- import { bindGraphics } from './core/graphics';
28
- import { CanvasInternal } from './core/canvas';
5
+ import { Audio } from './audio/audio';
6
+ import { Input } from './input/input';
7
+ import { Timer } from './timer/timer';
8
+ import { Keyboard } from './input/keyboard';
9
+ import { Mouse } from './input/mouse';
10
+ import { Gamepad } from './input/gamepad';
11
+ import { bindGraphics } from './graphics/index';
12
+ import { Canvas } from './graphics/canvas';
29
13
  import { sceneDispatch } from './scene';
30
14
  /**
15
+ * @private
31
16
  * Core game engine managing the event loop and subsystems.
32
- *
33
- * Normally you don't instantiate this directly - use {@link createLike} instead.
34
- * The Engine class is exposed for advanced use cases like testing or
35
- * custom initialization sequences.
36
- *
37
- * All subsystems are accessible via the {@link like} property.
38
17
  */
39
18
  export class Engine {
40
19
  constructor(container) {
@@ -44,6 +23,8 @@ export class Engine {
44
23
  writable: true,
45
24
  value: container
46
25
  });
26
+ /** The canvas on which we bind all events. Not always the same canvas
27
+ * that we render to. */
47
28
  Object.defineProperty(this, "canvas", {
48
29
  enumerable: true,
49
30
  configurable: true,
@@ -70,7 +51,6 @@ export class Engine {
70
51
  });
71
52
  /**
72
53
  * The Like interface providing access to all engine subsystems.
73
- * This object is passed to all scene callbacks and game code.
74
54
  */
75
55
  Object.defineProperty(this, "like", {
76
56
  enumerable: true,
@@ -78,22 +58,26 @@ export class Engine {
78
58
  writable: true,
79
59
  value: void 0
80
60
  });
81
- this.canvas = new CanvasInternal(this.dispatch.bind(this));
82
- const canvas = this.canvas._displayCanvas;
83
- canvas.addEventListener("like:updateRenderTarget", (event) => {
61
+ this.canvas = document.createElement('canvas');
62
+ const canvas = new Canvas(this.canvas, this.dispatch.bind(this), this.abort.signal);
63
+ this.canvas.addEventListener("like:updateRenderTarget", (event) => {
84
64
  if (!(event instanceof CustomEvent))
85
65
  return;
86
66
  this.like.gfx = bindGraphics(event.detail.target.getContext('2d'));
87
67
  });
88
- this.container.appendChild(canvas);
89
- let gfx = bindGraphics(canvas.getContext('2d'));
90
- const dispatch = this.dispatch.bind(this);
91
- const audio = new AudioInternal();
92
- const timer = new TimerInternal();
93
- const keyboard = new KeyboardInternal(canvas, dispatch);
94
- const mouse = new MouseInternal(canvas, dispatch);
95
- const gamepad = new GamepadInternal(dispatch);
96
- const input = new InputInternal({ keyboard, mouse, gamepad });
68
+ this.container.appendChild(this.canvas);
69
+ let gfx = bindGraphics(this.canvas.getContext('2d'));
70
+ const props = {
71
+ canvas: this.canvas,
72
+ dispatch: this.dispatch.bind(this),
73
+ abort: this.abort.signal,
74
+ };
75
+ const audio = new Audio();
76
+ const timer = new Timer(props);
77
+ const keyboard = new Keyboard(props);
78
+ const mouse = new Mouse(props);
79
+ const gamepad = new Gamepad(props);
80
+ const input = new Input(props.dispatch, { keyboard, mouse, gamepad });
97
81
  this.like = {
98
82
  audio,
99
83
  timer,
@@ -102,13 +86,14 @@ export class Engine {
102
86
  mouse,
103
87
  gamepad,
104
88
  gfx,
105
- canvas: this.canvas,
89
+ canvas,
106
90
  start: this.start.bind(this),
107
91
  dispose: this.dispose.bind(this),
108
92
  setScene: (scene) => {
109
93
  if (scene) {
110
94
  this.like.handleEvent = (event) => sceneDispatch(scene, this.like, event);
111
- this.dispatch("load", []);
95
+ if (this.isRunning)
96
+ this.dispatch("load", []);
112
97
  }
113
98
  else {
114
99
  this.like.handleEvent = undefined;
@@ -121,8 +106,7 @@ export class Engine {
121
106
  };
122
107
  window.addEventListener('focus', () => this.dispatch('focus', ['tab']));
123
108
  window.addEventListener('blur', () => this.dispatch('blur', ['tab']));
124
- canvas.addEventListener('focus', () => this.dispatch('focus', ['canvas']));
125
- canvas.addEventListener('focus', () => this.dispatch('focus', ['canvas']));
109
+ this.canvas.addEventListener('focus', () => this.dispatch('focus', ['canvas']));
126
110
  }
127
111
  dispatch(type, args) {
128
112
  const event = { type, args, timestamp: this.like.timer.getTime() };
@@ -135,13 +119,6 @@ export class Engine {
135
119
  }
136
120
  /**
137
121
  * Start the game loop.
138
- *
139
- * @remarks
140
- * This method:
141
- * 1. Dispatches the initial `load` event
142
- * 2. Starts the requestAnimationFrame loop
143
- *
144
- * The engine runs until dispose() is called.
145
122
  */
146
123
  async start() {
147
124
  this.isRunning = true;
@@ -153,14 +130,12 @@ export class Engine {
153
130
  const dt = (now - this.lastTime) / 1000;
154
131
  this.lastTime = now;
155
132
  if (!this.like.timer.isSleeping()) {
156
- this.like.timer._update(dt);
157
- const { pressed, released } = this.like.input._update();
158
- pressed.forEach(action => this.dispatch('actionpressed', [action]));
159
- released.forEach(action => this.dispatch('actionreleased', [action]));
133
+ this.canvas.dispatchEvent(new CustomEvent("like:update", { detail: { dt } }));
160
134
  this.dispatch('update', [dt]);
161
135
  }
136
+ this.canvas.dispatchEvent(new CustomEvent("like:preDraw"));
162
137
  this.dispatch('draw', []);
163
- this.canvas._present();
138
+ this.canvas.dispatchEvent(new CustomEvent("like:postDraw"));
164
139
  requestAnimationFrame(loop);
165
140
  };
166
141
  this.dispatch('load', []);
@@ -168,26 +143,9 @@ export class Engine {
168
143
  }
169
144
  /**
170
145
  * Clean up all resources and stop the engine.
171
- *
172
- * @remarks
173
- * This method:
174
- * - Stops the game loop
175
- * - Removes all event listeners (keyboard, mouse, window, fullscreen)
176
- * - Disposes canvas manager (removes resize observer)
177
- * - Removes the canvas element from the DOM
178
- *
179
- * The engine cannot be restarted after disposal - create a new instance.
180
146
  */
181
147
  dispose() {
182
- const canvas = this.canvas._displayCanvas;
183
148
  this.isRunning = false;
184
- this.like.keyboard._dispose();
185
- this.like.mouse._dispose();
186
- this.like.gamepad._dispose();
187
- this.canvas._dispose();
188
149
  this.abort.abort();
189
- if (canvas.parentNode === this.container) {
190
- this.container.removeChild(canvas);
191
- }
192
150
  }
193
151
  }
@@ -1,57 +1,31 @@
1
1
  /**
2
2
  * @module events
3
3
  * @description All events that flow through the engine.
4
- *
5
- * ## Overview
6
- *
7
- * LIKE uses events at its core.
8
- * These pass through the engine and down to your
9
- * callbacks or scene.
10
- *
11
- * This module is the single source of truth for what
12
- * events are possible.
13
- *
14
- * Use it as a reference.
15
- *
16
- * @see {@link EventMap} lists every event.
17
- * @see {@link Scene} for implementing callbacks in a class
18
- * @see {@link Like} for global callback assignment
19
- * @see {@link Input} for action mapping
20
4
  */
21
- import type { Vector2 } from '../math/vector2';
22
- import { LikeButton } from './gamepad-mapping';
5
+ import type { Vector2 } from './math/vector2';
6
+ import type { LikeButton } from './input';
23
7
  export type MouseButton = 'left' | 'middle' | 'right';
24
8
  declare global {
25
9
  interface HTMLElementEventMap {
26
10
  ['like:mousemoved']: CustomEvent<{
27
11
  pos: Vector2;
28
12
  delta: Vector2;
29
- renderSize: Vector2;
30
13
  }>;
31
14
  ['like:updateRenderTarget']: CustomEvent<{
32
15
  target: HTMLCanvasElement;
33
16
  }>;
17
+ ['like:resizeCanvas']: CustomEvent<{
18
+ size: Vector2;
19
+ }>;
20
+ ['like:preDraw']: CustomEvent<{}>;
21
+ ['like:postDraw']: CustomEvent<{}>;
22
+ ['like:update']: CustomEvent<{
23
+ dt: number;
24
+ }>;
34
25
  }
35
26
  }
36
27
  /**
37
28
  * The master type will all events on it.
38
- *
39
- * Each frame:
40
- * 1. `update(dt)` - Game logic with delta time in seconds
41
- * 2. `draw` - Render the frame
42
- *
43
- * Input events fire immediately when they occur:
44
- * - `keypressed`/`keyreleased` - Keyboard input
45
- * - `mousemoved`/`mousepressed`/`mousereleased` - Mouse input
46
- * - `gamepadpressed`/`gamepadreleased` - Controller input
47
- * - `actionpressed`/`actionreleased` - Mapped actions (see {@link Input})
48
- *
49
- * Window events:
50
- * - `focus`/`blur` - Tab/window focus changes
51
- * - `resize` - Canvas size changes
52
- *
53
- * Lifecycle:
54
- * - `load` - Called once when the game starts
55
29
  */
56
30
  export type EventMap = {
57
31
  /** Game initialization. Called once before the first frame. */
@@ -76,28 +50,31 @@ export type EventMap = {
76
50
  mousepressed: [pos: Vector2, button: MouseButton];
77
51
  /** Mouse button released. */
78
52
  mousereleased: [pos: Vector2, button: MouseButton];
79
- /** Gamepad button pressed. index is controller number (0-3). */
80
- gamepadpressed: [source: number, num: number, name: LikeButton];
81
- /** Gamepad button released. */
82
- gamepadreleased: [source: number, num: number, name: LikeButton];
53
+ /** Gamepad button pressed. `source` is controller index, `name` is derived from a mapping on the raw `num` */
54
+ gamepadpressed: [source: number, name: LikeButton, num: number];
55
+ /** Gamepad button released. `source` is controller index, `name` is derived from a mapping on the raw `num` */
56
+ gamepadreleased: [source: number, name: LikeButton, num: number];
57
+ /**
58
+ * Fires when a gamepad is connected.
59
+ */
60
+ gamepadconnected: [index: number];
61
+ /** Fires when a gamepad is disconnected. */
62
+ gamepaddisconnected: [index: number];
83
63
  /** Mapped action triggered. See {@link Input} for action mapping. */
84
64
  actionpressed: [action: string];
85
65
  /** Mapped action released. */
86
66
  actionreleased: [action: string];
87
67
  };
88
68
  export type EventType = keyof EventMap;
69
+ export type LikeMouseEvent = 'mousemoved' | 'mousepressed' | 'mousereleased';
70
+ export type LikeKeyboardEvent = 'keypressed' | 'keyreleased';
71
+ export type LikeGamepadEvent = 'gamepadpressed' | 'gamepadreleased' | 'gamepadconnected' | 'gamepaddisconnected';
72
+ /**
73
+ * Generic dispatcher - each module defines its own event subset
74
+ */
75
+ export type Dispatcher<T extends EventType> = <K extends T>(type: K, args: EventMap[K]) => void;
89
76
  /**
90
77
  * Discriminated union of all event objects.
91
- * Use this with `handleEvent` to receive all events generically.
92
- *
93
- * @example
94
- * ```typescript
95
- * handleEvent(like: Like, event: Like2DEvent) {
96
- * if (event.type === 'update') {
97
- * const dt = event.args[0];
98
- * }
99
- * }
100
- * ```
101
78
  */
102
79
  export type LikeEvent = {
103
80
  [K in EventType]: {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}