@dryanovski/gamefoo 0.2.3 → 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.
- package/dist/core/animate.js +147 -0
- package/dist/core/asset.js +74 -0
- package/dist/core/behaviour.js +88 -0
- package/dist/core/behaviours/collidable.js +186 -0
- package/dist/core/behaviours/control.js +75 -0
- package/dist/core/behaviours/healtkit.js +153 -0
- package/dist/core/behaviours/sprite_render.js +193 -0
- package/dist/core/camera.js +134 -0
- package/dist/core/engine.d.ts +1 -1
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +527 -0
- package/dist/core/fonts/font_bitmap.js +205 -0
- package/dist/core/fonts/font_bitmap_prebuild.js +137 -0
- package/dist/core/fonts/internal/font_3x5.js +169 -0
- package/dist/core/fonts/internal/font_4x6.js +171 -0
- package/dist/core/fonts/internal/font_5x5.js +129 -0
- package/dist/core/fonts/internal/font_6x8.js +171 -0
- package/dist/core/fonts/internal/font_8x13.js +171 -0
- package/dist/core/fonts/internal/font_8x8.js +171 -0
- package/dist/core/game_object_register.js +134 -0
- package/dist/core/input.js +170 -0
- package/dist/core/sprite.js +222 -0
- package/dist/core/utils/perlin_noise.js +183 -0
- package/dist/core/world.js +304 -0
- package/dist/debug/monitor.js +47 -0
- package/dist/decorators/index.js +1 -0
- package/dist/decorators/log.js +42 -0
- package/dist/entities/dynamic_entity.js +99 -0
- package/dist/entities/entity.js +283 -0
- package/dist/entities/player.js +93 -0
- package/dist/entities/text.js +62 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -29
- package/dist/subsystems/camera_system.d.ts +2 -2
- package/dist/subsystems/camera_system.d.ts.map +1 -1
- package/dist/subsystems/camera_system.js +41 -0
- package/dist/subsystems/collision_system.js +17 -0
- package/dist/subsystems/monitor_system.js +20 -0
- package/dist/subsystems/object_system.d.ts +1 -1
- package/dist/subsystems/object_system.d.ts.map +1 -1
- package/dist/subsystems/object_system.js +29 -0
- package/dist/subsystems/types.d.ts +1 -1
- package/dist/subsystems/types.d.ts.map +1 -1
- package/dist/subsystems/types.js +0 -0
- package/dist/types.js +10 -0
- package/package.json +17 -6
- package/src/core/animate.ts +159 -0
- package/src/core/asset.ts +76 -0
- package/src/core/behaviour.ts +145 -0
- package/src/core/behaviours/collidable.ts +296 -0
- package/src/core/behaviours/control.ts +80 -0
- package/src/core/behaviours/healtkit.ts +166 -0
- package/src/core/behaviours/sprite_render.ts +216 -0
- package/src/core/camera.ts +145 -0
- package/src/core/engine.ts +607 -0
- package/src/core/fonts/font_bitmap.ts +232 -0
- package/src/core/fonts/font_bitmap_prebuild.ts +141 -0
- package/src/core/fonts/internal/font_3x5.ts +178 -0
- package/src/core/fonts/internal/font_4x6.ts +180 -0
- package/src/core/fonts/internal/font_5x5.ts +137 -0
- package/src/core/fonts/internal/font_6x8.ts +180 -0
- package/src/core/fonts/internal/font_8x13.ts +180 -0
- package/src/core/fonts/internal/font_8x8.ts +180 -0
- package/src/core/game_object_register.ts +146 -0
- package/src/core/input.ts +182 -0
- package/src/core/sprite.ts +339 -0
- package/src/core/utils/perlin_noise.ts +196 -0
- package/src/core/world.ts +331 -0
- package/src/debug/monitor.ts +60 -0
- package/src/decorators/index.ts +1 -0
- package/src/decorators/log.ts +45 -0
- package/src/entities/dynamic_entity.ts +106 -0
- package/src/entities/entity.ts +322 -0
- package/src/entities/player.ts +99 -0
- package/src/entities/text.ts +72 -0
- package/src/index.ts +51 -0
- package/src/subsystems/camera_system.ts +52 -0
- package/src/subsystems/collision_system.ts +21 -0
- package/src/subsystems/monitor_system.ts +26 -0
- package/src/subsystems/object_system.ts +37 -0
- package/src/subsystems/types.ts +46 -0
- package/src/types.ts +178 -0
- 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
|
+
}
|