Skip to content

Commit

Permalink
Removing old renew / release code as it is not part of an entity pool…
Browse files Browse the repository at this point in the history
… any longer
  • Loading branch information
corbanbrook committed Apr 1, 2024
1 parent df1667f commit 75c15f2
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 67 deletions.
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
6 changes: 1 addition & 5 deletions src/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,4 @@ export abstract class Component<T> {
}
}

export interface ComponentTypes {
[key: string]: Component<any>
}

// export type ComponentTypes = {}
export type ComponentTypes = Record<string, Component<any>>
10 changes: 1 addition & 9 deletions src/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class Entity<C extends ComponentTypes> {

constructor(components: ComponentOf<C>[] = []) {
this.reset()
this.renew(components)
this.addComponents(components)
}

/**
Expand All @@ -51,14 +51,6 @@ export class Entity<C extends ComponentTypes> {
return this
}

/**
* Renew an Entity with new components
*/
renew(components: ComponentOf<C>[] = []): Entity<C> {
this.addComponents(components)
return this
}

/**
* Clone an Entity
*/
Expand Down
90 changes: 62 additions & 28 deletions src/EntityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ export class EntityManager<C extends ComponentTypes> {
archetypes: Map<string, Archetype<C>> = new Map()
entityChangeDisposers: Map<number, () => void> = new Map()

/**
* Filter entities by component types
*/
filter(types: string[]): Entity<C>[] {
return Array.from(this.entities.values()).filter((entity) =>
entity.hasComponents(...types)
)
}

/**
* Add an entity to the manager
*/
addEntity(entity: Entity<C>) {
if (!this.entities.has(entity.id)) {
if (!this.hasEntity(entity.id)) {
this.entities.set(entity.id, entity)

// Add entity listener
Expand All @@ -40,8 +46,11 @@ export class EntityManager<C extends ComponentTypes> {
}
}

/**
* Remove an entity from the manager
*/
removeEntity(entity: Entity<C>) {
if (this.entities.has(entity.id)) {
if (this.hasEntity(entity.id)) {
this.entities.delete(entity.id)

// clean up entity listener disposers
Expand All @@ -54,81 +63,103 @@ export class EntityManager<C extends ComponentTypes> {
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<C> | undefined {
return this.entities.get(entityId)
}

renewEntity(components: ComponentOf<C>[] = []): Entity<C> {
/**
* Create a new entity and add it to the manager
*/
createEntity(components: ComponentOf<C>[] = []): Entity<C> {
const entity = new Entity<C>(components)
this.addEntity(entity)
return entity
}

releaseEntity(entity: Entity<C>) {
if (this.hasEntity(entity.id)) {
this.removeEntity(entity)
}
return entity
}

/**
* Add an archetype to the manager
*/
addArchetype<T extends Archetype<C>>(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<T extends Archetype<C>>(
klass: new (...args: any[]) => T
): Archetype<C> {
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<T extends Archetype<C>>(
klass: new (...args: any[]) => T
): boolean {
return this.archetypes.has(klass.name)
}

/**
* Get an archetype from the manager
*/
getArchetype<T extends Archetype<C>>(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<C>,
component: ComponentOf<C>
Expand All @@ -140,6 +171,9 @@ export class EntityManager<C extends ComponentTypes> {
}
}

/**
* Handle entity remove component event
*/
private handleEntityRemoveComponent(
entity: Entity<C>,
component: ComponentOf<C>
Expand Down
5 changes: 4 additions & 1 deletion src/EntityPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ export class EntityPool<C extends ComponentTypes> {
}

const entity = this.entities[this.head--]
return entity.renew(components)
entity.addComponents(components)

return entity
}

/**
Expand All @@ -62,6 +64,7 @@ export class EntityPool<C extends ComponentTypes> {
)
}

entity.reset()
this.entities[++this.head] = entity
}
}
8 changes: 4 additions & 4 deletions src/World.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ interface WorldOptions {}
* World registers systems, archetypes and entities. Updates systems.
*/
export class World<C extends ComponentTypes> {
manager: EntityManager<C> = new EntityManager<C>()
readonly manager: EntityManager<C> = new EntityManager<C>()

private systems: Map<string, System<C>> = new Map()

constructor({}: WorldOptions = {}) {
constructor(public readonly options: WorldOptions = {}) {
this.manager = new EntityManager<C>()
}

Expand Down Expand Up @@ -122,7 +122,7 @@ export class World<C extends ComponentTypes> {
* Create a new entity
*/
createEntity(components: ComponentOf<C>[] = []): Entity<C> {
return this.manager.renewEntity(components)
return this.manager.createEntity(components)
}

/**
Expand All @@ -132,7 +132,7 @@ export class World<C extends ComponentTypes> {
const entity = this.getEntity(entityId)

if (entity) {
this.manager.releaseEntity(entity)
this.manager.removeEntity(entity)
}
}

Expand Down
30 changes: 15 additions & 15 deletions tests/EntityManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
// })
})

0 comments on commit 75c15f2

Please sign in to comment.