mani-game-engine 1.0.0-pre.3 → 1.0.0-pre.30
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/LICENSE +1 -1
- package/lib/clock.d.ts +34 -20
- package/lib/clock.js +55 -15
- package/lib/clock.js.map +1 -1
- package/lib/ecsInjector.d.ts +12 -6
- package/lib/ecsInjector.js +55 -29
- package/lib/ecsInjector.js.map +1 -1
- package/lib/entity.d.ts +8 -7
- package/lib/entity.js +29 -17
- package/lib/entity.js.map +1 -1
- package/lib/gameEngine.d.ts +44 -19
- package/lib/gameEngine.js +213 -103
- package/lib/gameEngine.js.map +1 -1
- package/lib/index.d.ts +9 -3
- package/lib/index.js +24 -5
- package/lib/index.js.map +1 -1
- package/lib/injector.d.ts +121 -0
- package/lib/injector.js +290 -0
- package/lib/injector.js.map +1 -0
- package/lib/scope/scopeContext.d.ts +57 -0
- package/lib/scope/scopeContext.js +255 -0
- package/lib/scope/scopeContext.js.map +1 -0
- package/lib/systemContext.d.ts +12 -0
- package/lib/systemContext.js +24 -0
- package/lib/systemContext.js.map +1 -0
- package/lib/types.d.ts +28 -9
- package/lib/types.js +17 -0
- package/lib/types.js.map +1 -1
- package/lib/utils/map2k.js +5 -1
- package/lib/utils/map2k.js.map +1 -1
- package/package.json +15 -21
- package/src/clock.ts +90 -29
- package/src/ecsInjector.ts +42 -19
- package/src/entity.ts +24 -14
- package/src/gameEngine.ts +271 -137
- package/src/index.ts +12 -3
- package/src/injector.ts +363 -0
- package/src/scope/scopeContext.ts +320 -0
- package/src/systemContext.ts +27 -0
- package/src/types.ts +27 -10
package/src/ecsInjector.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// TODO: what if systems for entity without components are created?
|
|
2
|
-
import {Class, createDependencyAnnotation, Dependency, ID, Injector, putIfAbsent, ResolverContext, ResolverFunction} from 'mani-injector';
|
|
3
2
|
import {Signal} from 'mani-signal';
|
|
3
|
+
import {Class, createDependencyAnnotation, Dependency, ID, Injector, putIfAbsent, ResolverContext, ResolverFunction} from './injector';
|
|
4
|
+
import {SystemContext} from './systemContext';
|
|
4
5
|
|
|
5
6
|
type ComponentClass = Class;
|
|
6
7
|
type EntityClass = Class;
|
|
@@ -8,6 +9,7 @@ type EntityClass = Class;
|
|
|
8
9
|
type SystemResolvers<T extends Class> = [T, ResolverFunction[]]
|
|
9
10
|
type ComponentDependency = Dependency & { kind: 'component'; index: number; type: Class; };
|
|
10
11
|
type EntityDependency = Dependency & { kind: 'entity'; index: number; };
|
|
12
|
+
type ContextDependency = Dependency & { kind: 'context'; index: number; };
|
|
11
13
|
type SignalDependency = Dependency & { kind: 'signal'; index: number; id: ID; };
|
|
12
14
|
|
|
13
15
|
type EntityResolverContext = ResolverContext & { entityClass: Object; };
|
|
@@ -16,6 +18,7 @@ export const entityComponents = new Map<EntityClass, Map<ComponentClass, string>
|
|
|
16
18
|
|
|
17
19
|
export const GetComponent = createDependencyAnnotation((type, index): ComponentDependency => ({kind: 'component', type, index}));
|
|
18
20
|
export const GetEntity = createDependencyAnnotation((_type, index): EntityDependency => ({kind: 'entity', index}));
|
|
21
|
+
export const GetContext = createDependencyAnnotation((_type, index): ContextDependency => ({kind: 'context', index}));
|
|
19
22
|
export const GetSignal = (id: ID) => createDependencyAnnotation((_type, index): SignalDependency => ({kind: 'signal', index, id}));
|
|
20
23
|
export const EntityComponent = (target: object, propertyKey: string): any => {
|
|
21
24
|
const entityClass = target.constructor;
|
|
@@ -28,12 +31,15 @@ export const EntityComponent = (target: object, propertyKey: string): any => {
|
|
|
28
31
|
};
|
|
29
32
|
|
|
30
33
|
export const signalHandlers = new Map<Object, [string, ID][]>();
|
|
34
|
+
export const staticSignalHandlers = new Map<Object, [string, ID][]>();
|
|
31
35
|
export const OnSignal = (id: ID) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
32
36
|
if (target instanceof Function) {
|
|
33
|
-
|
|
37
|
+
let mappingList = putIfAbsent(staticSignalHandlers, target, (): [string, ID][] => []);
|
|
38
|
+
mappingList.push([propertyKey, id]);
|
|
39
|
+
} else {
|
|
40
|
+
let mappingList = putIfAbsent(signalHandlers, target.constructor, (): [string, ID][] => []);
|
|
41
|
+
mappingList.push([propertyKey, id]);
|
|
34
42
|
}
|
|
35
|
-
let mappingList = putIfAbsent(signalHandlers, target.constructor, ():[string, ID][] => []);
|
|
36
|
-
mappingList.push([propertyKey, id]);
|
|
37
43
|
};
|
|
38
44
|
|
|
39
45
|
const getComponentDependencies = (system: Class) => {
|
|
@@ -69,14 +75,21 @@ const componentResolver = (context: ResolverContext, dependency: Dependency): Re
|
|
|
69
75
|
}
|
|
70
76
|
const entityClass = (context as EntityResolverContext).entityClass;
|
|
71
77
|
const key = entityComponents.get(<any>entityClass as Class)!.get((dependency as ComponentDependency).type);
|
|
72
|
-
return (
|
|
78
|
+
return (context: SystemContext) => (context.entity as any)[key!];
|
|
73
79
|
};
|
|
74
80
|
|
|
75
81
|
const entityResolver = ({type, kind}: ResolverContext, _dependency: Dependency): ResolverFunction => {
|
|
76
82
|
if (kind !== 'system') {
|
|
77
83
|
throw new Error(`Could not resolve Entity in ${type.name}. @GetEntity only allowed in system scope.`);
|
|
78
84
|
}
|
|
79
|
-
return (
|
|
85
|
+
return (context: SystemContext) => context.entity;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const contextResolver = ({type, kind}: ResolverContext, _dependency: Dependency): ResolverFunction => {
|
|
89
|
+
if (kind !== 'system') {
|
|
90
|
+
throw new Error(`Could not resolve Context in ${type.name}. @GetContext only allowed in system scope.`);
|
|
91
|
+
}
|
|
92
|
+
return (context: SystemContext) => context;
|
|
80
93
|
};
|
|
81
94
|
|
|
82
95
|
// const isSignalResolver = (dependency: Dependency): dependency is SignalDependency => dependency.kind === 'signal';
|
|
@@ -90,13 +103,15 @@ export class EcsInjector<SystemClass extends Class = Class> extends Injector {
|
|
|
90
103
|
constructor(parent?: EcsInjector<SystemClass>) {
|
|
91
104
|
super(parent);
|
|
92
105
|
this.signalMap = parent ? parent.signalMap : new Map<ID, Signal>();
|
|
93
|
-
this.entitySystemMap =
|
|
94
|
-
this.entitySystemResolverTuples =
|
|
106
|
+
this.entitySystemMap = new Map<EntityClass, SystemClass[]>();
|
|
107
|
+
this.entitySystemResolverTuples = new Map<EntityClass, SystemResolvers<SystemClass>[]>();
|
|
108
|
+
this.map(EcsInjector).toValue(this);
|
|
95
109
|
|
|
96
110
|
if (!parent) {
|
|
97
111
|
// only add extension resolvers to the main/parent injector
|
|
98
112
|
this.addExtensionResolver('entity', entityResolver);
|
|
99
113
|
this.addExtensionResolver('component', componentResolver);
|
|
114
|
+
this.addExtensionResolver('context', contextResolver);
|
|
100
115
|
|
|
101
116
|
const signalResolver = ({kind}: ResolverContext, dependency: Dependency): ResolverFunction => {
|
|
102
117
|
const signal = putIfAbsent(this.signalMap, (<SignalDependency>dependency).id, () => new Signal());
|
|
@@ -123,39 +138,47 @@ export class EcsInjector<SystemClass extends Class = Class> extends Injector {
|
|
|
123
138
|
}
|
|
124
139
|
}
|
|
125
140
|
|
|
126
|
-
createSystems(entity: Object):
|
|
141
|
+
createSystems(entity: Object): SystemContext[] {
|
|
127
142
|
const systemResolverTuples = putIfAbsent(this.entitySystemResolverTuples, entity.constructor, (): SystemResolvers<SystemClass>[] => {
|
|
128
143
|
const systems = this.entitySystemMap.get(entity.constructor as Class);
|
|
129
144
|
if (!systems) {
|
|
130
|
-
console.warn('no system for entity ' + entity.constructor.name);
|
|
131
145
|
return [];
|
|
132
146
|
}
|
|
133
147
|
|
|
134
148
|
const result: SystemResolvers<SystemClass>[] = [];
|
|
135
|
-
for (const systemClass of systems
|
|
149
|
+
for (const systemClass of systems) {
|
|
136
150
|
result.push([systemClass, this.createResolverArray({type: systemClass, kind: 'system', entityClass: entity.constructor})]);
|
|
137
151
|
}
|
|
138
152
|
return result;
|
|
139
153
|
});
|
|
140
|
-
const systemInstances:
|
|
154
|
+
const systemInstances: SystemContext[] = [];
|
|
141
155
|
for (const [system, resolver] of systemResolverTuples) {
|
|
142
156
|
const args = new Array(resolver.length);
|
|
157
|
+
|
|
158
|
+
const systemContext = new SystemContext<InstanceType<SystemClass>>(entity);
|
|
159
|
+
|
|
143
160
|
for (let i = 0; i < args.length; i++) {
|
|
144
|
-
args[i] = resolver[i](
|
|
161
|
+
args[i] = resolver[i](systemContext);
|
|
145
162
|
}
|
|
146
163
|
|
|
147
|
-
|
|
148
|
-
// TODO: new class(...args) is very slow in firefox :/
|
|
149
|
-
// let systemInstance = new (system.bind(system, args[0], args[1], args[2], args[3], args[4]));
|
|
164
|
+
// friend class :)
|
|
165
|
+
(systemContext as any).system = new system(...args); // TODO: new class(...args) is very slow in firefox :/
|
|
150
166
|
|
|
151
|
-
// systemInstance.
|
|
167
|
+
// let systemInstance = new (system.bind(system, args[0], args[1], args[2], args[3], args[4]));
|
|
152
168
|
|
|
153
|
-
systemInstances.push(
|
|
169
|
+
systemInstances.push(systemContext);
|
|
154
170
|
}
|
|
155
171
|
return systemInstances;
|
|
156
172
|
}
|
|
157
173
|
|
|
158
174
|
getSignal<T>(id: ID): Signal<T> {
|
|
159
|
-
return putIfAbsent(this.signalMap, id, () => new Signal());
|
|
175
|
+
return putIfAbsent(this.signalMap, id, () => new Signal<any>());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
dispose() {
|
|
179
|
+
// TODO: implement dispose in Injector
|
|
180
|
+
for (const [key, signal] of this.signalMap) {
|
|
181
|
+
signal.detachAll();
|
|
182
|
+
}
|
|
160
183
|
}
|
|
161
184
|
}
|
package/src/entity.ts
CHANGED
|
@@ -8,7 +8,7 @@ let idCounter = 0;
|
|
|
8
8
|
export class Entity {
|
|
9
9
|
|
|
10
10
|
readonly id: number;
|
|
11
|
-
children = new Set<Entity>();
|
|
11
|
+
readonly children = new Set<Entity>();
|
|
12
12
|
private gameEngine?: GameEngine;
|
|
13
13
|
|
|
14
14
|
// TODO: use single rootEntity class instead of undefined?
|
|
@@ -16,7 +16,8 @@ export class Entity {
|
|
|
16
16
|
readonly parentChanged = new Signal();
|
|
17
17
|
readonly onRemove = new Signal();
|
|
18
18
|
private markRemoval = false;
|
|
19
|
-
|
|
19
|
+
private _detached = false;
|
|
20
|
+
get detached(): boolean { return this._detached; }
|
|
20
21
|
|
|
21
22
|
constructor() {
|
|
22
23
|
this.id = idCounter++;
|
|
@@ -67,7 +68,7 @@ export class Entity {
|
|
|
67
68
|
} else {
|
|
68
69
|
if (this.gameEngine) {
|
|
69
70
|
|
|
70
|
-
await this.gameEngine.
|
|
71
|
+
await this.gameEngine.add(entity);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -78,35 +79,44 @@ export class Entity {
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
remove(entity: Entity) {
|
|
82
|
+
async remove(entity: Entity) {
|
|
82
83
|
if (!this.children.delete(entity)) {
|
|
83
84
|
throw new Error('Could not remove. Entity is not a child.');
|
|
84
85
|
}
|
|
85
86
|
entity.setParent(undefined);
|
|
87
|
+
if (this.gameEngine) {
|
|
88
|
+
return await this.gameEngine.remove(entity);
|
|
89
|
+
}
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
async detach() {
|
|
89
|
-
if (
|
|
90
|
-
throw new Error('
|
|
93
|
+
if (this._detached) {
|
|
94
|
+
throw new Error('entity already detached');
|
|
91
95
|
}
|
|
96
|
+
this._detached = true;
|
|
97
|
+
// TODO: mark for detach and prevent another call, because this.gameEngine is set to undefined until next frame
|
|
92
98
|
if (this.parent) {
|
|
93
|
-
this.parent.remove(this);
|
|
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);
|
|
94
105
|
}
|
|
95
|
-
await this.gameEngine.removeEntity(this);
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
has(entity: Entity) {
|
|
99
109
|
return this.children.has(entity);
|
|
100
110
|
}
|
|
101
111
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
/**
|
|
113
|
+
* this includes the entity itself
|
|
114
|
+
* @param target
|
|
115
|
+
*/
|
|
116
|
+
getAllEntities(target: Entity[] = []) {
|
|
107
117
|
target.push(this);
|
|
108
118
|
for (const child of this.children) {
|
|
109
|
-
child.
|
|
119
|
+
child.getAllEntities(target);
|
|
110
120
|
}
|
|
111
121
|
return target;
|
|
112
122
|
}
|