Skip to content

Commit

Permalink
Commenting functions in Entity and World
Browse files Browse the repository at this point in the history
  • Loading branch information
corbanbrook committed Mar 29, 2024
1 parent 1e0181d commit df1667f
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 92 deletions.
108 changes: 81 additions & 27 deletions src/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ const UNDEFINED_ID = -1

let instanceIdx = 0

/**
* Entity is a container for components.
*/
export class Entity<C extends ComponentTypes> {
id: number = UNDEFINED_ID
components: Partial<C> = {}

/** List of components */
get componentTypes(): (keyof C)[] {
return Object.keys(this.components)
}
Expand All @@ -35,13 +39,9 @@ export class Entity<C extends ComponentTypes> {
this.renew(components)
}

renew(components: ComponentOf<C>[] = []): Entity<C> {
for (const component of components) {
this.addComponent(component)
}
return this
}

/**
* Reset the entity to its initial state
*/
reset(): Entity<C> {
for (const type of this.componentTypes.reverse()) {
this.removeComponent(type)
Expand All @@ -51,47 +51,89 @@ 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
*/
clone(): Entity<C> {
return new Entity<C>(Object.values(this.components))
}

/**
* Attach an onChange listener to the entity
*/
onChange(listener: EntityChangeListener<C>) {
this.onChangeListeners.add(listener)
return () => this.onChangeListeners.delete(listener)
}

/**
* Remove an onChange listener from the entity
*/
removeOnChange(listener: EntityChangeListener<C>) {
if (this.onChangeListeners.has(listener)) {
this.onChangeListeners.delete(listener)
}
}

/**
* Check if the entity has a component
*/
hasComponent(type: keyof C): boolean {
return !!this.components[type]
}

// tslint:disable-next-line
has = this.hasComponent

/**
* Check if the entity has multiple components
*/
hasComponents = (...types: (keyof C)[]): boolean => {
return types.every((type) => this.hasComponent(type))
}

/**
* Add a component to the entity
*/
addComponent = (component: ComponentOf<C>) => {
if (!this.hasComponent(component.type)) {
this.components[component.type as keyof C] = component

component.onAttach(this)

for (const listener of this.onChangeListeners) {
listener({ type: 'add', entity: this, component })
}
} else {
if (this.hasComponent(component.type)) {
throw new Error(
`Entity already contains component of type ${component.type}.`
)
}

this.components[component.type as keyof C] = component

component.onAttach(this)

for (const listener of this.onChangeListeners) {
listener({ type: 'add', entity: this, component })
}
}

// tslint:disable-next-line
add = this.addComponent

/**
* Add multiple components to the entity
*/
addComponents = (components: ComponentOf<C>[]) => {
for (const component of components) {
this.addComponent(component)
}
}

/**
* Remove a component from the entity
*/
removeComponent = (type: keyof C) => {
if (this.hasComponent(type)) {
const component = this.components[type]!
Expand All @@ -109,13 +151,17 @@ export class Entity<C extends ComponentTypes> {
// tslint:disable-next-line
remove = this.removeComponent

/**
* Add or remove a component based on a predicated value
*/
toggleComponent(
componentClass: new (value: void) => ComponentOf<C>,
predicate: boolean
) {
const componentType = getComponentTypeFromClass(componentClass)

if (predicate) {
// Only add the component if it doesn't exist. Avoid throwing
if (!this.hasComponent(componentType)) {
this.addComponent(new componentClass())
}
Expand All @@ -127,39 +173,47 @@ export class Entity<C extends ComponentTypes> {
// tslint:disable-next-line
toggle = this.toggleComponent

// Get component instance
/**
* Get the component on the entity
*/
getComponent<T extends keyof C>(type: T): C[T] | undefined {
return this.components[type]
}

/**
* Get the value of the component on the entity
*/
getComponentValue<T extends keyof C>(type: T): C[T]['value'] {
if (this.hasComponent(type)) {
return this.components[type]!.value
} else {
if (!this.hasComponent(type)) {
throw new Error(
`Entity does not contain component of type ${String(type)}.`
)
}

return this.components[type]!.value
}

// tslint:disable-next-line
get = this.getComponentValue

/**
* Set the value of the component on the entity
*/
setComponentValue<T extends keyof C>(
type: T,
value: Partial<C[T]['value']> | C[T]['value']
) {
if (this.hasComponent(type)) {
if (typeof value === 'object' && !Array.isArray(value)) {
Object.assign(this.components[type]!.value, value)
} else {
this.components[type]!.value = value
}
} else {
if (!this.hasComponent(type)) {
throw new Error(
`Entity does not contain component of type ${String(type)}.`
)
}

if (typeof value === 'object' && !Array.isArray(value)) {
Object.assign(this.components[type]!.value, value)
} else {
this.components[type]!.value = value
}
}

// tslint:disable-next-line
Expand Down
85 changes: 63 additions & 22 deletions src/World.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,11 @@ import { Entity } from './Entity'
import { EntityManager } from './EntityManager'
import { System } from './System'

/*
World registers systems, archetypes and entities. Updates systems.
*/

interface WorldOptions {
// poolSize: number
}
interface WorldOptions {}

/**
* World registers systems, archetypes and entities. Updates systems.
*/
export class World<C extends ComponentTypes> {
manager: EntityManager<C> = new EntityManager<C>()

Expand All @@ -23,79 +18,116 @@ export class World<C extends ComponentTypes> {
this.manager = new EntityManager<C>()
}

/** List of systems */
get systemTypes(): string[] {
return Array.from(this.systems.keys())
}

/**
* Add a system to the world and initialize it
*/
addSystem(system: System<C>) {
const type = system.constructor.name
if (!this.systems.has(type)) {
this.systems.set(type, system)
system.init(this.manager)
} else {

if (this.systems.has(type)) {
throw new Error(
`World: Could not add system as '${type}' already exists.`
)
}

this.systems.set(type, system)
system.init(this.manager)
}

/**
* Add multiple systems to the world
*/
addSystems(...systems: System<C>[]) {
for (const system of systems) {
this.addSystem(system)
}
}

/**
* Remove a system from the world
*/
removeSystem<T extends System<C>>(
klass: new (...args: any[]) => T
): System<C> {
const system = this.systems.get(klass.name)
if (system) {
this.systems.delete(klass.name)
return system
} else {
const system = this.getSystem(klass)

if (!system) {
throw new Error(
`World: Could not delete system as '${klass.name}' does not exists.`
`World: Could not delete system as '${klass.name}' does not exist.`
)
}

this.systems.delete(klass.name)
return system
}

/**
* Check if a system exists in the world
*/
hasSystem<T extends System<C>>(klass: new (...args: any[]) => T): boolean {
return this.systems.has(klass.name)
}

/**
* Get a system from the world
*/
getSystem<T extends System<C>>(klass: new (...args: any[]) => T): T {
const system = this.systems.get(klass.name)
if (system) {
return system as T
} else {

if (!system) {
throw new Error(
`World: Could not get system as '${klass.name}' does not exists.`
)
}

return system as T
}

/**
* Add an archetype to the world
*/
addArchetype<T extends Archetype<C>>(klass: new (...args: any[]) => T) {
return this.manager.addArchetype(klass)
}

/**
* Remove an archetype from the world
*/
removeArchetype<T extends Archetype<C>>(klass: new (...args: any[]) => T) {
return this.manager.removeArchetype(klass)
}

/**
* Check if an archetype exists in the world
*/
hasArchetype<T extends Archetype<C>>(
klass: new (...args: any[]) => T
): boolean {
return this.manager.hasArchetype(klass)
}

/**
* Get an archetype from the world
*/
getArchetype<T extends Archetype<C>>(klass: new (...args: any[]) => T): T {
return this.manager.getArchetype(klass)
}

/**
* Create a new entity
*/
createEntity(components: ComponentOf<C>[] = []): Entity<C> {
return this.manager.renewEntity(components)
}

/**
* Remove an entity from the world
*/
removeEntity(entityId: number) {
const entity = this.getEntity(entityId)

Expand All @@ -104,14 +136,23 @@ export class World<C extends ComponentTypes> {
}
}

/**
* Get an entity from the world
*/
getEntity = (entityId: number): Entity<C> | undefined => {
return this.manager.getEntity(entityId)
}

/**
* Get multiple entities from the world
*/
getEntities(entityIds: number[]): (Entity<C> | undefined)[] {
return entityIds.map(this.getEntity)
}

/**
* Update all systems
*/
update(dt: number, time: number) {
for (const system of this.systems.values()) {
if (system.enabled) {
Expand Down
2 changes: 1 addition & 1 deletion tests/Component.fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, ComponentTypes } from '../src/Component'
import { Component } from '../src/Component'

export type Components = {
position: PositionComponent
Expand Down
Loading

0 comments on commit df1667f

Please sign in to comment.