@donut-games/engine 0.1.0
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/index.d.ts +6 -0
- package/dist/core/index.js +1158 -0
- package/dist/core/index.js.map +1 -0
- package/dist/ctrllr/index.d.ts +1 -0
- package/dist/ctrllr/index.js +302 -0
- package/dist/ctrllr/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1158 -0
- package/dist/index.js.map +1 -0
- package/dist/math/index.d.ts +1 -0
- package/dist/math/index.js +708 -0
- package/dist/math/index.js.map +1 -0
- package/dist/pixi/index.d.ts +1 -0
- package/dist/pixi/index.js +967 -0
- package/dist/pixi/index.js.map +1 -0
- package/dist/player/index.d.ts +1 -0
- package/dist/player/index.js +2193 -0
- package/dist/player/index.js.map +1 -0
- package/dist/three/index.d.ts +1 -0
- package/dist/three/index.js +6 -0
- package/dist/three/index.js.map +1 -0
- package/package.json +74 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1158 @@
|
|
|
1
|
+
// ../core/src/event-emitter.ts
|
|
2
|
+
var EventEmitter = class {
|
|
3
|
+
/** Map of event names to their registered handler functions. */
|
|
4
|
+
handlerMap = /* @__PURE__ */ new Map();
|
|
5
|
+
/**
|
|
6
|
+
* Registers a handler for the specified event.
|
|
7
|
+
* @param eventName - The name of the event to listen for.
|
|
8
|
+
* @param handler - The function to call when the event is emitted.
|
|
9
|
+
* @returns A subscription that can be used to unsubscribe.
|
|
10
|
+
*/
|
|
11
|
+
on(eventName, handler) {
|
|
12
|
+
if (!this.handlerMap.has(eventName)) {
|
|
13
|
+
this.handlerMap.set(eventName, /* @__PURE__ */ new Set());
|
|
14
|
+
}
|
|
15
|
+
this.handlerMap.get(eventName).add(handler);
|
|
16
|
+
return {
|
|
17
|
+
unsubscribe: () => {
|
|
18
|
+
this.off(eventName, handler);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Removes a previously registered handler for the specified event.
|
|
24
|
+
* @param eventName - The name of the event.
|
|
25
|
+
* @param handler - The handler function to remove.
|
|
26
|
+
*/
|
|
27
|
+
off(eventName, handler) {
|
|
28
|
+
const handlers = this.handlerMap.get(eventName);
|
|
29
|
+
if (handlers) {
|
|
30
|
+
handlers.delete(handler);
|
|
31
|
+
if (handlers.size === 0) {
|
|
32
|
+
this.handlerMap.delete(eventName);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Registers a handler that will be called at most once for the specified event.
|
|
38
|
+
* @param eventName - The name of the event to listen for.
|
|
39
|
+
* @param handler - The function to call when the event is emitted.
|
|
40
|
+
* @returns A subscription that can be used to unsubscribe.
|
|
41
|
+
*/
|
|
42
|
+
once(eventName, handler) {
|
|
43
|
+
const wrappedHandler = (...arguments_) => {
|
|
44
|
+
this.off(eventName, wrappedHandler);
|
|
45
|
+
handler(...arguments_);
|
|
46
|
+
};
|
|
47
|
+
return this.on(eventName, wrappedHandler);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Emits an event, calling all registered handlers with the provided arguments.
|
|
51
|
+
* @param eventName - The name of the event to emit.
|
|
52
|
+
* @param arguments_ - Arguments to pass to the event handlers.
|
|
53
|
+
*/
|
|
54
|
+
emit(eventName, ...arguments_) {
|
|
55
|
+
const handlers = this.handlerMap.get(eventName);
|
|
56
|
+
if (handlers) {
|
|
57
|
+
for (const handler of [...handlers]) {
|
|
58
|
+
handler(...arguments_);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// ../core/src/component.ts
|
|
65
|
+
var Component = class {
|
|
66
|
+
/** The entity that owns this component, if any. */
|
|
67
|
+
owner;
|
|
68
|
+
/** Optional list of component types that must be present on the entity. */
|
|
69
|
+
dependencies;
|
|
70
|
+
/**
|
|
71
|
+
* Serializes this component's state to a plain object.
|
|
72
|
+
* @returns A record of serializable key-value pairs.
|
|
73
|
+
*/
|
|
74
|
+
serialize() {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Restores this component's state from a serialized object.
|
|
79
|
+
* @param _data - The serialized data to restore from.
|
|
80
|
+
*/
|
|
81
|
+
deserialize(_data) {
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// ../core/src/entity.ts
|
|
86
|
+
var Entity = class _Entity {
|
|
87
|
+
/** Auto-incrementing counter for unique entity identifiers. */
|
|
88
|
+
static nextIdentifier = 1;
|
|
89
|
+
/** The unique numeric identifier for this entity. */
|
|
90
|
+
identifier;
|
|
91
|
+
/** The display name of this entity. */
|
|
92
|
+
name;
|
|
93
|
+
/** Map of component constructors to component instances for O(1) lookup. */
|
|
94
|
+
componentMap = /* @__PURE__ */ new Map();
|
|
95
|
+
/** Set of tags associated with this entity. */
|
|
96
|
+
tagSet = /* @__PURE__ */ new Set();
|
|
97
|
+
/** Whether this entity is active and should be processed by systems. */
|
|
98
|
+
isActive = true;
|
|
99
|
+
/** The parent entity in the hierarchy, if any. */
|
|
100
|
+
parent;
|
|
101
|
+
/** Child entities in the hierarchy. */
|
|
102
|
+
children = [];
|
|
103
|
+
/** Internal event emitter for composition-based event delegation. */
|
|
104
|
+
eventEmitter = new EventEmitter();
|
|
105
|
+
constructor(options) {
|
|
106
|
+
this.identifier = _Entity.nextIdentifier++;
|
|
107
|
+
this.name = options?.name ?? `Entity${this.identifier}`;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Registers a handler for the specified event.
|
|
111
|
+
* @param eventName - The name of the event to listen for.
|
|
112
|
+
* @param handler - The function to call when the event is emitted.
|
|
113
|
+
* @returns A subscription that can be used to unsubscribe.
|
|
114
|
+
*/
|
|
115
|
+
on(eventName, handler) {
|
|
116
|
+
return this.eventEmitter.on(eventName, handler);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Removes a previously registered handler for the specified event.
|
|
120
|
+
* @param eventName - The name of the event.
|
|
121
|
+
* @param handler - The handler function to remove.
|
|
122
|
+
*/
|
|
123
|
+
off(eventName, handler) {
|
|
124
|
+
this.eventEmitter.off(eventName, handler);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Registers a handler that will be called at most once for the specified event.
|
|
128
|
+
* @param eventName - The name of the event to listen for.
|
|
129
|
+
* @param handler - The function to call when the event is emitted.
|
|
130
|
+
* @returns A subscription that can be used to unsubscribe.
|
|
131
|
+
*/
|
|
132
|
+
once(eventName, handler) {
|
|
133
|
+
return this.eventEmitter.once(eventName, handler);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Emits an event, calling all registered handlers with the provided arguments.
|
|
137
|
+
* @param eventName - The name of the event to emit.
|
|
138
|
+
* @param arguments_ - Arguments to pass to the event handlers.
|
|
139
|
+
*/
|
|
140
|
+
emit(eventName, ...arguments_) {
|
|
141
|
+
this.eventEmitter.emit(eventName, ...arguments_);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Adds a component to this entity.
|
|
145
|
+
* Sets the component's owner, calls its onAdd hook, and emits 'componentadded'.
|
|
146
|
+
* @param component - The component instance to add.
|
|
147
|
+
* @returns The added component for chaining.
|
|
148
|
+
*/
|
|
149
|
+
addComponent(component) {
|
|
150
|
+
const constructor = component.constructor;
|
|
151
|
+
component.owner = this;
|
|
152
|
+
this.componentMap.set(constructor, component);
|
|
153
|
+
if (component.onAdd) {
|
|
154
|
+
component.onAdd(this);
|
|
155
|
+
}
|
|
156
|
+
this.emit("componentadded", component);
|
|
157
|
+
return component;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Removes a component of the specified type from this entity.
|
|
161
|
+
* Calls the component's onRemove hook and emits 'componentremoved'.
|
|
162
|
+
* @param componentType - The constructor of the component type to remove.
|
|
163
|
+
* @param _force - Reserved for future use.
|
|
164
|
+
*/
|
|
165
|
+
removeComponent(componentType, _force) {
|
|
166
|
+
const component = this.componentMap.get(componentType);
|
|
167
|
+
if (component) {
|
|
168
|
+
this.componentMap.delete(componentType);
|
|
169
|
+
if (component.onRemove) {
|
|
170
|
+
component.onRemove(this);
|
|
171
|
+
}
|
|
172
|
+
component.owner = void 0;
|
|
173
|
+
this.emit("componentremoved", component);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Retrieves a component of the specified type from this entity.
|
|
178
|
+
* @param componentType - The constructor of the component type to retrieve.
|
|
179
|
+
* @returns The component instance, or undefined if not found.
|
|
180
|
+
*/
|
|
181
|
+
get(componentType) {
|
|
182
|
+
return this.componentMap.get(componentType);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Checks whether this entity has a component of the specified type.
|
|
186
|
+
* @param componentType - The constructor of the component type to check for.
|
|
187
|
+
* @returns True if the component is present.
|
|
188
|
+
*/
|
|
189
|
+
has(componentType) {
|
|
190
|
+
return this.componentMap.has(componentType);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Adds a tag to this entity.
|
|
194
|
+
* @param tag - The tag string to add.
|
|
195
|
+
*/
|
|
196
|
+
addTag(tag) {
|
|
197
|
+
this.tagSet.add(tag);
|
|
198
|
+
this.emit("tagadded", tag);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Checks whether this entity has the specified tag.
|
|
202
|
+
* @param tag - The tag string to check for.
|
|
203
|
+
* @returns True if the tag is present.
|
|
204
|
+
*/
|
|
205
|
+
hasTag(tag) {
|
|
206
|
+
return this.tagSet.has(tag);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Removes a tag from this entity.
|
|
210
|
+
* @param tag - The tag string to remove.
|
|
211
|
+
*/
|
|
212
|
+
removeTag(tag) {
|
|
213
|
+
this.tagSet.delete(tag);
|
|
214
|
+
this.emit("tagremoved", tag);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Adds a child entity to this entity's hierarchy.
|
|
218
|
+
* @param entity - The child entity to add.
|
|
219
|
+
*/
|
|
220
|
+
addChild(entity) {
|
|
221
|
+
entity.parent = this;
|
|
222
|
+
this.children.push(entity);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Removes a child entity from this entity's hierarchy.
|
|
226
|
+
* @param entity - The child entity to remove.
|
|
227
|
+
*/
|
|
228
|
+
removeChild(entity) {
|
|
229
|
+
const index = this.children.indexOf(entity);
|
|
230
|
+
if (index !== -1) {
|
|
231
|
+
this.children.splice(index, 1);
|
|
232
|
+
entity.parent = void 0;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Marks this entity as inactive and emits the 'kill' event.
|
|
237
|
+
*/
|
|
238
|
+
kill() {
|
|
239
|
+
this.isActive = false;
|
|
240
|
+
this.emit("kill");
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// ../core/src/observable.ts
|
|
245
|
+
var Observable = class {
|
|
246
|
+
/** The registered listeners that will be called on each notify. */
|
|
247
|
+
listeners = /* @__PURE__ */ new Set();
|
|
248
|
+
/**
|
|
249
|
+
* Subscribes a listener to this observable.
|
|
250
|
+
* @param listener - The function to call each time a value is emitted.
|
|
251
|
+
* @returns A subscription that can be used to unsubscribe the listener.
|
|
252
|
+
*/
|
|
253
|
+
subscribe(listener) {
|
|
254
|
+
this.listeners.add(listener);
|
|
255
|
+
return {
|
|
256
|
+
unsubscribe: () => {
|
|
257
|
+
this.listeners.delete(listener);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Emits a value to all subscribed listeners.
|
|
263
|
+
* Intended for internal use by the producer that owns the observable.
|
|
264
|
+
* @param value - The value to emit.
|
|
265
|
+
*/
|
|
266
|
+
notify(value) {
|
|
267
|
+
for (const listener of [...this.listeners]) {
|
|
268
|
+
listener(value);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// ../core/src/query.ts
|
|
274
|
+
var Query = class {
|
|
275
|
+
/** The resolved query parameters. */
|
|
276
|
+
parameters;
|
|
277
|
+
/** The set of entities currently matching this query. */
|
|
278
|
+
matchedEntities = /* @__PURE__ */ new Set();
|
|
279
|
+
/** Emits entities when they transition from non-matching to matching this query. */
|
|
280
|
+
entityAdded$ = new Observable();
|
|
281
|
+
/** Emits entities when they transition from matching to non-matching this query. */
|
|
282
|
+
entityRemoved$ = new Observable();
|
|
283
|
+
/**
|
|
284
|
+
* Creates a new query from either an array of required component types or a full QueryParameters object.
|
|
285
|
+
* @param parameters - Component constructor array or full query parameters.
|
|
286
|
+
*/
|
|
287
|
+
constructor(parameters) {
|
|
288
|
+
if (Array.isArray(parameters)) {
|
|
289
|
+
this.parameters = { components: { all: parameters } };
|
|
290
|
+
} else {
|
|
291
|
+
this.parameters = parameters;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** Returns a readonly array of all entities currently matching this query. */
|
|
295
|
+
get entities() {
|
|
296
|
+
return [...this.matchedEntities];
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Tests whether a given entity matches this query's criteria.
|
|
300
|
+
* @param entity - The entity to test.
|
|
301
|
+
* @returns True if the entity matches all criteria.
|
|
302
|
+
*/
|
|
303
|
+
matches(entity) {
|
|
304
|
+
const { components, tags } = this.parameters;
|
|
305
|
+
if (components?.all) {
|
|
306
|
+
for (const componentType of components.all) {
|
|
307
|
+
if (!entity.has(componentType)) return false;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (components?.any) {
|
|
311
|
+
let hasAny = false;
|
|
312
|
+
for (const componentType of components.any) {
|
|
313
|
+
if (entity.has(componentType)) {
|
|
314
|
+
hasAny = true;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (!hasAny) return false;
|
|
319
|
+
}
|
|
320
|
+
if (components?.not) {
|
|
321
|
+
for (const componentType of components.not) {
|
|
322
|
+
if (entity.has(componentType)) return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (tags?.all) {
|
|
326
|
+
for (const tag of tags.all) {
|
|
327
|
+
if (!entity.hasTag(tag)) return false;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (tags?.any) {
|
|
331
|
+
let hasAny = false;
|
|
332
|
+
for (const tag of tags.any) {
|
|
333
|
+
if (entity.hasTag(tag)) {
|
|
334
|
+
hasAny = true;
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (!hasAny) return false;
|
|
339
|
+
}
|
|
340
|
+
if (tags?.not) {
|
|
341
|
+
for (const tag of tags.not) {
|
|
342
|
+
if (entity.hasTag(tag)) return false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Adds an entity to the matched set unconditionally. Called by the query manager
|
|
349
|
+
* when it has already determined the entity matches. Fires entityAdded$ on transition.
|
|
350
|
+
* @param entity - The entity to add.
|
|
351
|
+
*/
|
|
352
|
+
addEntity(entity) {
|
|
353
|
+
if (!this.matchedEntities.has(entity)) {
|
|
354
|
+
this.matchedEntities.add(entity);
|
|
355
|
+
this.entityAdded$.notify(entity);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Removes an entity from the matched set. Fires entityRemoved$ on transition.
|
|
360
|
+
* @param entity - The entity to remove.
|
|
361
|
+
*/
|
|
362
|
+
removeEntity(entity) {
|
|
363
|
+
if (this.matchedEntities.has(entity)) {
|
|
364
|
+
this.matchedEntities.delete(entity);
|
|
365
|
+
this.entityRemoved$.notify(entity);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Re-evaluates whether an entity matches this query and updates the matched
|
|
370
|
+
* set accordingly, firing entityAdded$ / entityRemoved$ on state transitions.
|
|
371
|
+
* @param entity - The entity to re-evaluate.
|
|
372
|
+
*/
|
|
373
|
+
checkAndModify(entity) {
|
|
374
|
+
const currentlyMatches = this.matches(entity);
|
|
375
|
+
const isTracked = this.matchedEntities.has(entity);
|
|
376
|
+
if (currentlyMatches && !isTracked) {
|
|
377
|
+
this.matchedEntities.add(entity);
|
|
378
|
+
this.entityAdded$.notify(entity);
|
|
379
|
+
} else if (!currentlyMatches && isTracked) {
|
|
380
|
+
this.matchedEntities.delete(entity);
|
|
381
|
+
this.entityRemoved$.notify(entity);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// ../core/src/query-manager.ts
|
|
387
|
+
var QueryManager = class {
|
|
388
|
+
/** All registered queries. */
|
|
389
|
+
queries = [];
|
|
390
|
+
/** Optional provider used to backfill newly created queries with existing entities. */
|
|
391
|
+
entityProvider;
|
|
392
|
+
/**
|
|
393
|
+
* Sets the entity provider used to backfill newly created queries.
|
|
394
|
+
* @param entityProvider - Provider that exposes the current world entities.
|
|
395
|
+
*/
|
|
396
|
+
setEntityProvider(entityProvider) {
|
|
397
|
+
this.entityProvider = entityProvider;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Creates a new query, registers it for automatic updates, and backfills it
|
|
401
|
+
* with any currently-existing entities that match.
|
|
402
|
+
* @param parameters - Component constructor array or full query parameters.
|
|
403
|
+
* @returns The newly created query.
|
|
404
|
+
*/
|
|
405
|
+
createQuery(parameters) {
|
|
406
|
+
const query = new Query(parameters);
|
|
407
|
+
this.queries.push(query);
|
|
408
|
+
if (this.entityProvider) {
|
|
409
|
+
for (const entity of this.entityProvider.entities) {
|
|
410
|
+
query.checkAndModify(entity);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return query;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Notifies all queries that an entity has been added to the world.
|
|
417
|
+
* @param entity - The entity that was added.
|
|
418
|
+
*/
|
|
419
|
+
notifyEntityAdded(entity) {
|
|
420
|
+
for (const query of this.queries) {
|
|
421
|
+
query.checkAndModify(entity);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Notifies all queries that an entity has been removed from the world,
|
|
426
|
+
* forcing removal regardless of match state so entityRemoved$ fires for
|
|
427
|
+
* matched entities.
|
|
428
|
+
* @param entity - The entity that was removed.
|
|
429
|
+
*/
|
|
430
|
+
notifyEntityRemoved(entity) {
|
|
431
|
+
for (const query of this.queries) {
|
|
432
|
+
query.removeEntity(entity);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Notifies all queries that an entity's components or tags have changed.
|
|
437
|
+
* Queries re-evaluate match state and fire observables on transitions.
|
|
438
|
+
* @param entity - The entity that changed.
|
|
439
|
+
*/
|
|
440
|
+
notifyComponentChanged(entity) {
|
|
441
|
+
for (const query of this.queries) {
|
|
442
|
+
query.checkAndModify(entity);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// ../core/src/system.ts
|
|
448
|
+
var SystemType = /* @__PURE__ */ ((SystemType2) => {
|
|
449
|
+
SystemType2["Update"] = "update";
|
|
450
|
+
SystemType2["Draw"] = "draw";
|
|
451
|
+
return SystemType2;
|
|
452
|
+
})(SystemType || {});
|
|
453
|
+
var SystemPriority = {
|
|
454
|
+
/** Highest priority (runs first). */
|
|
455
|
+
Highest: 0,
|
|
456
|
+
/** Higher than average priority. */
|
|
457
|
+
Higher: 25,
|
|
458
|
+
/** Default priority. */
|
|
459
|
+
Average: 50,
|
|
460
|
+
/** Lower than average priority. */
|
|
461
|
+
Lower: 75,
|
|
462
|
+
/** Lowest priority (runs last). */
|
|
463
|
+
Lowest: 100
|
|
464
|
+
};
|
|
465
|
+
var System = class {
|
|
466
|
+
/** The execution priority of this system. Lower values run first. */
|
|
467
|
+
static priority = SystemPriority.Average;
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// ../core/src/system-manager.ts
|
|
471
|
+
var SystemManager = class {
|
|
472
|
+
/** All registered systems. */
|
|
473
|
+
systems = [];
|
|
474
|
+
/**
|
|
475
|
+
* Registers a system for execution.
|
|
476
|
+
* @param system - The system to add.
|
|
477
|
+
*/
|
|
478
|
+
addSystem(system) {
|
|
479
|
+
this.systems.push(system);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Removes a previously registered system.
|
|
483
|
+
* @param system - The system to remove.
|
|
484
|
+
*/
|
|
485
|
+
removeSystem(system) {
|
|
486
|
+
const index = this.systems.indexOf(system);
|
|
487
|
+
if (index !== -1) {
|
|
488
|
+
this.systems.splice(index, 1);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Runs all systems of the specified type in priority order.
|
|
493
|
+
* Calls preupdate, update, and postupdate for each system.
|
|
494
|
+
* @param systemType - The type of systems to run.
|
|
495
|
+
* @param elapsedMilliseconds - The time elapsed since the last update in milliseconds.
|
|
496
|
+
*/
|
|
497
|
+
updateSystems(systemType, elapsedMilliseconds) {
|
|
498
|
+
const matchingSystems = this.systems.filter((system) => system.systemType === systemType).sort((a, b) => {
|
|
499
|
+
const priorityA = a.constructor.priority;
|
|
500
|
+
const priorityB = b.constructor.priority;
|
|
501
|
+
return priorityA - priorityB;
|
|
502
|
+
});
|
|
503
|
+
for (const system of matchingSystems) {
|
|
504
|
+
if (system.preupdate) {
|
|
505
|
+
system.preupdate(elapsedMilliseconds);
|
|
506
|
+
}
|
|
507
|
+
system.update(elapsedMilliseconds);
|
|
508
|
+
if (system.postupdate) {
|
|
509
|
+
system.postupdate(elapsedMilliseconds);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// ../core/src/entity-manager.ts
|
|
516
|
+
var EntityManager = class {
|
|
517
|
+
/** All managed entities. */
|
|
518
|
+
entitySet = /* @__PURE__ */ new Set();
|
|
519
|
+
/** Reference to the query manager for change notifications. */
|
|
520
|
+
queryManager;
|
|
521
|
+
/**
|
|
522
|
+
* Creates a new EntityManager.
|
|
523
|
+
* @param queryManager - The query manager to notify of entity changes.
|
|
524
|
+
*/
|
|
525
|
+
constructor(queryManager) {
|
|
526
|
+
this.queryManager = queryManager;
|
|
527
|
+
this.queryManager.setEntityProvider(this);
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Adds an entity to this manager and notifies the query manager.
|
|
531
|
+
* Also subscribes to the entity's component and tag change events.
|
|
532
|
+
* @param entity - The entity to add.
|
|
533
|
+
*/
|
|
534
|
+
addEntity(entity) {
|
|
535
|
+
this.entitySet.add(entity);
|
|
536
|
+
this.queryManager.notifyEntityAdded(entity);
|
|
537
|
+
entity.on("componentadded", () => {
|
|
538
|
+
this.queryManager.notifyComponentChanged(entity);
|
|
539
|
+
});
|
|
540
|
+
entity.on("componentremoved", () => {
|
|
541
|
+
this.queryManager.notifyComponentChanged(entity);
|
|
542
|
+
});
|
|
543
|
+
entity.on("tagadded", () => {
|
|
544
|
+
this.queryManager.notifyComponentChanged(entity);
|
|
545
|
+
});
|
|
546
|
+
entity.on("tagremoved", () => {
|
|
547
|
+
this.queryManager.notifyComponentChanged(entity);
|
|
548
|
+
});
|
|
549
|
+
entity.on("kill", () => {
|
|
550
|
+
this.removeEntity(entity);
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Removes an entity from this manager and notifies the query manager.
|
|
555
|
+
* @param entity - The entity to remove.
|
|
556
|
+
*/
|
|
557
|
+
removeEntity(entity) {
|
|
558
|
+
this.entitySet.delete(entity);
|
|
559
|
+
this.queryManager.notifyEntityRemoved(entity);
|
|
560
|
+
}
|
|
561
|
+
/** Returns a readonly array of all managed entities. */
|
|
562
|
+
get entities() {
|
|
563
|
+
return [...this.entitySet];
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// ../core/src/world.ts
|
|
568
|
+
var World = class {
|
|
569
|
+
/** Manages entity lifecycle. */
|
|
570
|
+
entityManager;
|
|
571
|
+
/** Manages system registration and execution. */
|
|
572
|
+
systemManager;
|
|
573
|
+
/** Manages query creation and updates. */
|
|
574
|
+
queryManager;
|
|
575
|
+
constructor() {
|
|
576
|
+
this.queryManager = new QueryManager();
|
|
577
|
+
this.entityManager = new EntityManager(this.queryManager);
|
|
578
|
+
this.systemManager = new SystemManager();
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Adds an entity to the world.
|
|
582
|
+
* @param entity - The entity to add.
|
|
583
|
+
*/
|
|
584
|
+
add(entity) {
|
|
585
|
+
this.entityManager.addEntity(entity);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Adds a system to the world.
|
|
589
|
+
* @param system - The system to add.
|
|
590
|
+
*/
|
|
591
|
+
addSystem(system) {
|
|
592
|
+
this.systemManager.addSystem(system);
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Removes an entity from the world.
|
|
596
|
+
* @param entity - The entity to remove.
|
|
597
|
+
*/
|
|
598
|
+
remove(entity) {
|
|
599
|
+
this.entityManager.removeEntity(entity);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Removes a system from the world.
|
|
603
|
+
* @param system - The system to remove.
|
|
604
|
+
*/
|
|
605
|
+
removeSystem(system) {
|
|
606
|
+
this.systemManager.removeSystem(system);
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Creates or retrieves a query matching the given parameters.
|
|
610
|
+
* @param parameters - Component constructor array or full query parameters.
|
|
611
|
+
* @returns A reactive query that tracks matching entities.
|
|
612
|
+
*/
|
|
613
|
+
query(parameters) {
|
|
614
|
+
return this.queryManager.createQuery(parameters);
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Runs all systems of the specified type for one frame.
|
|
618
|
+
* @param systemType - The type of systems to run.
|
|
619
|
+
* @param elapsedMilliseconds - The time elapsed since the last update in milliseconds.
|
|
620
|
+
*/
|
|
621
|
+
update(systemType, elapsedMilliseconds) {
|
|
622
|
+
this.systemManager.updateSystems(systemType, elapsedMilliseconds);
|
|
623
|
+
}
|
|
624
|
+
/** Returns a readonly array of all entities currently in the world. */
|
|
625
|
+
get entities() {
|
|
626
|
+
return this.entityManager.entities;
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// ../math/src/vector3.ts
|
|
631
|
+
var Vector3 = class _Vector3 {
|
|
632
|
+
/**
|
|
633
|
+
* Creates a new Vector3.
|
|
634
|
+
* @param x - The x component.
|
|
635
|
+
* @param y - The y component.
|
|
636
|
+
* @param z - The z component.
|
|
637
|
+
*/
|
|
638
|
+
constructor(x, y, z) {
|
|
639
|
+
this.x = x;
|
|
640
|
+
this.y = y;
|
|
641
|
+
this.z = z;
|
|
642
|
+
}
|
|
643
|
+
x;
|
|
644
|
+
y;
|
|
645
|
+
z;
|
|
646
|
+
/**
|
|
647
|
+
* Adds another vector to this vector and returns the result.
|
|
648
|
+
* @param other - The vector to add.
|
|
649
|
+
* @returns A new Vector3 representing the sum.
|
|
650
|
+
*/
|
|
651
|
+
add(other) {
|
|
652
|
+
return new _Vector3(this.x + other.x, this.y + other.y, this.z + other.z);
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Subtracts another vector from this vector and returns the result.
|
|
656
|
+
* @param other - The vector to subtract.
|
|
657
|
+
* @returns A new Vector3 representing the difference.
|
|
658
|
+
*/
|
|
659
|
+
subtract(other) {
|
|
660
|
+
return new _Vector3(this.x - other.x, this.y - other.y, this.z - other.z);
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Scales this vector by a scalar value and returns the result.
|
|
664
|
+
* @param scalar - The scalar multiplier.
|
|
665
|
+
* @returns A new scaled Vector3.
|
|
666
|
+
*/
|
|
667
|
+
scale(scalar) {
|
|
668
|
+
return new _Vector3(this.x * scalar, this.y * scalar, this.z * scalar);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Returns the normalized (unit length) version of this vector.
|
|
672
|
+
* If the vector has zero length, returns a zero vector.
|
|
673
|
+
* @returns A new normalized Vector3.
|
|
674
|
+
*/
|
|
675
|
+
normalize() {
|
|
676
|
+
const magnitude = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
|
677
|
+
if (magnitude === 0) {
|
|
678
|
+
return _Vector3.zero();
|
|
679
|
+
}
|
|
680
|
+
return new _Vector3(this.x / magnitude, this.y / magnitude, this.z / magnitude);
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Computes the dot product of this vector with another.
|
|
684
|
+
* @param other - The other vector.
|
|
685
|
+
* @returns The dot product.
|
|
686
|
+
*/
|
|
687
|
+
dot(other) {
|
|
688
|
+
return this.x * other.x + this.y * other.y + this.z * other.z;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Computes the cross product of this vector with another.
|
|
692
|
+
* @param other - The other vector.
|
|
693
|
+
* @returns A new Vector3 representing the cross product.
|
|
694
|
+
*/
|
|
695
|
+
cross(other) {
|
|
696
|
+
return new _Vector3(
|
|
697
|
+
this.y * other.z - this.z * other.y,
|
|
698
|
+
this.z * other.x - this.x * other.z,
|
|
699
|
+
this.x * other.y - this.y * other.x
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Computes the Euclidean distance between this vector and another.
|
|
704
|
+
* @param other - The other vector.
|
|
705
|
+
* @returns The distance.
|
|
706
|
+
*/
|
|
707
|
+
distance(other) {
|
|
708
|
+
return Math.sqrt(this.distanceSquared(other));
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Computes the squared Euclidean distance between this vector and another.
|
|
712
|
+
* @param other - The other vector.
|
|
713
|
+
* @returns The squared distance.
|
|
714
|
+
*/
|
|
715
|
+
distanceSquared(other) {
|
|
716
|
+
const deltaX = this.x - other.x;
|
|
717
|
+
const deltaY = this.y - other.y;
|
|
718
|
+
const deltaZ = this.z - other.z;
|
|
719
|
+
return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Linearly interpolates between this vector and a target vector.
|
|
723
|
+
* @param target - The target vector.
|
|
724
|
+
* @param interpolation - The interpolation factor, typically between 0 and 1.
|
|
725
|
+
* @returns A new interpolated Vector3.
|
|
726
|
+
*/
|
|
727
|
+
lerp(target, interpolation) {
|
|
728
|
+
return new _Vector3(
|
|
729
|
+
this.x + (target.x - this.x) * interpolation,
|
|
730
|
+
this.y + (target.y - this.y) * interpolation,
|
|
731
|
+
this.z + (target.z - this.z) * interpolation
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Creates a copy of this vector.
|
|
736
|
+
* @returns A new Vector3 with the same components.
|
|
737
|
+
*/
|
|
738
|
+
clone() {
|
|
739
|
+
return new _Vector3(this.x, this.y, this.z);
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Checks whether this vector is equal to another within floating-point tolerance.
|
|
743
|
+
* @param other - The vector to compare against.
|
|
744
|
+
* @param epsilon - The tolerance for comparison. Defaults to 1e-6.
|
|
745
|
+
* @returns True if the vectors are approximately equal.
|
|
746
|
+
*/
|
|
747
|
+
equals(other, epsilon = 1e-6) {
|
|
748
|
+
return Math.abs(this.x - other.x) <= epsilon && Math.abs(this.y - other.y) <= epsilon && Math.abs(this.z - other.z) <= epsilon;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Returns a string representation of this vector.
|
|
752
|
+
* @returns A string in the format "Vector3(x, y, z)".
|
|
753
|
+
*/
|
|
754
|
+
toString() {
|
|
755
|
+
return `Vector3(${this.x}, ${this.y}, ${this.z})`;
|
|
756
|
+
}
|
|
757
|
+
/** Returns a vector with all components set to zero. */
|
|
758
|
+
static zero() {
|
|
759
|
+
return new _Vector3(0, 0, 0);
|
|
760
|
+
}
|
|
761
|
+
/** Returns a vector with all components set to one. */
|
|
762
|
+
static one() {
|
|
763
|
+
return new _Vector3(1, 1, 1);
|
|
764
|
+
}
|
|
765
|
+
/** Returns the up direction vector (0, 1, 0). */
|
|
766
|
+
static up() {
|
|
767
|
+
return new _Vector3(0, 1, 0);
|
|
768
|
+
}
|
|
769
|
+
/** Returns the down direction vector (0, -1, 0). */
|
|
770
|
+
static down() {
|
|
771
|
+
return new _Vector3(0, -1, 0);
|
|
772
|
+
}
|
|
773
|
+
/** Returns the left direction vector (-1, 0, 0). */
|
|
774
|
+
static left() {
|
|
775
|
+
return new _Vector3(-1, 0, 0);
|
|
776
|
+
}
|
|
777
|
+
/** Returns the right direction vector (1, 0, 0). */
|
|
778
|
+
static right() {
|
|
779
|
+
return new _Vector3(1, 0, 0);
|
|
780
|
+
}
|
|
781
|
+
/** Returns the forward direction vector (0, 0, 1). */
|
|
782
|
+
static forward() {
|
|
783
|
+
return new _Vector3(0, 0, 1);
|
|
784
|
+
}
|
|
785
|
+
/** Returns the back direction vector (0, 0, -1). */
|
|
786
|
+
static back() {
|
|
787
|
+
return new _Vector3(0, 0, -1);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
// ../math/src/quaternion.ts
|
|
792
|
+
var Quaternion = class _Quaternion {
|
|
793
|
+
/**
|
|
794
|
+
* Creates a new Quaternion.
|
|
795
|
+
* @param x - The x component.
|
|
796
|
+
* @param y - The y component.
|
|
797
|
+
* @param z - The z component.
|
|
798
|
+
* @param w - The w (scalar) component.
|
|
799
|
+
*/
|
|
800
|
+
constructor(x, y, z, w) {
|
|
801
|
+
this.x = x;
|
|
802
|
+
this.y = y;
|
|
803
|
+
this.z = z;
|
|
804
|
+
this.w = w;
|
|
805
|
+
}
|
|
806
|
+
x;
|
|
807
|
+
y;
|
|
808
|
+
z;
|
|
809
|
+
w;
|
|
810
|
+
/**
|
|
811
|
+
* Multiplies this quaternion by another (composes rotations).
|
|
812
|
+
* @param other - The quaternion to multiply with.
|
|
813
|
+
* @returns A new Quaternion representing the combined rotation.
|
|
814
|
+
*/
|
|
815
|
+
multiply(other) {
|
|
816
|
+
return new _Quaternion(
|
|
817
|
+
this.w * other.x + this.x * other.w + this.y * other.z - this.z * other.y,
|
|
818
|
+
this.w * other.y - this.x * other.z + this.y * other.w + this.z * other.x,
|
|
819
|
+
this.w * other.z + this.x * other.y - this.y * other.x + this.z * other.w,
|
|
820
|
+
this.w * other.w - this.x * other.x - this.y * other.y - this.z * other.z
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Returns the inverse (conjugate divided by squared magnitude) of this quaternion.
|
|
825
|
+
* For unit quaternions, this is the same as the conjugate.
|
|
826
|
+
* @returns A new Quaternion representing the inverse rotation.
|
|
827
|
+
*/
|
|
828
|
+
inverse() {
|
|
829
|
+
const magnitudeSquared = this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
|
|
830
|
+
if (magnitudeSquared === 0) {
|
|
831
|
+
return _Quaternion.identity();
|
|
832
|
+
}
|
|
833
|
+
return new _Quaternion(
|
|
834
|
+
-this.x / magnitudeSquared,
|
|
835
|
+
-this.y / magnitudeSquared,
|
|
836
|
+
-this.z / magnitudeSquared,
|
|
837
|
+
this.w / magnitudeSquared
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Returns a normalized (unit length) version of this quaternion.
|
|
842
|
+
* @returns A new normalized Quaternion.
|
|
843
|
+
*/
|
|
844
|
+
normalize() {
|
|
845
|
+
const magnitude = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
|
|
846
|
+
if (magnitude === 0) {
|
|
847
|
+
return _Quaternion.identity();
|
|
848
|
+
}
|
|
849
|
+
return new _Quaternion(
|
|
850
|
+
this.x / magnitude,
|
|
851
|
+
this.y / magnitude,
|
|
852
|
+
this.z / magnitude,
|
|
853
|
+
this.w / magnitude
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Performs spherical linear interpolation between this quaternion and a target.
|
|
858
|
+
* @param target - The target quaternion.
|
|
859
|
+
* @param interpolation - The interpolation factor, typically between 0 and 1.
|
|
860
|
+
* @returns A new interpolated Quaternion.
|
|
861
|
+
*/
|
|
862
|
+
slerp(target, interpolation) {
|
|
863
|
+
let cosHalfAngle = this.x * target.x + this.y * target.y + this.z * target.z + this.w * target.w;
|
|
864
|
+
let targetX = target.x;
|
|
865
|
+
let targetY = target.y;
|
|
866
|
+
let targetZ = target.z;
|
|
867
|
+
let targetW = target.w;
|
|
868
|
+
if (cosHalfAngle < 0) {
|
|
869
|
+
cosHalfAngle = -cosHalfAngle;
|
|
870
|
+
targetX = -targetX;
|
|
871
|
+
targetY = -targetY;
|
|
872
|
+
targetZ = -targetZ;
|
|
873
|
+
targetW = -targetW;
|
|
874
|
+
}
|
|
875
|
+
if (cosHalfAngle > 0.9995) {
|
|
876
|
+
return new _Quaternion(
|
|
877
|
+
this.x + (targetX - this.x) * interpolation,
|
|
878
|
+
this.y + (targetY - this.y) * interpolation,
|
|
879
|
+
this.z + (targetZ - this.z) * interpolation,
|
|
880
|
+
this.w + (targetW - this.w) * interpolation
|
|
881
|
+
).normalize();
|
|
882
|
+
}
|
|
883
|
+
const halfAngle = Math.acos(cosHalfAngle);
|
|
884
|
+
const sinHalfAngle = Math.sin(halfAngle);
|
|
885
|
+
const ratioA = Math.sin((1 - interpolation) * halfAngle) / sinHalfAngle;
|
|
886
|
+
const ratioB = Math.sin(interpolation * halfAngle) / sinHalfAngle;
|
|
887
|
+
return new _Quaternion(
|
|
888
|
+
this.x * ratioA + targetX * ratioB,
|
|
889
|
+
this.y * ratioA + targetY * ratioB,
|
|
890
|
+
this.z * ratioA + targetZ * ratioB,
|
|
891
|
+
this.w * ratioA + targetW * ratioB
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Converts this quaternion to Euler angles (in radians).
|
|
896
|
+
* Returns a Vector3 with (pitch, yaw, roll) as (x, y, z).
|
|
897
|
+
* @returns A Vector3 containing the Euler angles in radians.
|
|
898
|
+
*/
|
|
899
|
+
toEulerAngles() {
|
|
900
|
+
const sinRollCosP = 2 * (this.w * this.x + this.y * this.z);
|
|
901
|
+
const cosRollCosP = 1 - 2 * (this.x * this.x + this.y * this.y);
|
|
902
|
+
const roll = Math.atan2(sinRollCosP, cosRollCosP);
|
|
903
|
+
const sinPitch = 2 * (this.w * this.y - this.z * this.x);
|
|
904
|
+
let pitch;
|
|
905
|
+
if (Math.abs(sinPitch) >= 1) {
|
|
906
|
+
pitch = Math.sign(sinPitch) * (Math.PI / 2);
|
|
907
|
+
} else {
|
|
908
|
+
pitch = Math.asin(sinPitch);
|
|
909
|
+
}
|
|
910
|
+
const sinYawCosP = 2 * (this.w * this.z + this.x * this.y);
|
|
911
|
+
const cosYawCosP = 1 - 2 * (this.y * this.y + this.z * this.z);
|
|
912
|
+
const yaw = Math.atan2(sinYawCosP, cosYawCosP);
|
|
913
|
+
return new Vector3(roll, pitch, yaw);
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Rotates a Vector3 by this quaternion.
|
|
917
|
+
* @param vector - The vector to rotate.
|
|
918
|
+
* @returns A new rotated Vector3.
|
|
919
|
+
*/
|
|
920
|
+
rotateVector3(vector) {
|
|
921
|
+
const vectorQuaternion = new _Quaternion(vector.x, vector.y, vector.z, 0);
|
|
922
|
+
const result = this.multiply(vectorQuaternion).multiply(this.inverse());
|
|
923
|
+
return new Vector3(result.x, result.y, result.z);
|
|
924
|
+
}
|
|
925
|
+
/** Creates a deep copy of this quaternion. */
|
|
926
|
+
clone() {
|
|
927
|
+
return new _Quaternion(this.x, this.y, this.z, this.w);
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Checks if this quaternion is approximately equal to another.
|
|
931
|
+
* @param other - The quaternion to compare against.
|
|
932
|
+
* @param epsilon - The tolerance for floating-point comparison.
|
|
933
|
+
*/
|
|
934
|
+
equals(other, epsilon = 1e-6) {
|
|
935
|
+
return Math.abs(this.x - other.x) < epsilon && Math.abs(this.y - other.y) < epsilon && Math.abs(this.z - other.z) < epsilon && Math.abs(this.w - other.w) < epsilon;
|
|
936
|
+
}
|
|
937
|
+
/** Returns a string representation of this quaternion. */
|
|
938
|
+
toString() {
|
|
939
|
+
return `Quaternion(${this.x}, ${this.y}, ${this.z}, ${this.w})`;
|
|
940
|
+
}
|
|
941
|
+
/** Returns the identity quaternion (no rotation). */
|
|
942
|
+
static identity() {
|
|
943
|
+
return new _Quaternion(0, 0, 0, 1);
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Creates a quaternion from Euler angles (in radians).
|
|
947
|
+
* @param euler - A Vector3 containing (roll, pitch, yaw) as (x, y, z) in radians.
|
|
948
|
+
* @returns A new Quaternion representing the rotation.
|
|
949
|
+
*/
|
|
950
|
+
static fromEulerAngles(euler) {
|
|
951
|
+
const halfRoll = euler.x * 0.5;
|
|
952
|
+
const halfPitch = euler.y * 0.5;
|
|
953
|
+
const halfYaw = euler.z * 0.5;
|
|
954
|
+
const sinRoll = Math.sin(halfRoll);
|
|
955
|
+
const cosRoll = Math.cos(halfRoll);
|
|
956
|
+
const sinPitch = Math.sin(halfPitch);
|
|
957
|
+
const cosPitch = Math.cos(halfPitch);
|
|
958
|
+
const sinYaw = Math.sin(halfYaw);
|
|
959
|
+
const cosYaw = Math.cos(halfYaw);
|
|
960
|
+
return new _Quaternion(
|
|
961
|
+
sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw,
|
|
962
|
+
cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw,
|
|
963
|
+
cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw,
|
|
964
|
+
cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Creates a quaternion from an axis-angle representation.
|
|
969
|
+
* @param axis - The axis of rotation (should be normalized).
|
|
970
|
+
* @param angleRadians - The angle of rotation in radians.
|
|
971
|
+
* @returns A new Quaternion representing the rotation.
|
|
972
|
+
*/
|
|
973
|
+
static fromAxisAngle(axis, angleRadians) {
|
|
974
|
+
const halfAngle = angleRadians * 0.5;
|
|
975
|
+
const sinHalfAngle = Math.sin(halfAngle);
|
|
976
|
+
return new _Quaternion(
|
|
977
|
+
axis.x * sinHalfAngle,
|
|
978
|
+
axis.y * sinHalfAngle,
|
|
979
|
+
axis.z * sinHalfAngle,
|
|
980
|
+
Math.cos(halfAngle)
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
// ../core/src/transform-component.ts
|
|
986
|
+
var TransformComponent = class _TransformComponent extends Component {
|
|
987
|
+
/** The position in world space. */
|
|
988
|
+
position = Vector3.zero();
|
|
989
|
+
/** The rotation as a quaternion. */
|
|
990
|
+
rotation = Quaternion.identity();
|
|
991
|
+
/** The scale factor along each axis. */
|
|
992
|
+
scale = Vector3.one();
|
|
993
|
+
/** The z-ordering index for 2D rendering. */
|
|
994
|
+
zIndex = 0;
|
|
995
|
+
/** Creates a deep copy of this transform component. */
|
|
996
|
+
clone() {
|
|
997
|
+
const cloned = new _TransformComponent();
|
|
998
|
+
cloned.position = this.position.clone();
|
|
999
|
+
cloned.rotation = this.rotation.clone();
|
|
1000
|
+
cloned.scale = this.scale.clone();
|
|
1001
|
+
cloned.zIndex = this.zIndex;
|
|
1002
|
+
return cloned;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Serializes this component's state to a plain object.
|
|
1006
|
+
* @returns A record containing position, rotation, scale, and zIndex.
|
|
1007
|
+
*/
|
|
1008
|
+
serialize() {
|
|
1009
|
+
return {
|
|
1010
|
+
position: { x: this.position.x, y: this.position.y, z: this.position.z },
|
|
1011
|
+
rotation: { x: this.rotation.x, y: this.rotation.y, z: this.rotation.z, w: this.rotation.w },
|
|
1012
|
+
scale: { x: this.scale.x, y: this.scale.y, z: this.scale.z },
|
|
1013
|
+
zIndex: this.zIndex
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Restores this component's state from a serialized object.
|
|
1018
|
+
* @param data - The serialized data to restore from.
|
|
1019
|
+
*/
|
|
1020
|
+
deserialize(data) {
|
|
1021
|
+
const position = data["position"];
|
|
1022
|
+
if (position) {
|
|
1023
|
+
this.position = new Vector3(position.x, position.y, position.z);
|
|
1024
|
+
}
|
|
1025
|
+
const rotation = data["rotation"];
|
|
1026
|
+
if (rotation) {
|
|
1027
|
+
this.rotation = new Quaternion(rotation.x, rotation.y, rotation.z, rotation.w);
|
|
1028
|
+
}
|
|
1029
|
+
const scale = data["scale"];
|
|
1030
|
+
if (scale) {
|
|
1031
|
+
this.scale = new Vector3(scale.x, scale.y, scale.z);
|
|
1032
|
+
}
|
|
1033
|
+
if (typeof data["zIndex"] === "number") {
|
|
1034
|
+
this.zIndex = data["zIndex"];
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
// ../core/src/motion-component.ts
|
|
1040
|
+
var MotionComponent = class _MotionComponent extends Component {
|
|
1041
|
+
/**
|
|
1042
|
+
* The linear velocity in units per second.
|
|
1043
|
+
*
|
|
1044
|
+
* Immutable — to change, assign a new Vector3
|
|
1045
|
+
* (`motion.velocity = new Vector3(x, y, z)`) or call setVelocity.
|
|
1046
|
+
*/
|
|
1047
|
+
velocity = Vector3.zero();
|
|
1048
|
+
/**
|
|
1049
|
+
* The linear acceleration in units per second squared.
|
|
1050
|
+
*
|
|
1051
|
+
* Immutable — to change, assign a new Vector3
|
|
1052
|
+
* (`motion.acceleration = new Vector3(x, y, z)`) or call setAcceleration.
|
|
1053
|
+
*/
|
|
1054
|
+
acceleration = Vector3.zero();
|
|
1055
|
+
/** The angular velocity in radians per second. */
|
|
1056
|
+
angularVelocity = 0;
|
|
1057
|
+
/** The torque applied to the entity in radians per second squared. */
|
|
1058
|
+
torque = 0;
|
|
1059
|
+
/** The rotational inertia of the entity. */
|
|
1060
|
+
inertia = 1;
|
|
1061
|
+
/**
|
|
1062
|
+
* Replaces the velocity with a new Vector3 constructed from the given components.
|
|
1063
|
+
* @param x - The x component of the new velocity.
|
|
1064
|
+
* @param y - The y component of the new velocity.
|
|
1065
|
+
* @param z - The z component of the new velocity.
|
|
1066
|
+
*/
|
|
1067
|
+
setVelocity(x, y, z) {
|
|
1068
|
+
this.velocity = new Vector3(x, y, z);
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Replaces the acceleration with a new Vector3 constructed from the given components.
|
|
1072
|
+
* @param x - The x component of the new acceleration.
|
|
1073
|
+
* @param y - The y component of the new acceleration.
|
|
1074
|
+
* @param z - The z component of the new acceleration.
|
|
1075
|
+
*/
|
|
1076
|
+
setAcceleration(x, y, z) {
|
|
1077
|
+
this.acceleration = new Vector3(x, y, z);
|
|
1078
|
+
}
|
|
1079
|
+
/** Creates a deep copy of this motion component. */
|
|
1080
|
+
clone() {
|
|
1081
|
+
const cloned = new _MotionComponent();
|
|
1082
|
+
cloned.velocity = this.velocity.clone();
|
|
1083
|
+
cloned.acceleration = this.acceleration.clone();
|
|
1084
|
+
cloned.angularVelocity = this.angularVelocity;
|
|
1085
|
+
cloned.torque = this.torque;
|
|
1086
|
+
cloned.inertia = this.inertia;
|
|
1087
|
+
return cloned;
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Serializes this component's state to a plain object.
|
|
1091
|
+
* @returns A record containing velocity, acceleration, and rotational properties.
|
|
1092
|
+
*/
|
|
1093
|
+
serialize() {
|
|
1094
|
+
return {
|
|
1095
|
+
velocity: { x: this.velocity.x, y: this.velocity.y, z: this.velocity.z },
|
|
1096
|
+
acceleration: { x: this.acceleration.x, y: this.acceleration.y, z: this.acceleration.z },
|
|
1097
|
+
angularVelocity: this.angularVelocity,
|
|
1098
|
+
torque: this.torque,
|
|
1099
|
+
inertia: this.inertia
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Restores this component's state from a serialized object.
|
|
1104
|
+
* @param data - The serialized data to restore from.
|
|
1105
|
+
*/
|
|
1106
|
+
deserialize(data) {
|
|
1107
|
+
const velocity = data["velocity"];
|
|
1108
|
+
if (velocity) {
|
|
1109
|
+
this.velocity = new Vector3(velocity.x, velocity.y, velocity.z);
|
|
1110
|
+
}
|
|
1111
|
+
const acceleration = data["acceleration"];
|
|
1112
|
+
if (acceleration) {
|
|
1113
|
+
this.acceleration = new Vector3(acceleration.x, acceleration.y, acceleration.z);
|
|
1114
|
+
}
|
|
1115
|
+
if (typeof data["angularVelocity"] === "number") {
|
|
1116
|
+
this.angularVelocity = data["angularVelocity"];
|
|
1117
|
+
}
|
|
1118
|
+
if (typeof data["torque"] === "number") {
|
|
1119
|
+
this.torque = data["torque"];
|
|
1120
|
+
}
|
|
1121
|
+
if (typeof data["inertia"] === "number") {
|
|
1122
|
+
this.inertia = data["inertia"];
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
// ../core/src/motion-system.ts
|
|
1128
|
+
var MotionSystem = class extends System {
|
|
1129
|
+
/** This system runs during the update phase. */
|
|
1130
|
+
systemType = "update" /* Update */;
|
|
1131
|
+
/** The query tracking entities with both TransformComponent and MotionComponent. */
|
|
1132
|
+
motionQuery;
|
|
1133
|
+
/**
|
|
1134
|
+
* Creates a new MotionSystem.
|
|
1135
|
+
* @param world - The world to create the motion query in.
|
|
1136
|
+
*/
|
|
1137
|
+
constructor(world) {
|
|
1138
|
+
super();
|
|
1139
|
+
this.motionQuery = world.query([TransformComponent, MotionComponent]);
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Updates all entities with motion, applying acceleration to velocity and velocity to position.
|
|
1143
|
+
* @param elapsedMilliseconds - The time elapsed since the last update in milliseconds.
|
|
1144
|
+
*/
|
|
1145
|
+
update(elapsedMilliseconds) {
|
|
1146
|
+
const deltaTimeSeconds = elapsedMilliseconds / 1e3;
|
|
1147
|
+
for (const entity of this.motionQuery.entities) {
|
|
1148
|
+
const transform = entity.get(TransformComponent);
|
|
1149
|
+
const motion = entity.get(MotionComponent);
|
|
1150
|
+
motion.velocity = motion.velocity.add(motion.acceleration.scale(deltaTimeSeconds));
|
|
1151
|
+
transform.position = transform.position.add(motion.velocity.scale(deltaTimeSeconds));
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
export { Component, Entity, EntityManager, EventEmitter, MotionComponent, MotionSystem, Observable, Query, QueryManager, System, SystemManager, SystemPriority, SystemType, TransformComponent, World };
|
|
1157
|
+
//# sourceMappingURL=index.js.map
|
|
1158
|
+
//# sourceMappingURL=index.js.map
|