@dryanovski/gamefoo 0.2.1 → 0.2.5

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 (84) hide show
  1. package/dist/core/animate.js +147 -0
  2. package/dist/core/asset.js +74 -0
  3. package/dist/core/behaviour.js +88 -0
  4. package/dist/core/behaviours/collidable.js +186 -0
  5. package/dist/core/behaviours/control.js +75 -0
  6. package/dist/core/behaviours/healtkit.js +153 -0
  7. package/dist/core/behaviours/sprite_render.js +193 -0
  8. package/dist/core/camera.js +134 -0
  9. package/dist/core/engine.d.ts +1 -1
  10. package/dist/core/engine.d.ts.map +1 -1
  11. package/dist/core/engine.js +527 -0
  12. package/dist/core/fonts/font_bitmap.js +205 -0
  13. package/dist/core/fonts/font_bitmap_prebuild.js +137 -0
  14. package/dist/core/fonts/internal/font_3x5.js +169 -0
  15. package/dist/core/fonts/internal/font_4x6.js +171 -0
  16. package/dist/core/fonts/internal/font_5x5.js +129 -0
  17. package/dist/core/fonts/internal/font_6x8.js +171 -0
  18. package/dist/core/fonts/internal/font_8x13.js +171 -0
  19. package/dist/core/fonts/internal/font_8x8.js +171 -0
  20. package/dist/core/game_object_register.js +134 -0
  21. package/dist/core/input.js +170 -0
  22. package/dist/core/sprite.js +222 -0
  23. package/dist/core/utils/perlin_noise.js +183 -0
  24. package/dist/core/world.js +304 -0
  25. package/dist/debug/monitor.js +47 -0
  26. package/dist/decorators/index.js +1 -0
  27. package/dist/decorators/log.js +42 -0
  28. package/dist/entities/dynamic_entity.js +99 -0
  29. package/dist/entities/entity.js +283 -0
  30. package/dist/entities/player.js +93 -0
  31. package/dist/entities/text.js +62 -0
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +47 -29
  35. package/dist/subsystems/camera_system.d.ts +2 -2
  36. package/dist/subsystems/camera_system.d.ts.map +1 -1
  37. package/dist/subsystems/camera_system.js +41 -0
  38. package/dist/subsystems/collision_system.js +17 -0
  39. package/dist/subsystems/monitor_system.js +20 -0
  40. package/dist/subsystems/object_system.d.ts +1 -1
  41. package/dist/subsystems/object_system.d.ts.map +1 -1
  42. package/dist/subsystems/object_system.js +29 -0
  43. package/dist/subsystems/types.d.ts +1 -1
  44. package/dist/subsystems/types.d.ts.map +1 -1
  45. package/dist/subsystems/types.js +0 -0
  46. package/dist/types.js +10 -0
  47. package/package.json +17 -6
  48. package/src/core/animate.ts +159 -0
  49. package/src/core/asset.ts +76 -0
  50. package/src/core/behaviour.ts +145 -0
  51. package/src/core/behaviours/collidable.ts +296 -0
  52. package/src/core/behaviours/control.ts +80 -0
  53. package/src/core/behaviours/healtkit.ts +166 -0
  54. package/src/core/behaviours/sprite_render.ts +216 -0
  55. package/src/core/camera.ts +145 -0
  56. package/src/core/engine.ts +607 -0
  57. package/src/core/fonts/font_bitmap.ts +232 -0
  58. package/src/core/fonts/font_bitmap_prebuild.ts +141 -0
  59. package/src/core/fonts/internal/font_3x5.ts +178 -0
  60. package/src/core/fonts/internal/font_4x6.ts +180 -0
  61. package/src/core/fonts/internal/font_5x5.ts +137 -0
  62. package/src/core/fonts/internal/font_6x8.ts +180 -0
  63. package/src/core/fonts/internal/font_8x13.ts +180 -0
  64. package/src/core/fonts/internal/font_8x8.ts +180 -0
  65. package/src/core/game_object_register.ts +146 -0
  66. package/src/core/input.ts +182 -0
  67. package/src/core/sprite.ts +339 -0
  68. package/src/core/utils/perlin_noise.ts +196 -0
  69. package/src/core/world.ts +331 -0
  70. package/src/debug/monitor.ts +60 -0
  71. package/src/decorators/index.ts +1 -0
  72. package/src/decorators/log.ts +45 -0
  73. package/src/entities/dynamic_entity.ts +106 -0
  74. package/src/entities/entity.ts +322 -0
  75. package/src/entities/player.ts +99 -0
  76. package/src/entities/text.ts +72 -0
  77. package/src/index.ts +51 -0
  78. package/src/subsystems/camera_system.ts +52 -0
  79. package/src/subsystems/collision_system.ts +21 -0
  80. package/src/subsystems/monitor_system.ts +26 -0
  81. package/src/subsystems/object_system.ts +37 -0
  82. package/src/subsystems/types.ts +46 -0
  83. package/src/types.ts +178 -0
  84. package/dist/index.js.map +0 -9
@@ -0,0 +1,322 @@
1
+ import type { Behaviour } from "../core/behaviour";
2
+ import type { Demension, Vector2 } from "../types";
3
+
4
+ /**
5
+ * Abstract base class for every game entity in the GameFoo engine.
6
+ *
7
+ * `Entity` provides:
8
+ *
9
+ * - **Identity** — a unique string {@link Entity.id | id}.
10
+ * - **Transform** — a 2-D {@link Entity.position | position} and
11
+ * {@link Entity.size | size} with convenient `x`/`y` accessors.
12
+ * - **Behaviour system** — attach, detach, query, and bulk-update
13
+ * {@link Behaviour} instances that compose an entity's logic.
14
+ *
15
+ * Subclasses must implement {@link Entity.update} and
16
+ * {@link Entity.render}.
17
+ *
18
+ * @category Entities
19
+ * @since 0.1.0
20
+ *
21
+ * @example Subclassing
22
+ * ```ts
23
+ * import { Entity } from "gamefoo";
24
+ *
25
+ * class Wall extends Entity {
26
+ * constructor(x: number, y: number, w: number, h: number) {
27
+ * super("wall", x, y, w, h);
28
+ * }
29
+ *
30
+ * update(_dt: number) {}
31
+ *
32
+ * render(ctx: CanvasRenderingContext2D) {
33
+ * ctx.fillStyle = "#888";
34
+ * ctx.fillRect(this.x, this.y, this.size.width, this.size.height);
35
+ * }
36
+ * }
37
+ * ```
38
+ *
39
+ * @example Attaching behaviours
40
+ * ```ts
41
+ * const entity = new Wall(0, 0, 100, 20);
42
+ * entity.attachBehaviour(new Collidable(entity, world, { ... }));
43
+ *
44
+ * if (entity.hasBehaviour("collidable")) {
45
+ * console.log("Wall has collision!");
46
+ * }
47
+ * ```
48
+ *
49
+ * @see {@link DynamicEntity} — extends Entity with velocity / speed
50
+ * @see {@link Player} — concrete player entity
51
+ * @see {@link Behaviour} — composable logic units
52
+ */
53
+ export default abstract class Entity {
54
+ /**
55
+ * Unique identifier for this entity.
56
+ *
57
+ * Used as the key in {@link GameObjectRegister} and for
58
+ * collision-callback identification.
59
+ */
60
+ public id: string = "";
61
+
62
+ /**
63
+ * World-space position of the entity's origin (top-left corner).
64
+ */
65
+ protected readonly position: Vector2 = { x: 0, y: 0 };
66
+
67
+ /**
68
+ * Bounding dimensions of the entity in pixels.
69
+ */
70
+ protected readonly size: Demension = { width: 0, height: 0 };
71
+
72
+ /**
73
+ * Internal map from behaviour key (lowercased type) to
74
+ * {@link Behaviour} instance.
75
+ */
76
+ private behaviorMap: Map<string, Behaviour> = new Map();
77
+
78
+ /**
79
+ * Priority-sorted cache of behaviours. Invalidated (`null`) whenever
80
+ * a behaviour is attached or detached.
81
+ */
82
+ private _sortedBehaviors: Behaviour[] | null = null;
83
+
84
+ /**
85
+ * Horizontal position of the entity (shorthand for
86
+ * `position.x`).
87
+ */
88
+ get x(): number {
89
+ return this.position.x;
90
+ }
91
+
92
+ /** Sets the horizontal position. */
93
+ set x(value: number) {
94
+ this.position.x = value;
95
+ }
96
+
97
+ /**
98
+ * Vertical position of the entity (shorthand for
99
+ * `position.y`).
100
+ */
101
+ get y(): number {
102
+ return this.position.y;
103
+ }
104
+
105
+ /** Sets the vertical position. */
106
+ set y(value: number) {
107
+ this.position.y = value;
108
+ }
109
+
110
+ /**
111
+ * Creates a new entity.
112
+ *
113
+ * @param id - Unique string identifier.
114
+ * @param x - Initial X position in pixels.
115
+ * @param y - Initial Y position in pixels.
116
+ * @param width - Width of the entity's bounding box in pixels.
117
+ * @param height - Height of the entity's bounding box in pixels.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * class Crate extends Entity {
122
+ * constructor(x: number, y: number) {
123
+ * super("crate", x, y, 32, 32);
124
+ * }
125
+ * // ...
126
+ * }
127
+ * ```
128
+ */
129
+ constructor(id: string, x: number, y: number, width?: number, height?: number) {
130
+ this.id = id;
131
+ this.position = { x, y };
132
+
133
+ if (width && height) {
134
+ this.size = { width, height };
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Advances the entity's state by one frame.
140
+ *
141
+ * @param deltaTime - Seconds elapsed since the previous frame.
142
+ */
143
+ abstract update(deltaTime: number): void;
144
+
145
+ /**
146
+ * Draws the entity to the canvas.
147
+ *
148
+ * @param ctx - The 2-D rendering context.
149
+ */
150
+ abstract render(ctx: CanvasRenderingContext2D): void;
151
+
152
+ /**
153
+ * Returns a **copy** of the entity's current position.
154
+ *
155
+ * @returns A new {@link Vector2} with the entity's `x` and `y`.
156
+ */
157
+ getPosition(): Vector2 {
158
+ return this.position;
159
+ }
160
+
161
+ /**
162
+ * Returns a **copy** of the entity's bounding dimensions.
163
+ *
164
+ * @returns An object with `width` and `height`.
165
+ */
166
+ getSize(): Demension {
167
+ return this.size;
168
+ }
169
+
170
+ /**
171
+ * Set size of the entity
172
+ *
173
+ * @since 0.2.0
174
+ *
175
+ * @return void
176
+ */
177
+ setSize(width: number, height: number): void {
178
+ this.size.width = width;
179
+ this.size.height = height;
180
+ }
181
+
182
+ /**
183
+ * Retrieves a behaviour by its key (case-insensitive).
184
+ *
185
+ * @typeParam T - The expected concrete behaviour type.
186
+ * @param key - The behaviour's {@link Behaviour.type | type} string.
187
+ * @returns The behaviour cast to `T`, or `undefined` if not found.
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * const ctrl = entity.getBehaviour<Control>("control");
192
+ * if (ctrl) ctrl.enabled = false;
193
+ * ```
194
+ */
195
+ getBehaviour<T extends Behaviour>(key: string): T | undefined {
196
+ return this.behaviorMap.get(key.toLowerCase()) as T | undefined;
197
+ }
198
+
199
+ /**
200
+ * Returns all attached behaviours that are instances of the given
201
+ * class.
202
+ *
203
+ * @typeParam T - The behaviour subclass to filter by.
204
+ * @param type - The constructor function to test with `instanceof`.
205
+ * @returns An array of matching behaviours.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const renderers = entity.getBehavioursByType(SpriteRender);
210
+ * ```
211
+ */
212
+ getBehavioursByType<T extends Behaviour>(type: new (...args: any[]) => T): T[] {
213
+ return this.behaviors.filter((b) => b instanceof type) as T[];
214
+ }
215
+
216
+ /**
217
+ * Checks whether a behaviour with the given key is attached.
218
+ *
219
+ * @param key - The behaviour's {@link Behaviour.type | type} string
220
+ * (case-insensitive).
221
+ * @returns `true` if the behaviour exists on this entity.
222
+ */
223
+ hasBehaviour(key: string): boolean {
224
+ return this.behaviorMap.has(key.toLowerCase());
225
+ }
226
+
227
+ /**
228
+ * Attaches a behaviour to this entity.
229
+ *
230
+ * If the behaviour defines an {@link Behaviour.onAttach | onAttach}
231
+ * hook, it is called immediately. The sorted-behaviour cache is
232
+ * invalidated.
233
+ *
234
+ * @typeParam T - The behaviour type being attached.
235
+ * @param behavior - The behaviour instance to add.
236
+ * @returns The same behaviour instance (for chaining).
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * const hk = entity.attachBehaviour(new HealthKit(entity, 100));
241
+ * hk.takeDamage(10);
242
+ * ```
243
+ */
244
+ attachBehaviour<T extends Behaviour>(behavior: T): T {
245
+ this.behaviorMap.set(behavior.key, behavior);
246
+ this._sortedBehaviors = null;
247
+
248
+ if (behavior.onAttach) {
249
+ behavior.onAttach();
250
+ }
251
+ return behavior;
252
+ }
253
+
254
+ /**
255
+ * Detaches a behaviour by its key and calls
256
+ * {@link Behaviour.onDetach | onDetach} if defined.
257
+ *
258
+ * @param key - The behaviour's {@link Behaviour.type | type} string
259
+ * (case-insensitive).
260
+ *
261
+ * @example
262
+ * ```ts
263
+ * entity.detachBehaviour("collidable");
264
+ * ```
265
+ */
266
+ detachBehaviour(key: string): void {
267
+ const behavior = this.behaviorMap.get(key.toLowerCase());
268
+ if (!behavior) return;
269
+
270
+ if (behavior.onDetach) {
271
+ behavior.onDetach();
272
+ }
273
+ this.behaviorMap.delete(key.toLowerCase());
274
+ this._sortedBehaviors = null;
275
+ }
276
+
277
+ /**
278
+ * Returns all attached behaviours sorted by
279
+ * {@link Behaviour.priority} (ascending). The result is cached and
280
+ * only re-computed when behaviours are added or removed.
281
+ *
282
+ * @internal
283
+ */
284
+ private get behaviors(): Behaviour[] {
285
+ if (!this._sortedBehaviors) {
286
+ this._sortedBehaviors = Array.from(this.behaviorMap.values()).sort((a, b) => a.priority - b.priority);
287
+ }
288
+ return this._sortedBehaviors;
289
+ }
290
+
291
+ /**
292
+ * Calls {@link Behaviour.update | update(deltaTime)} on every
293
+ * enabled behaviour, in priority order.
294
+ *
295
+ * Typically called from a subclass's `update` implementation.
296
+ *
297
+ * @param deltaTime - Seconds elapsed since the previous frame.
298
+ */
299
+ protected updateBehaviours(deltaTime: number): void {
300
+ for (const behavior of this.behaviors) {
301
+ if (behavior.enabled) {
302
+ behavior.update(deltaTime);
303
+ }
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Calls {@link Behaviour.render | render(ctx)} on every enabled
309
+ * behaviour that defines a render method, in priority order.
310
+ *
311
+ * Typically called from a subclass's `render` implementation.
312
+ *
313
+ * @param ctx - The canvas 2-D rendering context.
314
+ */
315
+ protected renderBehaviours(ctx: CanvasRenderingContext2D): void {
316
+ for (const behavior of this.behaviors) {
317
+ if (behavior.enabled && behavior.render) {
318
+ behavior.render(ctx);
319
+ }
320
+ }
321
+ }
322
+ }
@@ -0,0 +1,99 @@
1
+ import type { Control } from "../core/behaviours/control";
2
+ import type { HealthKit } from "../core/behaviours/healtkit";
3
+ import DynamicEntity from "./dynamic_entity";
4
+
5
+ /**
6
+ * Default player entity with convenience accessors for common
7
+ * behaviours.
8
+ *
9
+ * `Player` extends {@link DynamicEntity} and automatically delegates
10
+ * its {@link Player.update | update} and {@link Player.render | render}
11
+ * calls to all attached {@link Behaviour | behaviours}. It also
12
+ * provides typed getters for the {@link Control} and
13
+ * {@link HealthKit} behaviours so game code can access them without
14
+ * manual casting.
15
+ *
16
+ * Subclass `Player` to customise rendering, add game-specific logic,
17
+ * or bind additional behaviours.
18
+ *
19
+ * @category Entities
20
+ * @since 0.1.0
21
+ *
22
+ * @example Basic usage
23
+ * ```ts
24
+ * import { Player, Control, HealthKit, Input } from "gamefoo";
25
+ *
26
+ * const player = new Player("hero", 400, 300, 50, 50);
27
+ *
28
+ * player.attachBehaviour(new Control(player, new Input()));
29
+ * player.attachBehaviour(new HealthKit(player, 100));
30
+ *
31
+ * player.control?.enabled; // true
32
+ * player.healthkit?.getHealth(); // 100
33
+ * ```
34
+ *
35
+ * @example Subclassing for custom rendering
36
+ * ```ts
37
+ * class Knight extends Player {
38
+ * constructor(x: number, y: number) {
39
+ * super("knight", x, y, 48, 48);
40
+ * }
41
+ *
42
+ * override render(ctx: CanvasRenderingContext2D) {
43
+ * ctx.fillStyle = "#c0c0c0";
44
+ * ctx.fillRect(this.x, this.y, 48, 48);
45
+ * this.renderBehaviours(ctx);
46
+ * }
47
+ * }
48
+ * ```
49
+ *
50
+ * @see {@link DynamicEntity} — parent class (velocity, speed)
51
+ * @see {@link Control} — keyboard movement behaviour
52
+ * @see {@link HealthKit} — health-tracking behaviour
53
+ */
54
+ export default class Player extends DynamicEntity {
55
+ /**
56
+ * Convenience getter for the attached {@link Control} behaviour.
57
+ *
58
+ * @returns The `Control` instance, or `undefined` if not attached.
59
+ */
60
+ get control(): Control | undefined {
61
+ return this.getBehaviour<Control>("control");
62
+ }
63
+
64
+ /**
65
+ * Convenience getter for the attached {@link HealthKit} behaviour.
66
+ *
67
+ * @returns The `HealthKit` instance, or `undefined` if not attached.
68
+ */
69
+ get healthkit(): HealthKit | undefined {
70
+ return this.getBehaviour<HealthKit>("healthkit");
71
+ }
72
+
73
+ /**
74
+ * Advances all attached behaviours.
75
+ *
76
+ * @inheritDoc
77
+ * @param deltaTime - Seconds elapsed since the previous frame.
78
+ */
79
+ update(deltaTime: number): void {
80
+ this.updateBehaviours(deltaTime);
81
+ }
82
+
83
+ /**
84
+ * Draws a default blue rectangle and then renders all attached
85
+ * behaviours (e.g. sprite overlays, health bars).
86
+ *
87
+ * Override this in a subclass for custom visuals.
88
+ *
89
+ * TODO: write a better default player object
90
+ *
91
+ * @inheritDoc
92
+ * @param ctx - The canvas 2-D rendering context.
93
+ */
94
+ render(ctx: CanvasRenderingContext2D): void {
95
+ ctx.fillStyle = "blue";
96
+ ctx.fillRect(this.x, this.y, this.size.width, this.size.height);
97
+ this.renderBehaviours(ctx);
98
+ }
99
+ }
@@ -0,0 +1,72 @@
1
+ import FontBitmap, { type InternalBitmapFontName } from "../core/fonts/font_bitmap";
2
+ import Entity from "./entity";
3
+
4
+ /**
5
+ * Abstract base class for Text and Label alike objects
6
+ *
7
+ * `Text` extends {@link Entity} and could also get all behaviors attach to it but
8
+ * most likely there will be no need for that. Its primary design use case is to
9
+ * keep track of text objects and interact with them
10
+ *
11
+ * @category Entities
12
+ * @since 0.2.0
13
+ *
14
+ * @see {@link Entity} - parent class
15
+ */
16
+ export default abstract class Text extends Entity {
17
+ /** Bitmap font name to load internally */
18
+ protected fontName: string;
19
+
20
+ /** BitmapFont instance used to manipulate the font */
21
+ protected font: FontBitmap;
22
+
23
+ /** Internal state of the text needed to be update */
24
+ protected text: string = "";
25
+
26
+ /**
27
+ * Create new Text object that could be placed and render on the screen
28
+ *
29
+ * @param fontName - the FontBitmap valid name to load
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const Label = new Text('CustomLabel', '5x5');
34
+ * ```
35
+ */
36
+ constructor(id: string, fontName: InternalBitmapFontName) {
37
+ super(id, 0, 0);
38
+
39
+ this.fontName = fontName;
40
+
41
+ this.font = new FontBitmap(fontName);
42
+ }
43
+
44
+ /**
45
+ * Set internal state value
46
+ *
47
+ * @param text - any content that should be render
48
+ *
49
+ * @return void
50
+ */
51
+ setText(text: string) {
52
+ this.text = text;
53
+
54
+ this.setSize(this.font.width * this.text.length, this.font.height);
55
+ }
56
+
57
+ /**
58
+ * Get the internal state of the object
59
+ *
60
+ * @return string
61
+ */
62
+ getText(): string {
63
+ return this.text;
64
+ }
65
+
66
+ /**
67
+ * Render the text to the canvas using the BitmapFont instance
68
+ */
69
+ override render(ctx: CanvasRenderingContext2D): void {
70
+ this.font.renderText(this.getText(), this.x, this.y, ctx);
71
+ }
72
+ }
package/src/index.ts ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * GameFoo — a lightweight 2-D canvas game engine.
3
+ *
4
+ * This barrel module re-exports every public class, behaviour, utility,
5
+ * and type so consumers can import from a single entry point:
6
+ *
7
+ * ```ts
8
+ * import { Engine, Player, Input, Collidable } from "gamefoo";
9
+ * ```
10
+ *
11
+ * @category Core
12
+ * @module gamefoo
13
+ * @since 0.1.0
14
+ */
15
+
16
+ // --- Assets / Rendering ───────────────────────────────────────────────
17
+ export { default as Asset } from "./core/asset";
18
+ // ── Core ────────────────────────────────────────────────────────────
19
+ export { Behaviour } from "./core/behaviour";
20
+ export { Collidable } from "./core/behaviours/collidable";
21
+ export { Control } from "./core/behaviours/control";
22
+ export { HealthKit } from "./core/behaviours/healtkit";
23
+ export { default as SpriteRender } from "./core/behaviours/sprite_render";
24
+ export { default as Camera } from "./core/camera";
25
+ export { default as Engine } from "./core/engine";
26
+ export { default as FontBitmap } from "./core/fonts/font_bitmap";
27
+ export { default as FontBitmapPrebuild } from "./core/fonts/font_bitmap_prebuild";
28
+ export { default as GameObjectRegister } from "./core/game_object_register";
29
+ export { default as Input } from "./core/input";
30
+ export { default as Sprite } from "./core/sprite";
31
+ // ── Utilities ───────────────────────────────────────────────────────
32
+ export { PerlinNoise } from "./core/utils/perlin_noise";
33
+ // ── World / Physics ─────────────────────────────────────────────────
34
+ export { default as World } from "./core/world";
35
+ // -- Debug -----
36
+ export { default as Monitor } from "./debug/monitor";
37
+ // ── Decorators ───────────────────────────────────────────────────────
38
+ export { log } from "./decorators/index";
39
+ // ── Entities ────────────────────────────────────────────────────────
40
+ export { default as DynamicEntity } from "./entities/dynamic_entity";
41
+ export { default as Entity } from "./entities/entity";
42
+ export { default as Player } from "./entities/player";
43
+ export { default as Text } from "./entities/text";
44
+ export { CameraSystem } from "./subsystems/camera_system";
45
+ // ── Subsystems ───────────────────────────────────────────────────
46
+ export { CollisionSystem } from "./subsystems/collision_system";
47
+ export { MonitorSystem } from "./subsystems/monitor_system";
48
+ export { ObjectSystem } from "./subsystems/object_system";
49
+ export type { SubSystem } from "./subsystems/types";
50
+ // ── Type-only exports ───────────────────────────────────────────────
51
+ export type { ColliderShape, CollisionInfo, Demension, GameObject, Vector2 } from "./types";
@@ -0,0 +1,52 @@
1
+ import Camera from "../core/camera";
2
+ import type { Vector2 } from "../types";
3
+ import type { SubSystem } from "./types";
4
+
5
+ /**
6
+ * CameraSystem is responsible for managing the camera's position and view.
7
+ * It can follow a target (like a player) and adjust the view accordingly.
8
+ *
9
+ * @since 0.2.0
10
+ *
11
+ * @category SubSystems
12
+ */
13
+ export class CameraSystem implements SubSystem {
14
+ /**
15
+ * The unique identifier for this subsystem.
16
+ */
17
+ id = "camera";
18
+ order = 10;
19
+
20
+ camera: Camera;
21
+
22
+ private target: () => Vector2 | null = () => null;
23
+
24
+ constructor(width: number, height: number, target: () => Vector2 | null) {
25
+ this.camera = new Camera(width, height);
26
+
27
+ if (target) {
28
+ this.target = target;
29
+ }
30
+ }
31
+
32
+ update() {
33
+ let v = null;
34
+ if (this.target) {
35
+ v = this.target();
36
+ }
37
+
38
+ if (v !== null) {
39
+ this.camera.follow(v);
40
+ }
41
+ }
42
+
43
+ preRender(ctx: CanvasRenderingContext2D) {
44
+ const v = this.camera.getViewRect();
45
+ ctx.save();
46
+ ctx.translate(-v.x, -v.y);
47
+ }
48
+
49
+ postRender(ctx: CanvasRenderingContext2D) {
50
+ ctx.restore();
51
+ }
52
+ }
@@ -0,0 +1,21 @@
1
+ import World from "../core/world";
2
+ import type { SubSystem } from "./types";
3
+
4
+ /**
5
+ * CollisionSystem is responsible for detecting collisions between game objects.
6
+ * It uses the World class to manage and detect collisions based on registered collidable objects.
7
+ *
8
+ * @since 0.2.0
9
+ *
10
+ * @category SubSystems
11
+ */
12
+ export class CollisionSystem implements SubSystem {
13
+ id = "collision";
14
+ order = 30;
15
+
16
+ private world = new World();
17
+
18
+ update() {
19
+ this.world.detect();
20
+ }
21
+ }
@@ -0,0 +1,26 @@
1
+ import Monitor from "../debug/monitor";
2
+ import type { SubSystem } from "./types";
3
+
4
+ /**
5
+ * MonitorSystem is responsible for displaying debug information on the screen.
6
+ * It uses the Monitor class to track and render various performance metrics,
7
+ *
8
+ * @since 0.2.0
9
+ *
10
+ * @category SubSystems
11
+ */
12
+ export class MonitorSystem implements SubSystem {
13
+ id = "monitor";
14
+
15
+ order = 100;
16
+
17
+ private monitor = new Monitor();
18
+
19
+ update(deltaTime: number) {
20
+ this.monitor.update(deltaTime);
21
+ }
22
+
23
+ render(ctx: CanvasRenderingContext2D) {
24
+ this.monitor.render(ctx);
25
+ }
26
+ }
@@ -0,0 +1,37 @@
1
+ import GameObjectRegister from "../core/game_object_register";
2
+ import type { GameObject } from "../types";
3
+ import type { SubSystem } from "./types";
4
+
5
+ /**
6
+ * ObjectSystem is responsible for managing all non-player game objects within the engine.
7
+ * It maintains a central registry of game objects and delegates per-frame update and render calls to them.
8
+ *
9
+ * @since 0.2.0
10
+ * @category SubSystems
11
+ */
12
+ export class ObjectSystem implements SubSystem {
13
+ id = "objects";
14
+
15
+ order = 20;
16
+
17
+ private objects: GameObjectRegister = new GameObjectRegister();
18
+
19
+ constructor(objects: GameObject[] = []) {
20
+ for (const obj of objects) {
21
+ this.objects.register(obj);
22
+ }
23
+ }
24
+
25
+ update(deltaTime: number) {
26
+ if (this.objects) {
27
+ console.time("ObjectSystem Update");
28
+ this.objects.updateAll(deltaTime);
29
+ }
30
+ }
31
+
32
+ render(ctx: CanvasRenderingContext2D) {
33
+ if (this.objects) {
34
+ this.objects.renderAll(ctx);
35
+ }
36
+ }
37
+ }