From 75c15f2e7aa708f2f1fce3b64184b7572620cd71 Mon Sep 17 00:00:00 2001 From: Corban Riley Date: Mon, 1 Apr 2024 11:54:03 -0400 Subject: [PATCH] Removing old renew / release code as it is not part of an entity pool any longer --- README.md | 6 +-- src/Component.ts | 6 +-- src/Entity.ts | 10 +---- src/EntityManager.ts | 90 +++++++++++++++++++++++++------------ src/EntityPool.ts | 5 ++- src/World.ts | 8 ++-- tests/EntityManager.test.ts | 30 ++++++------- 7 files changed, 88 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 72e0357..afd4bd9 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,6 @@ An entity acts as a container for components. They have a unique id and include Resets an Entity to an empty state and gets a new unique id. -`renew()` - -Used by EntityManager to re instantiate an entity from the object pool. - `hasComponent(type: ComponentType)` \_alias: has Checks if entity contains a component by type. @@ -109,4 +105,4 @@ All Systems include an update method. ### World -The world is the user facing interface responsible for registering systems and archetype, creating and removing entities, and most importantly updating systems. +The world is the user facing interface responsible for registering systems and archetypes, creating and removing entities, and most importantly updating systems. diff --git a/src/Component.ts b/src/Component.ts index 5866f34..f2eef56 100644 --- a/src/Component.ts +++ b/src/Component.ts @@ -42,8 +42,4 @@ export abstract class Component { } } -export interface ComponentTypes { - [key: string]: Component -} - -// export type ComponentTypes = {} +export type ComponentTypes = Record> diff --git a/src/Entity.ts b/src/Entity.ts index 2faeb22..3d4cdec 100644 --- a/src/Entity.ts +++ b/src/Entity.ts @@ -36,7 +36,7 @@ export class Entity { constructor(components: ComponentOf[] = []) { this.reset() - this.renew(components) + this.addComponents(components) } /** @@ -51,14 +51,6 @@ export class Entity { return this } - /** - * Renew an Entity with new components - */ - renew(components: ComponentOf[] = []): Entity { - this.addComponents(components) - return this - } - /** * Clone an Entity */ diff --git a/src/EntityManager.ts b/src/EntityManager.ts index b36a8ee..cb64243 100644 --- a/src/EntityManager.ts +++ b/src/EntityManager.ts @@ -7,14 +7,20 @@ export class EntityManager { archetypes: Map> = new Map() entityChangeDisposers: Map void> = new Map() + /** + * Filter entities by component types + */ filter(types: string[]): Entity[] { return Array.from(this.entities.values()).filter((entity) => entity.hasComponents(...types) ) } + /** + * Add an entity to the manager + */ addEntity(entity: Entity) { - if (!this.entities.has(entity.id)) { + if (!this.hasEntity(entity.id)) { this.entities.set(entity.id, entity) // Add entity listener @@ -40,8 +46,11 @@ export class EntityManager { } } + /** + * Remove an entity from the manager + */ removeEntity(entity: Entity) { - if (this.entities.has(entity.id)) { + if (this.hasEntity(entity.id)) { this.entities.delete(entity.id) // clean up entity listener disposers @@ -54,81 +63,103 @@ export class EntityManager { for (const archetype of this.archetypes.values()) { archetype.handleEntityRemove(entity) } + entity.reset() } } - + /** + * Check if the manager has an entity + */ hasEntity(entityId: number): boolean { return this.entities.has(entityId) } + /** + * Get an entity from the manager + */ getEntity(entityId: number): Entity | undefined { return this.entities.get(entityId) } - renewEntity(components: ComponentOf[] = []): Entity { + /** + * Create a new entity and add it to the manager + */ + createEntity(components: ComponentOf[] = []): Entity { const entity = new Entity(components) this.addEntity(entity) - return entity - } - releaseEntity(entity: Entity) { - if (this.hasEntity(entity.id)) { - this.removeEntity(entity) - } + return entity } + /** + * Add an archetype to the manager + */ addArchetype>(klass: new (...args: any[]) => T) { const type = klass.name - if (!this.archetypes.has(type)) { - const archetype = new klass() - this.archetypes.set(type, archetype) - - // Add matching entities to archetypes - for (const entity of this.entities.values()) { - archetype.handleEntityAdd(entity) - } - - return archetype - } else { + if (this.archetypes.has(type)) { throw new Error( `EntityManager: Could not add archetype as '${type}' already exists.` ) } + + const archetype = new klass() + this.archetypes.set(type, archetype) + + // Add matching entities to archetypes + for (const entity of this.entities.values()) { + archetype.handleEntityAdd(entity) + } + + return archetype } + /** + * Remove an archetype from the manager + */ removeArchetype>( klass: new (...args: any[]) => T ): Archetype { const archetype = this.archetypes.get(klass.name) - if (archetype) { - this.archetypes.delete(klass.name) - return archetype - } else { + + if (!archetype) { throw new Error( `EntityManager: Could not delete archetype as '${klass.name}' does not exists.` ) } + + this.archetypes.delete(klass.name) + + return archetype } + /** + * Check if the manager has an archetype + */ hasArchetype>( klass: new (...args: any[]) => T ): boolean { return this.archetypes.has(klass.name) } + /** + * Get an archetype from the manager + */ getArchetype>(klass: new (...args: any[]) => T): T { const archetype = this.archetypes.get(klass.name) - if (archetype) { - return archetype as T - } else { + + if (!archetype) { throw new Error( `EntityManager: Could not get archetype as '${klass.name}' does not exists.` ) } + + return archetype as T } + /** + * Handle entity add component event + */ private handleEntityAddComponent( entity: Entity, component: ComponentOf @@ -140,6 +171,9 @@ export class EntityManager { } } + /** + * Handle entity remove component event + */ private handleEntityRemoveComponent( entity: Entity, component: ComponentOf diff --git a/src/EntityPool.ts b/src/EntityPool.ts index 5742711..869fcfc 100644 --- a/src/EntityPool.ts +++ b/src/EntityPool.ts @@ -49,7 +49,9 @@ export class EntityPool { } const entity = this.entities[this.head--] - return entity.renew(components) + entity.addComponents(components) + + return entity } /** @@ -62,6 +64,7 @@ export class EntityPool { ) } + entity.reset() this.entities[++this.head] = entity } } diff --git a/src/World.ts b/src/World.ts index 848f5ef..e01530b 100644 --- a/src/World.ts +++ b/src/World.ts @@ -10,11 +10,11 @@ interface WorldOptions {} * World registers systems, archetypes and entities. Updates systems. */ export class World { - manager: EntityManager = new EntityManager() + readonly manager: EntityManager = new EntityManager() private systems: Map> = new Map() - constructor({}: WorldOptions = {}) { + constructor(public readonly options: WorldOptions = {}) { this.manager = new EntityManager() } @@ -122,7 +122,7 @@ export class World { * Create a new entity */ createEntity(components: ComponentOf[] = []): Entity { - return this.manager.renewEntity(components) + return this.manager.createEntity(components) } /** @@ -132,7 +132,7 @@ export class World { const entity = this.getEntity(entityId) if (entity) { - this.manager.releaseEntity(entity) + this.manager.removeEntity(entity) } } diff --git a/tests/EntityManager.test.ts b/tests/EntityManager.test.ts index 5ce8cd5..ba15564 100644 --- a/tests/EntityManager.test.ts +++ b/tests/EntityManager.test.ts @@ -91,24 +91,24 @@ describe('EntityManager', () => { expect(physicalArchetype.hasEntity(entity)).toBe(false) }) - test('can renew and release entities from entity pool', () => { - const manager = new EntityManager() - const entity1 = manager.renewEntity() - const { id } = entity1 + // test('can renew and release entities from entity pool', () => { + // const manager = new EntityManager() + // const entity1 = manager.renewEntity() + // const { id } = entity1 - entity1.add(new PositionComponent({ x: 0, y: 0, z: 0 })) - expect(entity1).toBeInstanceOf(Entity) + // entity1.add(new PositionComponent({ x: 0, y: 0, z: 0 })) + // expect(entity1).toBeInstanceOf(Entity) - manager.releaseEntity(entity1) + // manager.releaseEntity(entity1) - expect( - entity1.componentTypes.length, - 'to have no components after release' - ).toBe(0) - expect(entity1.id, 'to be removed').not.toBe(id) + // expect( + // entity1.componentTypes.length, + // 'to have no components after release' + // ).toBe(0) + // expect(entity1.id, 'to be removed').not.toBe(id) - const entity2 = manager.renewEntity() + // const entity2 = manager.renewEntity() - expect(entity2, 'to be shared reference to entity1').toBe(entity1) - }) + // expect(entity2, 'to be shared reference to entity1').toBe(entity1) + // }) })