@arancini/core
Advanced tools
Comparing version 3.1.1 to 3.2.0
import { Entity } from './entity'; | ||
export type ComponentClass<T extends Component | Component = Component> = { | ||
new (...args: unknown[]): T; | ||
componentIndex: number; | ||
type: typeof ComponentDefinitionType.CLASS; | ||
}; | ||
export declare const ComponentDefinitionType: { | ||
readonly CLASS: 0; | ||
readonly OBJECT: 1; | ||
readonly TAG: 2; | ||
readonly CLASS: 1; | ||
readonly OBJECT: 2; | ||
readonly TAG: 3; | ||
}; | ||
@@ -18,13 +13,13 @@ /** | ||
_arancini_id?: string; | ||
_arancini_entity?: Entity; | ||
}; | ||
export type ComponentValue = Component | unknown; | ||
export type ClassComponentDefinition<T extends ComponentValue = unknown> = { | ||
type: typeof ComponentDefinitionType.CLASS; | ||
export type ClassComponentDefinition<T = unknown> = { | ||
name?: string; | ||
componentIndex: number; | ||
new (...args: unknown[]): T; | ||
objectPooled: boolean; | ||
T?: T; | ||
} & { | ||
type: typeof ComponentDefinitionType.CLASS; | ||
new (): T; | ||
}; | ||
export type ObjectComponentDefinition<T extends ComponentValue = unknown> = { | ||
export type ObjectComponentDefinition<T = unknown> = { | ||
type: typeof ComponentDefinitionType.OBJECT; | ||
@@ -41,8 +36,8 @@ name?: string; | ||
}; | ||
export type ComponentDefinition<T extends ComponentValue = unknown> = ClassComponentDefinition<T> | ObjectComponentDefinition<T> | TagComponentDefinition; | ||
export type ComponentDefinitionInstance<T extends ComponentDefinition<unknown>> = T['type'] extends typeof ComponentDefinitionType.CLASS ? T extends { | ||
new (...args: unknown[]): infer U; | ||
export type ComponentDefinition<T = unknown> = ClassComponentDefinition<T> | ObjectComponentDefinition<T> | TagComponentDefinition; | ||
export type ComponentInstance<T extends ComponentDefinition<unknown>> = T['type'] extends typeof ComponentDefinitionType.CLASS ? T extends { | ||
new (...args: any[]): infer U; | ||
} ? U : never : T['type'] extends typeof ComponentDefinitionType.OBJECT ? T['T'] : T['type'] extends typeof ComponentDefinitionType.TAG ? unknown : never; | ||
export type ComponentDefinitionArgs<T extends ComponentDefinition<unknown>> = T['type'] extends typeof ComponentDefinitionType.CLASS ? T extends { | ||
new (...args: unknown[]): infer U; | ||
new (args: any[]): infer U; | ||
} ? U extends { | ||
@@ -52,44 +47,33 @@ construct(...args: infer Args): void; | ||
/** | ||
* There are multiple ways to define a component in Arancini. | ||
* Decorator for opting ia class component into being object objectPooled. | ||
* | ||
* You can define: | ||
* - an object component with the `Component.object` method | ||
* - a tag component with the `Component.tag` method | ||
* - a class component by extending the `Component` class | ||
* | ||
* To get the most out of arancini, you should use class components where possible. | ||
* Class components let you utilise arancini's object pooling and lifecycle features. | ||
* | ||
* @example defining an object component | ||
* @example defining an object pooled component using the @objectPool decorator | ||
* ```ts | ||
* import { Component, World } from '@arancini/core' | ||
* import { Component, objectPooled, World } from '@arancini/core' | ||
* | ||
* const PositionComponent = Component.object<{ x: number, y: number }>('Position') | ||
* | ||
* const world = new World() | ||
* world.registerComponent(PositionComponent) | ||
* | ||
* const entity = world.create() | ||
* | ||
* entity.add(PositionComponent, { x: 1, y: 2 }) | ||
* @objectPooled() | ||
* class ExampleComponent extends Component {} | ||
* ``` | ||
* | ||
* @example defining a tag component | ||
* ```ts | ||
* import { Component, World } from '@arancini/core' | ||
* This can also be achieved by setting the `objectPooled` property on a component class | ||
* @example vanilla js | ||
* ```js | ||
* import { Component } from '@arancini/core' | ||
* | ||
* const PoweredUpComponent = Component.tag('PoweredUp') | ||
* | ||
* const world = new World() | ||
* world.registerComponent(PoweredUpComponent) | ||
* | ||
* const entity = world.create() | ||
* | ||
* entity.add(PoweredUpComponent) | ||
* class ExampleComponent extends Component {} | ||
* ExampleComponent.objectPooled = true | ||
* ``` | ||
*/ | ||
export declare const objectPooled: () => (target: { | ||
new (): Component; | ||
objectPooled: boolean; | ||
}, _v?: any) => void; | ||
/** | ||
* The base class for class components. | ||
* | ||
* @example defining and creating a class component that extends the `Component` class | ||
* * @example defining and creating a class component that extends the `Component` class and uses the `@objectPooled` decorator | ||
* ```ts | ||
* import { Component, World } from '@arancini/core' | ||
* import { Component, objectPooled, World } from '@arancini/core' | ||
* | ||
* @objectPooled() | ||
* class ExampleComponent extends Component { | ||
@@ -132,81 +116,60 @@ * // When using typescript, the `!:` not null assertion can be used as a "late-init" syntax. | ||
*/ | ||
export declare abstract class Component { | ||
export declare class Component { | ||
construct(..._args: any[]): void; | ||
onInit(): void; | ||
onDestroy(): void; | ||
static componentIndex: number; | ||
static type: 1; | ||
static objectPooled: boolean; | ||
entity: Entity; | ||
/** | ||
* This component instances unique id | ||
* @private internal | ||
* @ignore | ||
*/ | ||
_arancini_id: string; | ||
/** | ||
* The entity this component belongs to. | ||
* @private internal | ||
* @ignore | ||
*/ | ||
_arancini_entity: Entity; | ||
/** | ||
* The class the component was constructed from | ||
* @private internal | ||
*/ | ||
_arancini_component_definition: ComponentDefinition<unknown>; | ||
static componentIndex: number; | ||
static type: 0; | ||
/** | ||
* Properties can be be initialised with arguments with the `construct` method. | ||
* Creates an object component definition with the given type. | ||
* | ||
* Component instances are object pooled. To prevent unexpected behavior properties should be initialised or reset in the `construct` method. | ||
* @param name a name for the component | ||
* @return object component definition | ||
* | ||
* @example | ||
* * @example defining an object component | ||
* ```ts | ||
* class MyComponent extends Component { | ||
* exampleNumber!: number; | ||
* import { Component, World } from '@arancini/core' | ||
* | ||
* exampleMap = new Map(); | ||
* const PositionComponent = Component.object<{ x: number, y: number }>('Position') | ||
* | ||
* construct(): void { | ||
* this.exampleNumber = 0; | ||
* const world = new World() | ||
* world.registerComponent(PositionComponent) | ||
* | ||
* this.exampleMap.clear(); | ||
* } | ||
* const entity = world.create() | ||
* | ||
* onInit(): void { | ||
* // because we used the not-null operator `!:` the type of `this.exampleProperty` here will be `number`, as opposed to `number | undefined` | ||
* this.exampleProperty += 1; | ||
* } | ||
* } | ||
* entity.add(PositionComponent, { x: 1, y: 2 }) | ||
* ``` | ||
*/ | ||
construct(..._args: any[] | []): void; | ||
static object<T>(name: string): ObjectComponentDefinition<T>; | ||
/** | ||
* Destruction logic | ||
*/ | ||
onDestroy(): void; | ||
/** | ||
* Initialisation logic | ||
*/ | ||
onInit(): void; | ||
/** | ||
* Creates an object component definition with the given type. | ||
* | ||
* @param name an optional name for the component, useful for debugging | ||
* @return object component definition | ||
* | ||
* @example | ||
* ```ts | ||
* import { object } from '@arancini/core' | ||
* | ||
* const PositionComponent = object<{ x: number, y: number }>('Position') | ||
* ``` | ||
*/ | ||
static object<T extends object>(name?: string): ObjectComponentDefinition<T>; | ||
/** | ||
* Creates a tag component definition. | ||
* @param name an optional name for the component, useful for debugging | ||
* @param name an name for the component | ||
* @returns tag component definition | ||
* | ||
* @example | ||
* @example defining a tag component | ||
* ```ts | ||
* import { tag } from '@arancini/core' | ||
* import { Component, World } from '@arancini/core' | ||
* | ||
* const PoweredUpComponent = tag('PoweredUp') | ||
* const PoweredUpComponent = Component.tag('PoweredUp') | ||
* | ||
* const world = new World() | ||
* world.registerComponent(PoweredUpComponent) | ||
* | ||
* const entity = world.create() | ||
* | ||
* entity.add(PoweredUpComponent) | ||
* ``` | ||
*/ | ||
static tag(name?: string): TagComponentDefinition; | ||
static tag(name: string): TagComponentDefinition; | ||
} | ||
export declare const cloneComponentDefinition: <T extends ComponentDefinition<unknown>>(componentDefinition: T) => T; |
@@ -1,2 +0,2 @@ | ||
import type { ComponentDefinition, ComponentDefinitionArgs, ComponentDefinitionInstance } from './component'; | ||
import { type ComponentDefinition, type ComponentDefinitionArgs, type ComponentInstance } from './component'; | ||
import { BitSet } from './utils/bit-set'; | ||
@@ -15,7 +15,7 @@ import type { World } from './world'; | ||
* // example tag component without any data or behavior | ||
* class ExampleComponent extends Component {} | ||
* const TagComponent = Component.tag('TagComponent') | ||
* | ||
* // create a world and register the component | ||
* const world = new World() | ||
* world.registerComponent(ExampleComponent) | ||
* world.registerComponent(TagComponent) | ||
* | ||
@@ -26,14 +26,14 @@ * // create an entitty | ||
* // try retrieving a component that isn't in the entity | ||
* entity.find(ExampleComponent) // returns `undefined` | ||
* entity.get(ExampleComponent) // throws Error | ||
* entity.find(TagComponent) // returns `undefined` | ||
* entity.get(TagComponent) // throws Error | ||
* | ||
* // add ExampleComponent to the entity | ||
* const exampleComponent = entity.add(ExampleComponent) | ||
* // add TagComponent to the entity | ||
* const tagComponent = entity.add(TagComponent) | ||
* | ||
* entity.has(ExampleComponent) // returns `true` | ||
* entity.get(ExampleComponent) // returns `exampleComponent` | ||
* entity.get(ExampleComponent) // returns `exampleComponent` | ||
* entity.has(TagComponent) // returns `true` | ||
* entity.get(TagComponent) // returns `tagComponent` | ||
* entity.get(TagComponent) // returns `tagComponent` | ||
* | ||
* // remove the component | ||
* entity.remove(ExampleComponent); | ||
* entity.remove(TagComponent); | ||
* | ||
@@ -70,12 +70,14 @@ * // destroy the entity | ||
/** | ||
* Whether to update queries when components are added or removed | ||
* Used by the `bulk` method to control when queries are updated | ||
* @private | ||
* @private internal | ||
*/ | ||
_updateQueries: boolean; | ||
/** | ||
* @private internal | ||
*/ | ||
_updateBitSet: boolean; | ||
/** | ||
* Adds a component to the entity | ||
* @param componentDefinition the component to add | ||
*/ | ||
add<C extends ComponentDefinition<unknown>>(componentDefinition: C, ...args: ComponentDefinitionArgs<C>): ComponentDefinitionInstance<C>; | ||
add<C extends ComponentDefinition<unknown>>(componentDefinition: C, ...args: ComponentDefinitionArgs<C>): ComponentInstance<C>; | ||
/** | ||
@@ -85,7 +87,7 @@ * Removes a component from the entity and destroys it | ||
*/ | ||
remove(component: ComponentDefinition<unknown>): Entity; | ||
remove<T extends ComponentDefinition<unknown>>(componentDefinition: T): Entity; | ||
/** | ||
* Utility method for adding and removing components in bulk. | ||
* | ||
* Wrap multiple `add` and `remove` calls in `entity.bulk(() => { ... })` to prevent queries from updating until all components have been added or removed. | ||
* Wrap multiple `add` and `remove` calls in `entity.bulk(() => { ... })` to update queries once after adding or removing multiple components. | ||
* | ||
@@ -96,3 +98,3 @@ * @param updateFn callback to update the Entity | ||
* ```ts | ||
* world.create().bulk((entity) => { | ||
* entity.bulk((entity) => { | ||
* entity.add(TestComponentOne) | ||
@@ -107,6 +109,6 @@ * entity.add(TestComponentTwo) | ||
* Retrieves a component on an entity by type, throws an error if the component is not in the entity | ||
* @param value a constructor for the component type to retrieve | ||
* @param componentDefinition the component to to get | ||
* @returns the component | ||
*/ | ||
get<T extends ComponentDefinition<unknown>>(value: T): ComponentDefinitionInstance<T>; | ||
get<T extends ComponentDefinition<unknown>>(componentDefinition: T): ComponentInstance<T>; | ||
/** | ||
@@ -122,3 +124,3 @@ * Returns all components for the entity | ||
*/ | ||
find<T extends ComponentDefinition<unknown>>(value: T): ComponentDefinitionInstance<T> | undefined; | ||
find<T extends ComponentDefinition<unknown>>(value: T): ComponentInstance<T> | undefined; | ||
/** | ||
@@ -131,5 +133,5 @@ * Returns whether the entity contains the given component | ||
/** | ||
* Destroy the Entity's components and remove the Entity from the world | ||
* Destroys the Entity | ||
*/ | ||
destroy(): void; | ||
} |
export * from './component'; | ||
export * from './entity'; | ||
export * from './events'; | ||
export * from './pools/object-pool'; | ||
export { ObjectPool } from './pools'; | ||
export * from './query'; | ||
export * from './system'; | ||
export * from './topic'; | ||
export * from './world'; |
@@ -1,2 +0,2 @@ | ||
const t={CLASS:0,OBJECT:1,TAG:2};class e{_arancini_id;_arancini_entity;_arancini_component_definition;static componentIndex;static type=t.CLASS;construct(...t){}onDestroy(){}onInit(){}static object(e){return{name:e,type:t.OBJECT,componentIndex:-1,T:void 0}}static tag(e){return{name:e,type:t.TAG,componentIndex:-1,T:void 0}}}class n{words;constructor(t=[]){this.words=new Uint32Array(8);for(const e of t)this.add(e)}add(t){this.resize(t),this.words[t>>>5]|=1<<t}remove(t){this.resize(t),this.words[t>>>5]&=~(1<<t)}has(t){return 0!=(this.words[t>>>5]&1<<t)}resize(t){if(this.words.length<<5>t)return;const e=new Uint32Array(t+32>>>5<<1);e.set(this.words),this.words=e}reset(){for(let t=0;t<this.words.length;t++)this.words[t]=0}copy(t){const e=new Uint32Array(t.words.length);e.set(t.words),this.words=e}clone(){const t=new Uint32Array(this.words.length);t.set(this.words);const e=new n;return e.words=t,e}containsAll(t){for(let e=0;e<this.words.length;e++)if(0!=(~this.words[e]&t.words[e]))return!1;return!0}containsAny(t){for(let e=0;e<this.words.length;e++)if(0!=(this.words[e]&t.words[e]))return!0;return!1}}let s=0;const i=()=>(s++,s.toString());class o{id=i();initialised=!1;world;_componentsBitSet=new n;_components={};_updateQueries=!0;add(t,...e){const n=this.world.entityManager.addComponentToEntity(this,t,e);return this._updateQueries&&this.world.queryManager.onEntityComponentChange(this),n}remove(t){return this.world.entityManager.removeComponentFromEntity(this,t,!0),this._updateQueries&&this.world.queryManager.onEntityComponentChange(this),this}bulk(t){return this._updateQueries=!1,t(this),this._updateQueries=!0,this.world.queryManager.onEntityComponentChange(this),this}get(t){const e=this._components[t.componentIndex];if(e)return e;throw new Error(`Component ${t}} with componentIndex ${t.componentIndex} not in entity ${this.id} - ${Object.keys(this._components)}`)}getAll(){return Object.values(this._components)}find(t){return this._components[t.componentIndex]}has(t){return!!this._components[t.componentIndex]}destroy(){this.world&&this.world.entityManager.destroyEntity(this)}}class r{listeners=new Set;add(t){return this.listeners.add(t),()=>this.remove(t)}remove(t){this.listeners.delete(t)}emit(...t){for(const e of this.listeners)e(...t)}clear(){this.listeners.clear()}}class a{availableObjects=[];factory;get available(){return this.availableObjects.length}get used(){return this.size-this.availableObjects.length}size=0;constructor(t,e){this.factory=t,void 0!==e&&this.grow(e)}grow(t){for(let e=0;e<t;e++)this.availableObjects.push(this.factory());this.size+=t}free(t){for(let e=0;e<t;e++){if(!this.availableObjects.pop())break;this.size--}}request(){return this.availableObjects.length<=0&&this.grow(Math.round(.2*this.size)+1),this.availableObjects.pop()}recycle(t){this.availableObjects.push(t)}}const d={ALL:"all",ANY:"any",NOT:"not"};class c{key;entities=[];get first(){return this.entities[0]||void 0}onEntityAdded=new r;onEntityRemoved=new r;[Symbol.iterator](){let t=this.entities.length;const e={value:void 0,done:!1};return{next:()=>(e.value=this.entities[--t],e.done=t<0,e)}}world;constructor(t,e){this.world=t,this.key=e}destroy(){this.world.queryManager.removeQuery(this)}static getDescriptionDedupeString(t){return Array.isArray(t)?t.map((t=>`${t.componentIndex}`)).join("&"):Object.entries(t).flatMap((([t,e])=>t===d.ALL?e.map((t=>`${t.componentIndex}`)).sort():[`${t}:${e.sort().map((t=>t.componentIndex))}`])).sort().join("&")}}class l{enabled=!0;world;__internal={class:null,queries:new Set,priority:0,order:0,requiredQueries:[]};constructor(t){this.world=t}onDestroy(){}onInit(){}onUpdate(t,e){}destroy(){this.world.unregisterSystem(this.__internal.class)}query(t,e){return this.world.systemManager.createSystemQuery(this,t,e)}singleton(t,e){return{__internal:{placeholder:!0,componentDefinition:t,options:e}}}}class h{components=new Map;currentComponentIndex=-1;world;constructor(t){this.world=t}registerComponent(t){let e=this.components.get(t);if(void 0!==e)return e;if(this.currentComponentIndex++,e=this.currentComponentIndex,t.componentIndex=e,this.components.set(t,e),this.world.initialised)for(const t of this.world.entities.values())t._componentsBitSet.resize(this.currentComponentIndex);return e}}class u{get totalPools(){return this.objectPools.size}get size(){let t=0;for(const e of this.objectPools.values())t+=e.size;return t}get available(){let t=0;for(const e of this.objectPools.values())t+=e.available;return t}get used(){let t=0;for(const e of this.objectPools.values())t+=e.used;return t}objectPools=new Map;request(t){return this.getPool(t).request()}recycle(t){const e=this.objectPools.get(t._arancini_component_definition.componentIndex);e&&e.recycle(t)}grow(t,e){this.getPool(t).grow(e)}free(t,e){this.getPool(t).free(e)}getPool(t){let e=this.objectPools.get(t.componentIndex);return void 0===e&&(e=new a((()=>new t)),this.objectPools.set(t.componentIndex,e)),e}}class y{objectPool=new a((()=>{const t=new o;return t.world=this.world,t}));get size(){return this.objectPool.size}get available(){return this.objectPool.available}get used(){return this.objectPool.used}world;constructor(t){this.world=t}request(){return this.objectPool.request()}recycle(t){t.id=i(),t.initialised=!1,t._componentsBitSet.reset(),this.objectPool.recycle(t)}grow(t){this.objectPool.grow(t)}free(t){this.objectPool.free(t)}}class m{componentPool;entityPool;world;constructor(t){this.world=t,this.componentPool=new u,this.entityPool=new y(this.world)}init(){for(const t of this.world.entities.values())this.initialiseEntity(t)}destroy(){for(const t of this.world.entities.values())this.destroyEntity(t)}createEntity(){const t=this.entityPool.request();return this.world.entities.set(t.id,t),this.world.initialised&&this.initialiseEntity(t),t}destroyEntity(t){this.world.entities.delete(t.id);for(const e of Object.values(t._components)){const n=e;this.removeComponentFromEntity(t,n._arancini_component_definition,!1)}this.world.queryManager.onEntityRemoved(t),this.entityPool.recycle(t)}addComponentToEntity(n,s,o){if(n._components[s.componentIndex])throw new Error(`Cannot add component ${s.name}, entity with id ${n.id} already has this component`);let r;if(s.type===t.CLASS){const t=this.componentPool.request(s);t.construct(...o??[]),r=t}else r=s.type===t.OBJECT?o[0]:{};const a=r;return a._arancini_entity=n,a._arancini_id=i(),a._arancini_component_definition=s,n._components[s.componentIndex]=r,n._componentsBitSet.add(s.componentIndex),n.initialised&&r instanceof e&&r.onInit(),r}removeComponentFromEntity(e,n,s){const o=e.find(n);if(void 0===o)throw new Error("Component does not exist in Entity");const r=o,{componentIndex:a}=r._arancini_component_definition,d=r._arancini_component_definition.type===t.CLASS;delete e._components[a],s&&e._componentsBitSet.remove(a),d?(o?.onDestroy(),r._arancini_id=i(),r._arancini_entity=void 0,this.componentPool.recycle(o)):(delete r._arancini_entity,delete r._arancini_component_definition,delete r._arancini_id)}initialiseEntity(t){t.initialised=!0,t._componentsBitSet.resize(this.world.componentRegistry.currentComponentIndex);for(const n of Object.values(t._components))n instanceof e&&n.onInit()}}class p{dedupedQueries=new Map;world;constructor(t){this.world=t}createQuery(t){const e=c.getDescriptionDedupeString(t);let n=this.dedupedQueries.get(e);if(void 0===n){const s=Array.isArray(t);if(s&&0===t.length||!s&&(!t.all&&!t.any&&!t.not||t.all&&0===t.all.length||t.any&&0===t.any.length||t.not&&0===t.not.length))throw new Error("Query must have at least one condition");n={dedupeString:e,instances:new Set,description:t,bitSets:this.getQueryBitSets(t),entities:[],entitySet:new Set};const i=this.getQueryResults(n.bitSets);for(const t of i)n.entities.push(t),n.entitySet.add(t);this.dedupedQueries.set(e,n)}const s=new c(this.world,e);return s.entities=n.entities,n.instances.add(s),s}hasQuery(t){const e=c.getDescriptionDedupeString(t);return this.dedupedQueries.has(e)}onEntityComponentChange(t){for(const e of this.dedupedQueries.values()){const n=e.entitySet.has(t),s=this.matchesQueryConditions(e.bitSets,t);if(s&&!n){e.entities.push(t),e.entitySet.add(t);for(const n of e.instances)n.onEntityAdded.emit(t)}else if(!s&&n){const n=e.entities.indexOf(t,0);-1!==n&&e.entities.splice(n,1),e.entitySet.delete(t);for(const n of e.instances)n.onEntityRemoved.emit(t)}}}onEntityRemoved(t){for(const e of this.dedupedQueries.values()){const n=e.entities.indexOf(t,0);-1!==n&&e.entities.splice(n,1),e.entitySet.delete(t);for(const n of e.instances)n.onEntityRemoved.emit(t)}}find(t){const e=c.getDescriptionDedupeString(t),n=this.dedupedQueries.get(e);return n?n.entities:this.getQueryResults(this.getQueryBitSets(t))}removeQuery(t){const e=this.dedupedQueries.get(t.key);void 0!==e&&e.instances.has(t)&&(e.instances.delete(t),t.onEntityAdded.clear(),t.onEntityRemoved.clear(),0===e.instances.size&&this.dedupedQueries.delete(e.dedupeString))}matchesQueryConditions(t,e){return!(t.all&&!e._componentsBitSet.containsAll(t.all))&&(!(t.any&&!e._componentsBitSet.containsAny(t.any))&&(!t.not||!e._componentsBitSet.containsAny(t.not)))}getQueryResults(t){const e=[];for(const n of this.world.entities.values())this.matchesQueryConditions(t,n)&&e.push(n);return e}getQueryBitSets(t){const{all:e,any:s,not:i}=Array.isArray(t)?{all:t,any:void 0,not:void 0}:t,o={};return o.all=e?new n(e.map((t=>t.componentIndex))):void 0,o.any=s?new n(s.map((t=>t.componentIndex))):void 0,o.not=i?new n(i.map((t=>t.componentIndex))):void 0,o}}class g{systems=new Map;sortedSystems=[];systemCounter=0;world;constructor(t){this.world=t}init(){for(const t of this.systems.values())t.onInit();this.sortSystems()}update(t,e){for(const n of this.sortedSystems)n.enabled&&(n.__internal.requiredQueries.length>0&&n.__internal.requiredQueries.some((t=>0===t.entities.length))||n.onUpdate(t,e))}destroy(){for(const t of this.systems.values())t.onDestroy()}registerSystem(t,e){if(this.systems.has(t))throw new Error(`System "${t.name}" has already been registered`);this.systemCounter++;const n=new t(this.world);this.systems.set(t,n),n.__internal.class=t,n.__internal.priority=e?.priority??0,n.__internal.order=this.systemCounter;for(const t in n){const e=n,s=e[t];if(s?.__internal?.placeholder){const{__internal:{componentDefinition:i,options:o}}=s,r=this.createSystemQuery(n,[i],o),a=()=>{e[t]=r.first?.get(i)};r.onEntityAdded.add(a),r.onEntityRemoved.add(a),a()}}const s=(i=t,o="onUpdate",Object.getOwnPropertyNames(i.prototype).includes(o));var i,o;s&&this.sortedSystems.push(n),this.world.initialised&&(n.onInit(),s&&this.sortSystems())}unregisterSystem(t){const e=this.systems.get(t);e&&(this.systems.delete(t),this.sortedSystems=this.sortedSystems.filter((e=>e.__internal.class!==t)),e.__internal.queries.forEach((t=>{this.world.queryManager.removeQuery(t)})),e.__internal.requiredQueries=[],e.onDestroy())}createSystemQuery(t,e,n){const s=this.world.queryManager.createQuery(e);return t.__internal.queries.add(s),n?.required&&t.__internal.requiredQueries.push(s),s}sortSystems(){this.sortedSystems.sort(((t,e)=>e.__internal.priority-t.__internal.priority||t.__internal.order-e.__internal.order))}}class w{initialised=!1;time=0;entityManager;queryManager;systemManager;componentRegistry;entities=new Map;constructor(){this.componentRegistry=new h(this),this.entityManager=new m(this),this.queryManager=new p(this),this.systemManager=new g(this)}init(){this.initialised=!0,this.entityManager.init(),this.systemManager.init()}update(t=0){this.time+=t,this.systemManager.update(t,this.time)}destroy(){this.time=0,this.initialised=!1,this.systemManager.destroy(),this.entityManager.destroy()}create(t){const e=this.entityManager.createEntity();return t&&e.bulk(t),e}query(t){return this.queryManager.createQuery(t)}find(t){return this.queryManager.find(t)}registerComponent(t){return this.componentRegistry.registerComponent(t),this}registerSystem(t,e){return this.systemManager.registerSystem(t,e),this}unregisterSystem(t){return this.systemManager.unregisterSystem(t),this}getSystem(t){return this.systemManager.systems.get(t)}getSystems(){return Array.from(this.systemManager.systems.values())}}export{e as Component,t as ComponentDefinitionType,o as Entity,a as ObjectPool,c as Query,d as QueryConditionType,l as System,r as Topic,w as World}; | ||
const t={CLASS:1,OBJECT:2,TAG:3},e=()=>(t,e)=>{t.objectPooled=!0};class s{construct(...t){}onInit(){}onDestroy(){}static componentIndex;static type=t.CLASS;static objectPooled=!1;entity;_arancini_id;_arancini_component_definition;static object(e){return{name:e,type:t.OBJECT,componentIndex:-1,T:void 0}}static tag(e){return{name:e,type:t.TAG,componentIndex:-1,T:void 0}}}const n=e=>{if(e.type===t.CLASS){const t=class extends e{};return t.componentIndex=-1,t}const s={...e};return s.componentIndex=-1,s};class i{words;constructor(t=[]){this.words=new Uint32Array(8);for(const e of t)this.add(e)}add(t){this.resize(t),this.words[t>>>5]|=1<<t}remove(t){this.resize(t),this.words[t>>>5]&=~(1<<t)}has(t){return 0!=(this.words[t>>>5]&1<<t)}resize(t){if(this.words.length<<5>t)return;const e=new Uint32Array(t+32>>>5<<1);e.set(this.words),this.words=e}reset(){for(let t=0;t<this.words.length;t++)this.words[t]=0}copy(t){const e=new Uint32Array(t.words.length);e.set(t.words),this.words=e}clone(){const t=new Uint32Array(this.words.length);t.set(this.words);const e=new i;return e.words=t,e}containsAll(t){for(let e=0;e<this.words.length;e++)if(0!=(~this.words[e]&t.words[e]))return!1;return!0}containsAny(t){for(let e=0;e<this.words.length;e++)if(0!=(this.words[e]&t.words[e]))return!0;return!1}}let o=0;const r=()=>(o++,o.toString());class a{id=r();initialised=!1;world;_componentsBitSet=new i;_components={};_updateQueries=!0;_updateBitSet=!0;add(e,...s){if(this._components[e.componentIndex])throw new Error(`Cannot add component ${e.name}, entity with id ${this.id} already has this component`);let n;e.type===t.CLASS?(n=e.objectPooled?this.world.componentPool.request(e):new e,n.construct(...s)):n=e.type===t.OBJECT?s[0]:{};const i=n;return i._arancini_id=r(),i._arancini_component_definition=e,this._components[e.componentIndex]=n,this._componentsBitSet.add(e.componentIndex),e.type===t.CLASS&&(n.entity=this,this.initialised&&n.onInit()),this._updateQueries&&this.world.queryManager.onEntityComponentChange(this),n}remove(e){const s=this.find(e);if(void 0===s)throw new Error("Component does not exist in Entity");const n=s,{componentIndex:i}=n._arancini_component_definition;if(delete this._components[i],this._updateBitSet&&this._componentsBitSet.remove(i),n._arancini_component_definition.type===t.CLASS){const t=s;t.onDestroy(),t.entity=void 0,n._arancini_component_definition.objectPooled&&(n._arancini_id=r(),this.world.componentPool.recycle(s))}else delete n._arancini_component_definition,delete n._arancini_id;return this._updateQueries&&this.world.queryManager.onEntityComponentChange(this),this}bulk(t){return this._updateQueries=!1,t(this),this._updateQueries=!0,this.world.queryManager.onEntityComponentChange(this),this}get(t){const e=this._components[t.componentIndex];if(!e)throw new Error(`Component ${t}} with componentIndex ${t.componentIndex} not in entity ${this.id} - ${Object.keys(this._components)}`);return e}getAll(){return Object.values(this._components)}find(t){return this._components[t.componentIndex]}has(t){return!!this._components[t.componentIndex]}destroy(){this.world?.destroy(this)}}class h{availableObjects=[];factory;get available(){return this.availableObjects.length}get used(){return this.size-this.availableObjects.length}size=0;constructor(t,e){this.factory=t,void 0!==e&&this.grow(e)}grow(t){for(let e=0;e<t;e++)this.availableObjects.push(this.factory());this.size+=t}free(t){for(let e=0;e<t;e++){if(!this.availableObjects.pop())break;this.size--}}request(){return this.availableObjects.length<=0&&this.grow(Math.round(.2*this.size)+1),this.availableObjects.pop()}recycle(t){this.availableObjects.push(t)}}class l{objectPool=new h((()=>{const t=new a;return t.world=this.world,t}));get size(){return this.objectPool.size}get available(){return this.objectPool.available}get used(){return this.objectPool.used}world;constructor(t){this.world=t}request(){return this.objectPool.request()}recycle(t){t.id=r(),t.initialised=!1,t._componentsBitSet.reset(),this.objectPool.recycle(t)}grow(t){this.objectPool.grow(t)}free(t){this.objectPool.free(t)}}class c{get totalPools(){return this.objectPools.size}get size(){let t=0;for(const e of this.objectPools.values())t+=e.size;return t}get available(){let t=0;for(const e of this.objectPools.values())t+=e.available;return t}get used(){let t=0;for(const e of this.objectPools.values())t+=e.used;return t}objectPools=new Map;request(t){return this.getPool(t).request()}recycle(t){this.objectPools.get(t._arancini_component_definition.componentIndex)?.recycle(t)}grow(t,e){this.getPool(t).grow(e)}free(t,e){this.getPool(t).free(e)}getPool(t){let e=this.objectPools.get(t.componentIndex);return void 0===e&&(e=new h((()=>new t)),this.objectPools.set(t.componentIndex,e)),e}}class d{listeners=new Set;add(t){return this.listeners.add(t),()=>this.remove(t)}remove(t){this.listeners.delete(t)}emit(...t){for(const e of this.listeners)e(...t)}clear(){this.listeners.clear()}}class y{version=0;entities=[];onEntityAdded=new d;onEntityRemoved=new d;entityPositions=new Map;get first(){return this.entities[0]||void 0}[Symbol.iterator](){let t=this.entities.length;const e={value:void 0,done:!1};return{next:()=>(e.value=this.entities[--t],e.done=t<0,e)}}has(t){return this.entityPositions.has(t)}_addEntity(t){t&&!this.has(t)&&(this.entities.push(t),this.entityPositions.set(t,this.entities.length-1),this.version++,this.onEntityAdded.emit(t))}_removeEntity(t){if(!this.has(t))return;const e=this.entityPositions.get(t);this.entityPositions.delete(t);const s=this.entities[this.entities.length-1];s!==t&&(this.entities[e]=s,this.entityPositions.set(s,e)),this.entities.pop(),this.version++,this.onEntityRemoved.emit(t)}}class u extends y{world;key;description;bitSets;constructor(t,e,s,n){super(),this.world=t,this.key=e,this.description=s,this.bitSets=n}destroy(){this.world.queryManager.removeQuery(this)}}const m=t=>Array.isArray(t)?t.map((t=>`${t.componentIndex}`)).join("&"):Object.entries(t).flatMap((([t,e])=>"all"===t?e.map((t=>`${t.componentIndex}`)).sort():[`${t}:${e.sort().map((t=>t.componentIndex))}`])).sort().join("&");class p{queries=new Map;queryOwners=new Map;world;constructor(t){this.world=t}createQuery(t,e="standalone"){const s=m(t);let n=this.queries.get(s);if(void 0===n){const e=Array.isArray(t);if(e&&0===t.length||!e&&(!t.all&&!t.any&&!t.not||t.all&&0===t.all.length||t.any&&0===t.any.length||t.not&&0===t.not.length))throw new Error("Query must have at least one condition");n=new u(this.world,s,t,this.getQueryBitSets(t));const i=this.getQueryResults(n.bitSets);for(const t of i)n._addEntity(t);this.queries.set(s,n)}if(e){const t=this.queryOwners.get(s)??[];t.push(e),this.queryOwners.set(s,t)}return n}removeQuery(t,e="standalone"){if(!this.queries.has(t.key))return;let s=this.queryOwners.get(t.key)??[];s=s.filter((t=>t!==e)),s.length>0?this.queryOwners.set(t.key,s):(this.queries.delete(t.key),this.queryOwners.delete(t.key),t.onEntityAdded.clear(),t.onEntityRemoved.clear())}hasQuery(t){const e=m(t);return this.queries.has(e)}onEntityComponentChange(t){for(const e of this.queries.values()){const s=this.matchesQueryConditions(e.bitSets,t),n=e.has(t);s&&!n?e._addEntity(t):!s&&n&&e._removeEntity(t)}}onEntityRemoved(t){for(const e of this.queries.values())e._removeEntity(t)}find(t){const e=m(t),s=this.queries.get(e);return s?s.entities:this.getQueryResults(this.getQueryBitSets(t))}matchesQueryConditions(t,e){return!(t.all&&!e._componentsBitSet.containsAll(t.all))&&(!(t.any&&!e._componentsBitSet.containsAny(t.any))&&(!t.not||!e._componentsBitSet.containsAny(t.not)))}getQueryResults(t){const e=[];for(const s of this.world.entities.values())this.matchesQueryConditions(t,s)&&e.push(s);return e}getQueryBitSets(t){const{all:e,any:s,not:n}=Array.isArray(t)?{all:t,any:void 0,not:void 0}:t,o={};return o.all=e?new i(e.map((t=>t.componentIndex))):void 0,o.any=s?new i(s.map((t=>t.componentIndex))):void 0,o.not=n?new i(n.map((t=>t.componentIndex))):void 0,o}}class _{enabled=!0;world;__internal={class:null,queries:new Set,priority:0,order:0,requiredQueries:[]};constructor(t){this.world=t}onDestroy(){}onInit(){}onUpdate(t,e){}unregister(){this.world.unregisterSystem(this.__internal.class)}query(t,e){return this.world.systemManager.createSystemQuery(this,t,e)}singleton(t,e){return{__internal:{systemSingletonPlaceholder:!0,componentDefinition:t,options:e}}}attach(t){return{__internal:{attachedSystemPlaceholder:!0,systemClass:t}}}}class w{systems=new Map;sortedSystems=[];systemCounter=0;systemAttachments=new Map;world;constructor(t){this.world=t}init(){for(const t of this.systems.values())this.initSystem(t);this.sortSystems()}update(t,e){for(const s of this.sortedSystems.values())s.enabled&&(s.__internal.requiredQueries.length>0&&s.__internal.requiredQueries.some((t=>0===t.entities.length))||s.onUpdate(t,e))}destroy(){for(const t of this.systems.values())t.onDestroy()}registerSystem(t,e){if(this.systems.has(t))throw new Error(`System "${t.name}" has already been registered`);this.systemCounter++;const s=new t(this.world);this.systems.set(t,s),s.__internal.class=t,s.__internal.priority=e?.priority??0,s.__internal.order=this.systemCounter,this.initSingletonQueries(s),this.updateAllSystemAttachments();const n=(i=t,o="onUpdate",Object.getOwnPropertyNames(i.prototype).includes(o));var i,o;n&&this.sortedSystems.push(s),this.world.initialised&&(this.initSystem(s),n&&this.sortSystems())}unregisterSystem(t){const e=this.systems.get(t);e&&(this.systems.delete(t),this.sortedSystems=this.sortedSystems.filter((e=>e.__internal.class!==t)),e.__internal.queries.forEach((t=>{this.world.queryManager.removeQuery(t,e)})),e.__internal.requiredQueries=[],e.onDestroy(),this.updateAllSystemAttachments())}createSystemQuery(t,e,s){const n=this.world.queryManager.createQuery(e,t);return t.__internal.queries.add(n),s?.required&&t.__internal.requiredQueries.push(n),n}initSystem(t){t.onInit()}initSingletonQueries(t){for(const e in t){const s=t,n=s[e];if(n?.__internal?.systemSingletonPlaceholder){const{__internal:{componentDefinition:i,options:o}}=n,r=this.createSystemQuery(t,[i],o),a=()=>{s[e]=r.first?.get(i)};r.onEntityAdded.add(a),r.onEntityRemoved.add(a),a()}}}updateSystemAttachments(t){for(const e in t){const s=t[e];if(s?.__internal?.attachedSystemPlaceholder){const n=this.systemAttachments.get(t)??[];n.push({field:e,systemClass:s.__internal.systemClass}),this.systemAttachments.set(t,n);const{__internal:{systemClass:i}}=s;t[e]=this.world.getSystem(i)}}const e=this.systemAttachments.get(t)??[];for(const{field:s,systemClass:n}of e)t[s]=this.world.getSystem(n)}updateAllSystemAttachments(){for(const t of this.systems.values())this.updateSystemAttachments(t)}sortSystems(){this.sortedSystems.sort(((t,e)=>e.__internal.priority-t.__internal.priority||t.__internal.order-e.__internal.order))}}class g{components=new Map;currentComponentIndex=-1;world;constructor(t){this.world=t}registerComponent(t){let e=this.components.get(t);if(void 0!==e)return e;if(this.currentComponentIndex++,e=this.currentComponentIndex,t.componentIndex=e,this.components.set(t,e),this.world.initialised)for(const t of this.world.entities.values())t._componentsBitSet.resize(this.currentComponentIndex);return e}}class f extends y{initialised=!1;time=0;queryManager;systemManager;componentRegistry;componentPool;entityPool;constructor(){super(),this.componentRegistry=new g(this),this.queryManager=new p(this),this.systemManager=new w(this),this.componentPool=new c,this.entityPool=new l(this)}init(){this.initialised=!0;for(const t of this.entities)this.initialiseEntity(t);this.systemManager.init()}update(t=0){this.time+=t,this.systemManager.update(t,this.time)}reset(){this.time=0,this.initialised=!1,this.systemManager.destroy();for(const t of this.entities.values())this.destroy(t)}create(t){const e=this.entityPool.request();return this.initialised&&this.initialiseEntity(e),t&&e.bulk(t),this._addEntity(e),e}destroy(t){this._removeEntity(t),t._updateQueries=!1,t._updateBitSet=!1;for(const e of Object.values(t._components)){const s=e;t.remove(s._arancini_component_definition)}t._updateQueries=!0,t._updateBitSet=!0,this.queryManager.onEntityRemoved(t),this.entityPool.recycle(t)}query(t){return this.queryManager.createQuery(t)}find(t){return this.queryManager.find(t)}registerComponent(t){return this.componentRegistry.registerComponent(t),this}registerSystem(t,e){return this.systemManager.registerSystem(t,e),this}unregisterSystem(t){return this.systemManager.unregisterSystem(t),this}getSystem(t){return this.systemManager.systems.get(t)}getSystems(){return Array.from(this.systemManager.systems.values())}initialiseEntity(e){e.initialised=!0;for(const s of Object.values(e._components)){const e=s;e._arancini_component_definition?.type===t.CLASS&&s.onInit()}}}export{s as Component,t as ComponentDefinitionType,a as Entity,h as ObjectPool,u as Query,p as QueryManager,_ as System,w as SystemManager,d as Topic,f as World,n as cloneComponentDefinition,m as getQueryDedupeString,e as objectPooled}; | ||
//# sourceMappingURL=index.es.js.map |
import type { ComponentDefinition } from './component'; | ||
import type { Entity } from './entity'; | ||
import { Topic } from './events'; | ||
import type { BitSet } from './utils/bit-set'; | ||
import { EntityContainer } from './entity-container'; | ||
import { BitSet } from './utils/bit-set'; | ||
import type { World } from './world'; | ||
export type QueryConditionType = 'all' | 'any' | 'not'; | ||
/** | ||
* Enum for query condition types | ||
*/ | ||
export declare const QueryConditionType: { | ||
readonly ALL: "all"; | ||
readonly ANY: "any"; | ||
readonly NOT: "not"; | ||
}; | ||
/** | ||
* Type for query conditions | ||
*/ | ||
export type QueryDescription = ComponentDefinition<unknown>[] | { | ||
[QueryConditionType.ALL]?: ComponentDefinition<unknown>[]; | ||
[QueryConditionType.NOT]?: ComponentDefinition<unknown>[]; | ||
[QueryConditionType.ANY]?: ComponentDefinition<unknown>[]; | ||
all?: ComponentDefinition<unknown>[]; | ||
not?: ComponentDefinition<unknown>[]; | ||
any?: ComponentDefinition<unknown>[]; | ||
}; | ||
@@ -36,3 +29,3 @@ export type QueryBitSets = { | ||
* | ||
* Query results can also be retrieved once-off without creating a persistent query with `world.query(...)`. | ||
* Query results can also be retrieved once-off without creating a persistent query with `world.find(...)`. | ||
* | ||
@@ -62,3 +55,3 @@ * ```ts | ||
* // get once-off query results, re-using existing query results if available | ||
* world.query(simpleQueryDescription) | ||
* world.find(simpleQueryDescription) | ||
* | ||
@@ -84,53 +77,64 @@ * // get a query that will update every world update | ||
*/ | ||
export declare class Query { | ||
/** | ||
* The query dedupe string | ||
*/ | ||
export declare class Query extends EntityContainer { | ||
world: World; | ||
key: string; | ||
description: QueryDescription; | ||
bitSets: QueryBitSets; | ||
/** | ||
* The current entities matched by the query | ||
* Constructor for a new query instance | ||
* @param world the world the query is in | ||
* @param queryKey the key for the query | ||
*/ | ||
entities: Entity[]; | ||
constructor(world: World, key: string, description: QueryDescription, bitSets: QueryBitSets); | ||
/** | ||
* Returns the first entity within this archetype. | ||
* */ | ||
get first(): Entity | undefined; | ||
/** | ||
* Event dispatcher for when an Entity is added to the query | ||
* Destroys the Query | ||
*/ | ||
onEntityAdded: Topic<[Entity]>; | ||
destroy(): void; | ||
} | ||
export declare const getQueryDedupeString: (queryDescription: QueryDescription) => string; | ||
/** | ||
* QueryManager is an internal class that manages Query instances | ||
* | ||
* @private internal class, do not use directly | ||
*/ | ||
export declare class QueryManager { | ||
queries: Map<string, Query>; | ||
queryOwners: Map<string, unknown[]>; | ||
private world; | ||
constructor(world: World); | ||
/** | ||
* Event dispatcher for when an Entity is removed from the query | ||
* Creates a new query by a query description | ||
* @param queryDescription the description of the query to create | ||
*/ | ||
onEntityRemoved: Topic<[Entity]>; | ||
createQuery(queryDescription: QueryDescription, owner?: unknown): Query; | ||
/** | ||
* Iterator for entities matched by the query. Iterates over matching entities in reverse order. | ||
* Removes a query from the query manager | ||
* @param query the query to remove | ||
*/ | ||
[Symbol.iterator](): { | ||
next: () => { | ||
value: Entity; | ||
done: boolean; | ||
}; | ||
}; | ||
removeQuery(query: Query, owner?: unknown): void; | ||
/** | ||
* The World the Query is in | ||
* Returns whether the query manager has the query | ||
* @param queryDescription the query description to check for | ||
*/ | ||
private world; | ||
hasQuery(queryDescription: QueryDescription): boolean; | ||
/** | ||
* Constructor for a new query instance | ||
* @param world the world the query is in | ||
* @param queryKey the key for the query | ||
* Updates queries after a component has been added to or removed from an entity | ||
* @param entity the query | ||
*/ | ||
constructor(world: World, queryKey: string); | ||
onEntityComponentChange(entity: Entity): void; | ||
/** | ||
* Destroys the Query | ||
* Updates queries after a query has been removed from the World | ||
* @param entity the query | ||
*/ | ||
destroy(): void; | ||
onEntityRemoved(entity: Entity): void; | ||
/** | ||
* Returns a string that identifies a query description | ||
* Executes a query and returns a set of the matching Entities. | ||
* By default the query is freshly evaluated, regardless of whether a query with the same description already exists in the world. | ||
* If `options.useExisting` is true, results are taken from an existing query if present. | ||
* @param queryDescription the query description | ||
* @returns a string that identifies a query description | ||
* @private called internally, do not call directly | ||
*/ | ||
static getDescriptionDedupeString(queryDescription: QueryDescription): string; | ||
find(queryDescription: QueryDescription): Entity[]; | ||
private matchesQueryConditions; | ||
private getQueryResults; | ||
private getQueryBitSets; | ||
} |
@@ -1,2 +0,2 @@ | ||
import { ComponentDefinition, ComponentDefinitionInstance } from './component'; | ||
import { ComponentDefinition, ComponentInstance } from './component'; | ||
import type { QueryDescription } from './query'; | ||
@@ -96,5 +96,5 @@ import { Query } from './query'; | ||
/** | ||
* Destroys the system and removes it from the World | ||
* Unregisters the system | ||
*/ | ||
destroy(): void; | ||
unregister(): void; | ||
/** | ||
@@ -109,5 +109,71 @@ * Creates and returns a query that gets updated every update. | ||
* Shortcut for creating a query for a singleton component. | ||
* @param clazz the singleton component class | ||
* @param componentDefinition the singleton component | ||
*/ | ||
protected singleton<T extends ComponentDefinition<unknown>>(componentDefinition: T, options?: SystemQueryOptions): ComponentDefinitionInstance<T> | undefined; | ||
protected singleton<T extends ComponentDefinition<unknown>>(componentDefinition: T, options?: SystemQueryOptions): ComponentInstance<T> | undefined; | ||
/** | ||
* Returns a reference to another system that updates as systems are registered and unregistered. | ||
* @param systemClass | ||
* @returns a reference to the system that will be set just before the onInit method is called. | ||
*/ | ||
protected attach<T extends SystemClass<any>>(systemClass: T): InstanceType<T> | undefined; | ||
} | ||
export type SystemAttributes = { | ||
priority?: number; | ||
}; | ||
/** | ||
* SystemManager is an internal class that manages Systems and calls their lifecycle hooks. | ||
* | ||
* Handles adding and removing systems and providing them with queries via the `QueryManager`. | ||
* | ||
* Maintains the usage of queries by systems and removes queries from the `QueryManager` if no systems are | ||
* using a query. | ||
* | ||
* @private internal class, do not use directly | ||
*/ | ||
export declare class SystemManager { | ||
/** | ||
* Systems in the System Manager | ||
*/ | ||
systems: Map<SystemClass, System>; | ||
private sortedSystems; | ||
private systemCounter; | ||
private systemAttachments; | ||
private world; | ||
constructor(world: World); | ||
/** | ||
* Initialises the system manager | ||
*/ | ||
init(): void; | ||
/** | ||
* Updates Systems in the SystemManager | ||
* @param delta the time elapsed in seconds | ||
* @param time the current time in seconds | ||
*/ | ||
update(delta: number, time: number): void; | ||
/** | ||
* Destroys all systems | ||
*/ | ||
destroy(): void; | ||
/** | ||
* Adds a system to the system manager | ||
* @param Clazz the system class to add | ||
*/ | ||
registerSystem(Clazz: SystemClass, attributes?: SystemAttributes): void; | ||
/** | ||
* Unregisters a System from the SystemManager | ||
* @param clazz the System to remove | ||
*/ | ||
unregisterSystem(clazz: SystemClass): void; | ||
/** | ||
* Creates a query for a system | ||
* @param system the system to create the query for | ||
* @param queryDescription the query description | ||
* @param options the options for the query | ||
*/ | ||
createSystemQuery(system: System, queryDescription: QueryDescription, options?: SystemQueryOptions): Query; | ||
private initSystem; | ||
private initSingletonQueries; | ||
private updateSystemAttachments; | ||
private updateAllSystemAttachments; | ||
private sortSystems; | ||
} |
@@ -1,10 +0,10 @@ | ||
import type { ComponentDefinition } from './component'; | ||
import { type ComponentDefinition } from './component'; | ||
import { ComponentRegistry } from './component-registry'; | ||
import type { Entity } from './entity'; | ||
import { EntityManager } from './entity-manager'; | ||
import { EntityContainer } from './entity-container'; | ||
import { ComponentPool, EntityPool } from './pools'; | ||
import type { Query, QueryDescription } from './query'; | ||
import { QueryManager } from './query-manager'; | ||
import type { System, SystemClass } from './system'; | ||
import type { SystemAttributes } from './system-manager'; | ||
import { SystemManager } from './system-manager'; | ||
import { QueryManager } from './query'; | ||
import type { System, SystemAttributes, SystemClass } from './system'; | ||
import { SystemManager } from './system'; | ||
/** | ||
@@ -29,7 +29,7 @@ * A World that can contain Entities, Systems, and Queries. | ||
* | ||
* // destroy the world, removing all entities | ||
* world.destroy() | ||
* // reset the world, removing all entities | ||
* world.reset() | ||
* ``` | ||
*/ | ||
export declare class World { | ||
export declare class World extends EntityContainer { | ||
/** | ||
@@ -44,7 +44,2 @@ * Whether the World has been initialised | ||
/** | ||
* The EntityManager for the World | ||
* Manages Entities and Components | ||
*/ | ||
entityManager: EntityManager; | ||
/** | ||
* The QueryManager for the World | ||
@@ -65,6 +60,10 @@ * Manages and updates Queries | ||
/** | ||
* Entities in the World | ||
* Object pool for components | ||
*/ | ||
entities: Map<string, Entity>; | ||
componentPool: ComponentPool; | ||
/** | ||
* Object pool for entities | ||
*/ | ||
entityPool: EntityPool; | ||
/** | ||
* Constructor for a World | ||
@@ -83,5 +82,9 @@ */ | ||
/** | ||
* Destroys the World | ||
* Resets the World. | ||
* | ||
* This removes all entities, and calls onDestroy on all Systems. | ||
* Components and Systems will remain registered. | ||
* The World will need to be initialised again after this. | ||
*/ | ||
destroy(): void; | ||
reset(): void; | ||
/** | ||
@@ -110,2 +113,7 @@ * Creates an Entity | ||
/** | ||
* Destroys an Entity | ||
* @param entity the Entity to destroy | ||
*/ | ||
destroy(entity: Entity): void; | ||
/** | ||
* Creates a Query | ||
@@ -152,2 +160,3 @@ * @param queryDescription the query description | ||
getSystems(): System[]; | ||
private initialiseEntity; | ||
} |
@@ -12,3 +12,3 @@ { | ||
"license": "MIT", | ||
"version": "3.1.1", | ||
"version": "3.2.0", | ||
"homepage": "https://github.com/isaac-mason/arancini", | ||
@@ -27,4 +27,4 @@ "bugs": { | ||
"devDependencies": { | ||
"@babel/core": "^7.21.4", | ||
"@babel/preset-env": "^7.22.7", | ||
"@babel/core": "^7.22.11", | ||
"@babel/preset-env": "^7.22.10", | ||
"@babel/preset-typescript": "^7.21.4", | ||
@@ -31,0 +31,0 @@ "@mdx-js/react": "^2.3.0", |
Sorry, the diff of this file is not supported yet
122735
19
1119