mani-game-engine 1.0.0-pre.34 → 1.0.0-pre.36

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,123 +1,131 @@
1
- import {GameEngine} from './gameEngine';
2
- import {Signal} from 'mani-signal';
3
- import {Class} from './types';
4
- import {entityComponents} from './ecsInjector';
5
-
6
- let idCounter = 0;
7
-
8
- export class Entity {
9
-
10
- readonly id: number;
11
- readonly children = new Set<Entity>();
12
- private gameEngine?: GameEngine;
13
-
14
- // TODO: use single rootEntity class instead of undefined?
15
- private _parent?: Entity;
16
- readonly parentChanged = new Signal();
17
- readonly onRemove = new Signal();
18
- private markRemoval = false;
19
- private _detached = false;
20
- get detached(): boolean { return this._detached; }
21
-
22
- constructor() {
23
- this.id = idCounter++;
24
- }
25
-
26
- async onBeforeRemove?(): Promise<void>;
27
-
28
- private setParent(parent: Entity | undefined) {
29
- if (parent === this._parent) {
30
- //TODO: i think this check can be removed
31
- throw Error('same parent');
32
- }
33
- this._parent = parent;
34
- this.parentChanged.dispatch();
35
- }
36
-
37
- get parent(): Entity | undefined {
38
- return this._parent;
39
- }
40
-
41
- getComponent<T extends Class>(componentClass: T): InstanceType<T> | undefined {
42
- const componentMap = entityComponents.get(this.constructor as Class);
43
- if (!componentMap) {
44
- throw new Error(`No components in entity of type '${this.constructor.name}'.`);
45
- }
46
- const key = componentMap.get(componentClass as T);
47
- return key && (<any>this)[key];
48
- }
49
-
50
- async add(entity: Entity) {
51
- if (entity.parent === this) {
52
- throw new Error('Entity is already a child');
53
- }
54
- if (entity === this) {
55
- throw new Error('Could not add Entity as a child of itself.');
56
- }
57
-
58
- if (entity.parent) {
59
- entity.parent.children.delete(entity);
60
- }
61
- this.children.add(entity);
62
- entity.setParent(this);
63
-
64
- if (entity.gameEngine) {
65
- if (this.gameEngine !== entity.gameEngine) {
66
- throw new Error('cant add an active to a passive entity');
67
- }
68
- } else {
69
- if (this.gameEngine) {
70
-
71
- await this.gameEngine.add(entity);
72
- }
73
- }
74
-
75
- // TODO: systems should be informed so they can do stuff (renderer system should add object 3d etc...)
76
-
77
- if (this.gameEngine && this.gameEngine !== entity.gameEngine) {
78
-
79
- }
80
- }
81
-
82
- async remove(entity: Entity) {
83
- if (!this.children.delete(entity)) {
84
- throw new Error('Could not remove. Entity is not a child.');
85
- }
86
- entity.setParent(undefined);
87
- if (this.gameEngine) {
88
- return await this.gameEngine.remove(entity);
89
- }
90
- }
91
-
92
- async detach() {
93
- if (this._detached) {
94
- throw new Error('entity already detached');
95
- }
96
- this._detached = true;
97
- // TODO: mark for detach and prevent another call, because this.gameEngine is set to undefined until next frame
98
- if (this.parent) {
99
- await this.parent.remove(this);
100
- } else {
101
- if (!this.gameEngine) {
102
- throw new Error('entity isn\'t attached to a parent or the game engine');
103
- }
104
- await this.gameEngine.remove(this);
105
- }
106
- }
107
-
108
- has(entity: Entity) {
109
- return this.children.has(entity);
110
- }
111
-
112
- /**
113
- * this includes the entity itself
114
- * @param target
115
- */
116
- getAllEntities(target: Entity[] = []) {
117
- target.push(this);
118
- for (const child of this.children) {
119
- child.getAllEntities(target);
120
- }
121
- return target;
122
- }
123
- }
1
+ import {GameEngine} from './gameEngine';
2
+ import {Signal} from 'mani-signal';
3
+ import {Class} from './types';
4
+ import {entityComponents} from './ecsInjector';
5
+ import {ID, putIfAbsent} from './injector';
6
+
7
+ let idCounter = 0;
8
+
9
+ export class Entity {
10
+
11
+ readonly id: number;
12
+ readonly children = new Set<Entity>();
13
+ private gameEngine?: GameEngine;
14
+
15
+ // TODO: use single rootEntity class instead of undefined?
16
+ private _parent?: Entity;
17
+ readonly parentChanged = new Signal();
18
+ readonly onRemove = new Signal();
19
+ private markRemoval = false;
20
+ private _detached = false;
21
+ private readonly signalMap: Map<ID, Signal> = new Map<ID, Signal>();
22
+
23
+ get detached(): boolean { return this._detached; }
24
+
25
+ constructor() {
26
+ this.id = idCounter++;
27
+ }
28
+
29
+ async onBeforeRemove?(): Promise<void>;
30
+
31
+ getSignal<T>(id: ID): Signal<T> {
32
+ return putIfAbsent(this.signalMap, id, () => new Signal<any>());
33
+ }
34
+
35
+ private setParent(parent: Entity | undefined) {
36
+ if (parent === this._parent) {
37
+ //TODO: i think this check can be removed
38
+ throw Error('same parent');
39
+ }
40
+ this._parent = parent;
41
+ this.parentChanged.dispatch();
42
+ }
43
+
44
+ get parent(): Entity | undefined {
45
+ return this._parent;
46
+ }
47
+
48
+ getComponent<T extends Class>(componentClass: T): InstanceType<T> | undefined {
49
+ const componentMap = entityComponents.get(this.constructor as Class);
50
+ if (!componentMap) {
51
+ throw new Error(`No components in entity of type '${this.constructor.name}'.`);
52
+ }
53
+ const key = componentMap.get(componentClass as T);
54
+ return key && (<any>this)[key];
55
+ }
56
+
57
+ async add(entity: Entity) {
58
+ if (entity.parent === this) {
59
+ throw new Error('Entity is already a child');
60
+ }
61
+ if (entity === this) {
62
+ throw new Error('Could not add Entity as a child of itself.');
63
+ }
64
+
65
+ if (entity.parent) {
66
+ entity.parent.children.delete(entity);
67
+ }
68
+ this.children.add(entity);
69
+ entity.setParent(this);
70
+
71
+ if (entity.gameEngine) {
72
+ if (this.gameEngine !== entity.gameEngine) {
73
+ throw new Error('cant add an active to a passive entity');
74
+ }
75
+ } else {
76
+ if (this.gameEngine) {
77
+
78
+ await this.gameEngine.add(entity);
79
+ }
80
+ }
81
+
82
+ // TODO: systems should be informed so they can do stuff (renderer system should add object 3d etc...)
83
+
84
+ if (this.gameEngine && this.gameEngine !== entity.gameEngine) {
85
+
86
+ }
87
+ }
88
+
89
+ async remove(entity: Entity) {
90
+ if (!this.children.delete(entity)) {
91
+ throw new Error('Could not remove. Entity is not a child.');
92
+ }
93
+ entity.setParent(undefined);
94
+ if (this.gameEngine) {
95
+ return await this.gameEngine.remove(entity);
96
+ }
97
+ this.signalMap.forEach(signal => signal.detachAll());
98
+ }
99
+
100
+ async detach() {
101
+ if (this._detached) {
102
+ throw new Error('entity already detached');
103
+ }
104
+ this._detached = true;
105
+ // TODO: mark for detach and prevent another call, because this.gameEngine is set to undefined until next frame
106
+ if (this.parent) {
107
+ await this.parent.remove(this);
108
+ } else {
109
+ if (!this.gameEngine) {
110
+ throw new Error('entity isn\'t attached to a parent or the game engine');
111
+ }
112
+ await this.gameEngine.remove(this);
113
+ }
114
+ }
115
+
116
+ has(entity: Entity) {
117
+ return this.children.has(entity);
118
+ }
119
+
120
+ /**
121
+ * this includes the entity itself
122
+ * @param target
123
+ */
124
+ getAllEntities(target: Entity[] = []) {
125
+ target.push(this);
126
+ for (const child of this.children) {
127
+ child.getAllEntities(target);
128
+ }
129
+ return target;
130
+ }
131
+ }