mani-game-engine 1.0.0-pre.35 → 1.0.0-pre.37
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/lib/clock.d.ts +2 -2
- package/lib/ecsInjector.d.ts +25 -7
- package/lib/ecsInjector.js +76 -27
- package/lib/ecsInjector.js.map +1 -1
- package/lib/entity.d.ts +7 -0
- package/lib/entity.js +59 -3
- package/lib/entity.js.map +1 -1
- package/lib/gameEngine.d.ts +3 -1
- package/lib/gameEngine.js +43 -39
- package/lib/gameEngine.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/injector.d.ts +20 -20
- package/lib/injector.js.map +1 -1
- package/lib/scope/scopeContext.d.ts +3 -3
- package/lib/systemContext.d.ts +3 -3
- package/lib/systemContext.js.map +1 -1
- package/lib/types.d.ts +7 -7
- package/lib/types.js.map +1 -1
- package/package.json +10 -10
- package/src/ecsInjector.ts +92 -30
- package/src/entity.ts +62 -2
- package/src/gameEngine.ts +44 -39
- package/src/index.ts +1 -1
- package/src/injector.ts +1 -0
- package/src/systemContext.ts +3 -2
- package/src/types.ts +1 -0
package/src/entity.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {GameEngine} from './gameEngine';
|
|
2
2
|
import {Signal} from 'mani-signal';
|
|
3
3
|
import {Class} from './types';
|
|
4
|
-
import {entityComponents} from './ecsInjector';
|
|
5
4
|
import {ID, putIfAbsent} from './injector';
|
|
5
|
+
import {EcsInjector} from './ecsInjector';
|
|
6
6
|
|
|
7
7
|
let idCounter = 0;
|
|
8
8
|
|
|
@@ -20,6 +20,62 @@ export class Entity {
|
|
|
20
20
|
private _detached = false;
|
|
21
21
|
private readonly signalMap: Map<ID, Signal> = new Map<ID, Signal>();
|
|
22
22
|
|
|
23
|
+
private dynamicComponents = new Map<Class, InstanceType<any>>();
|
|
24
|
+
|
|
25
|
+
addDynamicComponent<T extends Class>(component: InstanceType<T>) {
|
|
26
|
+
if (this.hasDynamicComponentClass(component.constructor)) {
|
|
27
|
+
throw new Error('component of this class already added');
|
|
28
|
+
}
|
|
29
|
+
this.dynamicComponents.set(component.constructor as T, component);
|
|
30
|
+
|
|
31
|
+
if (!this.gameEngine) return;
|
|
32
|
+
|
|
33
|
+
const systemContexts = this.gameEngine.injector.createSystemsForDynamicComponents(component.constructor, this);
|
|
34
|
+
|
|
35
|
+
if (!systemContexts) return;
|
|
36
|
+
const preparePromises = [];
|
|
37
|
+
for (const context of systemContexts) {
|
|
38
|
+
context.system.onPrepare && preparePromises.push(context.system.onPrepare());
|
|
39
|
+
}
|
|
40
|
+
for (const context of systemContexts) {
|
|
41
|
+
this.gameEngine['entitySystemMap'].get(this)?.add(context);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (preparePromises.length === 0) {
|
|
45
|
+
this.gameEngine['startSystems'](systemContexts, () => {});
|
|
46
|
+
} else {
|
|
47
|
+
Promise.all(preparePromises).then(() => this.gameEngine?.['startSystems'](systemContexts));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
hasDynamicComponentClass<T extends Class>(componentClass: T): boolean {
|
|
53
|
+
return this.dynamicComponents.has(componentClass);
|
|
54
|
+
}
|
|
55
|
+
removeDynamicComponentClass<T extends Class>(componentClass: T) {
|
|
56
|
+
const systemContexts = this.gameEngine?.['entitySystemMap'].get(this);
|
|
57
|
+
systemContexts?.forEach(systemContext => {
|
|
58
|
+
const systemToDependencies = this.gameEngine?.injector['dynamicComponentDependencyMap'];
|
|
59
|
+
const dependencies = systemToDependencies?.get((systemContext.system as any).constructor);
|
|
60
|
+
if (!dependencies) return;
|
|
61
|
+
if (dependencies.some(dependency => dependency.type === componentClass)) {
|
|
62
|
+
this.gameEngine?.['disposeContext']?.(systemContext);
|
|
63
|
+
systemContexts?.delete(systemContext);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
if (!this.dynamicComponents.delete(componentClass)) {
|
|
67
|
+
throw new Error('component not found: ' + JSON.stringify(componentClass));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
removeDynamicComponent<T extends Class>(component: InstanceType<T>) {
|
|
72
|
+
this.removeDynamicComponentClass(component.constructor as T);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getDynamicComponent<T extends Class>(componentClass: T): InstanceType<T> | undefined {
|
|
76
|
+
return this.dynamicComponents.get(componentClass);
|
|
77
|
+
}
|
|
78
|
+
|
|
23
79
|
get detached(): boolean { return this._detached; }
|
|
24
80
|
|
|
25
81
|
constructor() {
|
|
@@ -46,7 +102,7 @@ export class Entity {
|
|
|
46
102
|
}
|
|
47
103
|
|
|
48
104
|
getComponent<T extends Class>(componentClass: T): InstanceType<T> | undefined {
|
|
49
|
-
const componentMap = entityComponents.get(this.constructor as Class);
|
|
105
|
+
const componentMap = EcsInjector.entityComponents.get(this.constructor as Class);
|
|
50
106
|
if (!componentMap) {
|
|
51
107
|
throw new Error(`No components in entity of type '${this.constructor.name}'.`);
|
|
52
108
|
}
|
|
@@ -128,4 +184,8 @@ export class Entity {
|
|
|
128
184
|
}
|
|
129
185
|
return target;
|
|
130
186
|
}
|
|
187
|
+
|
|
188
|
+
hasDynamicComponents() {
|
|
189
|
+
return this.dynamicComponents.size > 0;
|
|
190
|
+
}
|
|
131
191
|
}
|
package/src/gameEngine.ts
CHANGED
|
@@ -17,7 +17,7 @@ const defaultOptions = {
|
|
|
17
17
|
export type GameEngineOptions = Partial<typeof defaultOptions>;
|
|
18
18
|
declare global {
|
|
19
19
|
interface GameEngineActions {
|
|
20
|
-
'testAction': string
|
|
20
|
+
'testAction': string;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -50,7 +50,8 @@ export class GameEngine {
|
|
|
50
50
|
private readonly onLateUpdateSet = new Set<System>();
|
|
51
51
|
private readonly onRenderUpdateSet = new Set<System>();
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
// TODO: for performance, we could store the systems directly in the entity
|
|
54
|
+
private readonly entitySystemMap = new Map<Entity, Set<SystemContext>>();
|
|
54
55
|
|
|
55
56
|
private readonly serviceClasses = new Set<Class<Service>>();
|
|
56
57
|
private readonly services: Service[] = [];
|
|
@@ -346,19 +347,7 @@ export class GameEngine {
|
|
|
346
347
|
this.entitySystemMap.delete(entity);
|
|
347
348
|
if (systemContexts) {
|
|
348
349
|
for (const context of systemContexts) {
|
|
349
|
-
|
|
350
|
-
const system = context.system;
|
|
351
|
-
this.unmapAnnotatedSignals(system);
|
|
352
|
-
system.onEnd && system.onEnd();
|
|
353
|
-
system.onPreFixedUpdate && this.onPreFixedUpdateSet.delete(system);
|
|
354
|
-
system.onFixedUpdate && this.onFixedUpdateSet.delete(system);
|
|
355
|
-
system.onUpdate && this.onUpdateSet.delete(system);
|
|
356
|
-
system.onLateUpdate && this.onLateUpdateSet.delete(system);
|
|
357
|
-
system.onPhysicsUpdate && this.onPhysicsUpdateSet.delete(system);
|
|
358
|
-
system.onPrePhysicsUpdate && this.onPrePhysicsUpdateSet.delete(system);
|
|
359
|
-
system.onPostPhysicsUpdate && this.onPostPhysicsUpdateSet.delete(system);
|
|
360
|
-
system.onLateFixedUpdate && this.onLateFixedUpdateSet.delete(system);
|
|
361
|
-
system.onRender && this.onRenderUpdateSet.delete(system);
|
|
350
|
+
this.disposeContext(context);
|
|
362
351
|
}
|
|
363
352
|
}
|
|
364
353
|
entity.onRemove.dispatch();
|
|
@@ -370,45 +359,27 @@ export class GameEngine {
|
|
|
370
359
|
this.removeQueue = [];
|
|
371
360
|
|
|
372
361
|
for (const [entity, resolve] of this.addQueue) {
|
|
362
|
+
|
|
373
363
|
const preparePromises = [];
|
|
374
|
-
const
|
|
364
|
+
const nextSystemContexts: SystemContext[] = [];
|
|
375
365
|
const entities = entity.getAllEntities();
|
|
376
366
|
for (const entity of entities) {
|
|
377
367
|
if ((<any>entity).gameEngine) throw new Error('Entity already added to a gameEngine');
|
|
378
368
|
(<any>entity).gameEngine = this;
|
|
379
|
-
const systemContexts = this.injector.
|
|
369
|
+
const systemContexts = this.injector.createSystemsForEntity(entity);
|
|
380
370
|
for (const context of systemContexts) {
|
|
381
371
|
// TODO: implement synced mode where async onPrepares aren't allowed
|
|
382
372
|
context.system.onPrepare && preparePromises.push(context.system.onPrepare());
|
|
383
|
-
|
|
373
|
+
nextSystemContexts.push(context);
|
|
384
374
|
}
|
|
385
375
|
this.entitySystemMap.set(entity, systemContexts);
|
|
386
376
|
}
|
|
387
377
|
|
|
388
|
-
const startSystems = () => {
|
|
389
|
-
// TODO: check if a "global" method is faster than this inner method
|
|
390
|
-
for (const context of nexSystemContexts) {
|
|
391
|
-
const system = context.system;
|
|
392
|
-
this.mapAnnotatedSignals(system);
|
|
393
|
-
system.onStart && system.onStart();
|
|
394
|
-
system.onPreFixedUpdate && this.onPreFixedUpdateSet.add(system);
|
|
395
|
-
system.onFixedUpdate && this.onFixedUpdateSet.add(system);
|
|
396
|
-
system.onUpdate && this.onUpdateSet.add(system);
|
|
397
|
-
system.onLateUpdate && this.onLateUpdateSet.add(system);
|
|
398
|
-
system.onPhysicsUpdate && this.onPhysicsUpdateSet.add(system);
|
|
399
|
-
system.onPrePhysicsUpdate && this.onPrePhysicsUpdateSet.add(system);
|
|
400
|
-
system.onPostPhysicsUpdate && this.onPostPhysicsUpdateSet.add(system);
|
|
401
|
-
system.onLateFixedUpdate && this.onLateFixedUpdateSet.add(system);
|
|
402
|
-
system.onRender && this.onRenderUpdateSet.add(system);
|
|
403
|
-
}
|
|
404
|
-
resolve();
|
|
405
|
-
};
|
|
406
|
-
|
|
407
378
|
// Promise.all doesnt resolve immediately when there are no promises bu
|
|
408
379
|
if (preparePromises.length === 0) {
|
|
409
|
-
startSystems();
|
|
380
|
+
this.startSystems(nextSystemContexts, resolve);
|
|
410
381
|
} else {
|
|
411
|
-
Promise.all(preparePromises).then(startSystems);
|
|
382
|
+
Promise.all(preparePromises).then(() => this.startSystems(nextSystemContexts, resolve));
|
|
412
383
|
}
|
|
413
384
|
}
|
|
414
385
|
this.addQueue = [];
|
|
@@ -419,6 +390,40 @@ export class GameEngine {
|
|
|
419
390
|
});
|
|
420
391
|
this.onPrepare.dispatch();
|
|
421
392
|
}
|
|
393
|
+
|
|
394
|
+
private startSystems(contexts: Iterable<SystemContext>, then?: () => void) {
|
|
395
|
+
for (const context of contexts) {
|
|
396
|
+
const system = context.system;
|
|
397
|
+
this.mapAnnotatedSignals(system);
|
|
398
|
+
system.onStart && system.onStart();
|
|
399
|
+
system.onPreFixedUpdate && this.onPreFixedUpdateSet.add(system);
|
|
400
|
+
system.onFixedUpdate && this.onFixedUpdateSet.add(system);
|
|
401
|
+
system.onUpdate && this.onUpdateSet.add(system);
|
|
402
|
+
system.onLateUpdate && this.onLateUpdateSet.add(system);
|
|
403
|
+
system.onPhysicsUpdate && this.onPhysicsUpdateSet.add(system);
|
|
404
|
+
system.onPrePhysicsUpdate && this.onPrePhysicsUpdateSet.add(system);
|
|
405
|
+
system.onPostPhysicsUpdate && this.onPostPhysicsUpdateSet.add(system);
|
|
406
|
+
system.onLateFixedUpdate && this.onLateFixedUpdateSet.add(system);
|
|
407
|
+
system.onRender && this.onRenderUpdateSet.add(system);
|
|
408
|
+
}
|
|
409
|
+
then?.();
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
private disposeContext(context: SystemContext<System>) {
|
|
413
|
+
context.dispose();
|
|
414
|
+
const system = context.system;
|
|
415
|
+
this.unmapAnnotatedSignals(system);
|
|
416
|
+
system.onEnd && system.onEnd();
|
|
417
|
+
system.onPreFixedUpdate && this.onPreFixedUpdateSet.delete(system);
|
|
418
|
+
system.onFixedUpdate && this.onFixedUpdateSet.delete(system);
|
|
419
|
+
system.onUpdate && this.onUpdateSet.delete(system);
|
|
420
|
+
system.onLateUpdate && this.onLateUpdateSet.delete(system);
|
|
421
|
+
system.onPhysicsUpdate && this.onPhysicsUpdateSet.delete(system);
|
|
422
|
+
system.onPrePhysicsUpdate && this.onPrePhysicsUpdateSet.delete(system);
|
|
423
|
+
system.onPostPhysicsUpdate && this.onPostPhysicsUpdateSet.delete(system);
|
|
424
|
+
system.onLateFixedUpdate && this.onLateFixedUpdateSet.delete(system);
|
|
425
|
+
system.onRender && this.onRenderUpdateSet.delete(system);
|
|
426
|
+
}
|
|
422
427
|
}
|
|
423
428
|
//
|
|
424
429
|
// type RunSpread<T> = T extends undefined
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ export {
|
|
|
17
17
|
Scope, SCOPE_CONTEXT, ScopeContext, OnScopeSignal, scopeSignalHandlers, ScopeSignalOptions, ScopeMapping,
|
|
18
18
|
} from './scope/scopeContext';
|
|
19
19
|
export {SystemContext, OnEntitySignal} from './systemContext';
|
|
20
|
-
export {EcsInjector, EntityComponent, GetComponent, GetEntity, GetContext, GetSignal, OnSignal, GetEntitySignal} from './ecsInjector';
|
|
20
|
+
export {EcsInjector, EntityComponent, GetComponent,GetDynamicComponent, GetEntity, GetContext, GetSignal, OnSignal, GetEntitySignal, } from './ecsInjector';
|
|
21
21
|
|
|
22
22
|
// intellij doesnt auto resolve imports if we just export * from an external dependency
|
|
23
23
|
|
package/src/injector.ts
CHANGED
|
@@ -73,6 +73,7 @@ export const createDependencyAnnotation = (cb: (type: any, index: number, depend
|
|
|
73
73
|
throw new Error('Could not inject class in itself.');
|
|
74
74
|
}
|
|
75
75
|
const depList = putIfAbsent(Injector.dependencyMap, dependantType, (): Dependency[] => []);
|
|
76
|
+
|
|
76
77
|
depList.push(cb(type, index, dependantType));
|
|
77
78
|
};
|
|
78
79
|
|
package/src/systemContext.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {System} from './types';
|
|
2
|
-
import {GameEngine, ID, putIfAbsent, Signal, SignalBinding, SignalCallback} from './index';
|
|
2
|
+
import {Entity, GameEngine, ID, putIfAbsent, Signal, SignalBinding, SignalCallback} from './index';
|
|
3
3
|
|
|
4
4
|
export const entitySignalHandlers = new Map<Object, [string, ID][]>();
|
|
5
5
|
export const OnEntitySignal = (id: ID) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
@@ -15,7 +15,7 @@ export class SystemContext<T extends System = System> {
|
|
|
15
15
|
readonly system!: T;
|
|
16
16
|
private signalBindings: SignalBinding[] = [];
|
|
17
17
|
|
|
18
|
-
constructor(readonly entity:
|
|
18
|
+
constructor(readonly entity: Entity) {
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// TODO: is this a bit hacky?
|
|
@@ -33,5 +33,6 @@ export class SystemContext<T extends System = System> {
|
|
|
33
33
|
|
|
34
34
|
dispose() {
|
|
35
35
|
for (const binding of this.signalBindings) binding.detach();
|
|
36
|
+
|
|
36
37
|
}
|
|
37
38
|
}
|
package/src/types.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface System {
|
|
|
14
14
|
onPostPhysicsUpdate?(time: number, deltaTime: number): void;
|
|
15
15
|
onLateFixedUpdate?(time: number, deltaTime: number): void;
|
|
16
16
|
onRender?(time: number, deltaTime: number, alpha: number): void;
|
|
17
|
+
// new(...args: any[]): System;
|
|
17
18
|
// new(): GameSystem;
|
|
18
19
|
}
|
|
19
20
|
|