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/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
- private readonly entitySystemMap = new Map<Entity, SystemContext[]>();
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
- context.dispose();
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 nexSystemContexts: SystemContext[] = [];
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.createSystems(entity);
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
- nexSystemContexts.push(context);
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
 
@@ -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: Object) {
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