Comparing version 0.3.15 to 0.3.16-1
@@ -13,2 +13,4 @@ declare module 'bitecs' { | ||
export type ListType = readonly [Type, number]; | ||
export const Types: { | ||
@@ -57,4 +59,12 @@ i8: "i8" | ||
export type ComponentType<T extends ISchema> = { | ||
[key in keyof T]: T[key] extends Type ? ArrayByType[T[key]] : T[key] extends ISchema ? ComponentType<T[key]> : unknown; | ||
} | ||
[key in keyof T]: T[key] extends Type | ||
? ArrayByType[T[key]] | ||
: T[key] extends [infer RT, number] | ||
? RT extends Type | ||
? Array<ArrayByType[RT]> | ||
: unknown | ||
: T[key] extends ISchema | ||
? ComponentType<T[key]> | ||
: unknown; | ||
}; | ||
@@ -66,7 +76,7 @@ export interface IWorld { | ||
export interface ISchema { | ||
[key: string]: Type | [Type, number] | ISchema | ||
[key: string]: Type | ListType | ISchema | ||
} | ||
export interface IComponentProp { | ||
[key: string]: TypedArray | ||
[key: string]: TypedArray | Array<TypedArray> | ||
} | ||
@@ -84,4 +94,8 @@ | ||
export type System = (world: IWorld) => IWorld | ||
export type System = (world: IWorld, ...args: any[]) => IWorld | ||
export type Serializer = (target: IWorld | number[]) => ArrayBuffer | ||
export type Deserializer = (world: IWorld, packet: ArrayBuffer, mode?: DESERIALIZE_MODE) => void | ||
export function setDefaultSize(size: number): void | ||
export function createWorld(size?: number): IWorld | ||
@@ -111,6 +125,8 @@ export function resetWorld(world: IWorld): IWorld | ||
export function defineSerializer(target: IWorld | Component[] | IComponentProp[] | QueryModifier, maxBytes?: number): (target: IWorld | number[]) => ArrayBuffer | ||
export function defineDeserializer(target: IWorld | Component[] | IComponentProp[] | QueryModifier): (world: IWorld, packet: ArrayBuffer, mode?: DESERIALIZE_MODE) => void | ||
export function defineSerializer(target: IWorld | Component[] | IComponentProp[] | QueryModifier, maxBytes?: number): Serializer | ||
export function defineDeserializer(target: IWorld | Component[] | IComponentProp[] | QueryModifier): Deserializer | ||
export function pipe(...fns: ((...args: any[]) => any)[]): (...input: any[]) => any | ||
export const parentArray: Symbol | ||
} |
@@ -357,3 +357,11 @@ const TYPES_ENUM = { | ||
}; | ||
/** | ||
* Defines a new serializer which targets the given components to serialize the data of when called on a world or array of EIDs. | ||
* | ||
* @param {object|array} target | ||
* @param {number} [maxBytes=20000000] | ||
* @returns {ArrayBuffer} | ||
*/ | ||
const defineSerializer = (target, maxBytes = 20000000) => { | ||
@@ -480,2 +488,8 @@ const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap); | ||
const newEntities = new Map(); | ||
/** | ||
* Defines a new deserializer which targets the given components to deserialize onto a given world. | ||
* | ||
* @param {object|array} target | ||
*/ | ||
const defineDeserializer = target => { | ||
@@ -574,2 +588,3 @@ const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap); | ||
const $entityMasks = Symbol('entityMasks'); | ||
const $entityComponents = Symbol('entityMasks'); | ||
const $entitySparseSet = Symbol('entitySparseSet'); | ||
@@ -582,5 +597,4 @@ const $entityArray = Symbol('entityArray'); | ||
let globalSize = defaultSize; | ||
const threshold = globalSize - globalSize / 5; | ||
let resizeThreshold = () => threshold; | ||
let resizeThreshold = () => globalSize - globalSize / 5; | ||
@@ -596,4 +610,10 @@ const getGlobalSize = () => globalSize; // removed eids should also be global to prevent memory leaks | ||
const getDefaultSize = () => defaultSize; | ||
const setDefaultSize = x => { | ||
defaultSize = x; | ||
/** | ||
* Sets the default maximum number of entities for worlds and component stores. | ||
* | ||
* @param {number} size | ||
*/ | ||
const setDefaultSize = size => { | ||
defaultSize = size; | ||
resetGlobals(); | ||
@@ -603,2 +623,9 @@ }; | ||
const eidToWorld = new Map(); | ||
/** | ||
* Adds a new entity to the specified world. | ||
* | ||
* @param {World} world | ||
* @returns {number} eid | ||
*/ | ||
const addEntity = world => { | ||
@@ -625,4 +652,12 @@ const eid = removed.length > 0 ? removed.shift() : globalEntityCursor++; | ||
}); | ||
world[$entityComponents].set(eid, new Set()); | ||
return eid; | ||
}; | ||
/** | ||
* Removes an existing entity from the specified world. | ||
* | ||
* @param {World} world | ||
* @param {number} eid | ||
*/ | ||
const removeEntity = (world, eid) => { | ||
@@ -637,9 +672,18 @@ // Check if entity is already removed | ||
removed.push(eid); // pop swap | ||
removed.push(eid); // remove all eid state from world | ||
world[$entitySparseSet].remove(eid); // Clear entity bitmasks | ||
world[$entitySparseSet].remove(eid); | ||
world[$entityComponents].delete(eid); // Clear entity bitmasks | ||
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0; | ||
}; | ||
/** | ||
* Returns an array of components that an entity possesses. | ||
* | ||
* @param {*} world | ||
* @param {*} eid | ||
*/ | ||
const getEntityComponents = (world, eid) => Array.from(world[$entityComponents].get(eid)); | ||
function Not(c) { | ||
@@ -660,2 +704,9 @@ return function QueryNot() { | ||
const $queryComponents = Symbol('queryComponents'); | ||
/** | ||
* Given an existing query, returns a new function which returns entities who have been added to the given query since the last call of the function. | ||
* | ||
* @param {function} query | ||
* @returns {function} enteredQuery | ||
*/ | ||
const enterQuery = query => world => { | ||
@@ -666,2 +717,9 @@ if (!world[$queryMap].has(query)) registerQuery(world, query); | ||
}; | ||
/** | ||
* Given an existing query, returns a new function which returns entities who have been removed from the given query since the last call of the function. | ||
* | ||
* @param {function} query | ||
* @returns {function} enteredQuery | ||
*/ | ||
const exitQuery = query => world => { | ||
@@ -807,3 +865,10 @@ if (!world[$queryMap].has(query)) registerQuery(world, query); | ||
}; | ||
/** | ||
* Defines a query function which returns a matching set of entities when called on a world. | ||
* | ||
* @param {array} components | ||
* @returns {function} query | ||
*/ | ||
const defineQuery = components => { | ||
@@ -875,2 +940,9 @@ if (components === undefined || components[$componentMap] !== undefined) { | ||
}; | ||
/** | ||
* Resets a Changed-based query, clearing the underlying list of changed entities. | ||
* | ||
* @param {World} world | ||
* @param {function} query | ||
*/ | ||
const resetChangedQuery = (world, query) => { | ||
@@ -880,2 +952,9 @@ const q = world[$queryMap].get(query); | ||
}; | ||
/** | ||
* Removes a query from a world. | ||
* | ||
* @param {World} world | ||
* @param {function} query | ||
*/ | ||
const removeQuery = (world, query) => { | ||
@@ -892,2 +971,9 @@ const q = world[$queryMap].get(query); | ||
}; | ||
/** | ||
* Defines a new component store. | ||
* | ||
* @param {object} schema | ||
* @returns {object} | ||
*/ | ||
const defineComponent = schema => { | ||
@@ -906,2 +992,9 @@ const component = createStore(schema, getDefaultSize()); | ||
}; | ||
/** | ||
* Registers a component with a world. | ||
* | ||
* @param {World} world | ||
* @param {Component} component | ||
*/ | ||
const registerComponent = (world, component) => { | ||
@@ -932,5 +1025,21 @@ if (!component) throw new Error(`๐พ bitECS - cannot register component as it is null or undefined.`); | ||
}; | ||
/** | ||
* Registers multiple components with a world. | ||
* | ||
* @param {World} world | ||
* @param {Component} components | ||
*/ | ||
const registerComponents = (world, components) => { | ||
components.forEach(c => registerComponent(world, c)); | ||
}; | ||
/** | ||
* Checks if an entity has a component. | ||
* | ||
* @param {World} world | ||
* @param {Component} component | ||
* @param {number} eid | ||
* @returns {boolean} | ||
*/ | ||
const hasComponent = (world, component, eid) => { | ||
@@ -946,2 +1055,11 @@ const registeredComponent = world[$componentMap].get(component); | ||
}; | ||
/** | ||
* Adds a component to an entity | ||
* | ||
* @param {World} world | ||
* @param {Component} component | ||
* @param {number} eid | ||
* @param {boolean} [reset=false] | ||
*/ | ||
const addComponent = (world, component, eid, reset = false) => { | ||
@@ -973,6 +1091,16 @@ if (!Number.isInteger(eid)) { | ||
if (match) queryAddEntity(q, eid); | ||
}); // Zero out each property value | ||
}); | ||
world[$entityComponents].get(eid).add(component); // Zero out each property value | ||
if (reset) resetStoreFor(component, eid); | ||
}; | ||
/** | ||
* Removes a component from an entity and resets component state unless otherwise specified. | ||
* | ||
* @param {World} world | ||
* @param {Component} component | ||
* @param {number} eid | ||
* @param {boolean} [reset=true] | ||
*/ | ||
const removeComponent = (world, component, eid, reset = true) => { | ||
@@ -1003,3 +1131,4 @@ if (!Number.isInteger(eid)) { | ||
if (match) queryAddEntity(q, eid); | ||
}); // Zero out each property value | ||
}); | ||
world[$entityComponents].get(eid).delete(component); // Zero out each property value | ||
@@ -1017,4 +1146,3 @@ if (reset) resetStoreFor(component, eid); | ||
worlds.forEach(world => { | ||
world[$size] = size; // resize(world[$entitySparseSet].sparse, size) | ||
// resize(world[$entitySparseSet].dense, size) | ||
world[$size] = size; | ||
@@ -1029,2 +1157,8 @@ for (let i = 0; i < world[$entityMasks].length; i++) { | ||
}; | ||
/** | ||
* Creates a new world. | ||
* | ||
* @returns {object} | ||
*/ | ||
const createWorld = () => { | ||
@@ -1036,2 +1170,9 @@ const world = {}; | ||
}; | ||
/** | ||
* Resets a world. | ||
* | ||
* @param {World} world | ||
* @returns {object} | ||
*/ | ||
const resetWorld = world => { | ||
@@ -1042,5 +1183,5 @@ const size = getGlobalSize(); | ||
world[$entityMasks] = [new Uint32Array(size)]; | ||
world[$entityComponents] = new Map(); | ||
world[$archetypes] = []; | ||
world[$entitySparseSet] = SparseSet(); // world[$entitySparseSet] = Uint32SparseSet(size) | ||
world[$entitySparseSet] = SparseSet(); | ||
world[$entityArray] = world[$entitySparseSet].dense; | ||
@@ -1056,2 +1197,8 @@ world[$bitflag] = 1; | ||
}; | ||
/** | ||
* Deletes a world. | ||
* | ||
* @param {World} world | ||
*/ | ||
const deleteWorld = world => { | ||
@@ -1067,2 +1214,9 @@ Object.getOwnPropertySymbols(world).forEach($ => { | ||
/** | ||
* Defines a new system function. | ||
* | ||
* @param {function} update | ||
* @returns {function} | ||
*/ | ||
const defineSystem = (fn1, fn2) => { | ||
@@ -1112,3 +1266,3 @@ const update = fn2 !== undefined ? fn2 : fn1; | ||
export { Changed, DESERIALIZE_MODE, Not, Types, addComponent, addEntity, commitRemovals, createWorld, defineComponent, defineDeserializer, defineQuery, defineSerializer, defineSystem, deleteWorld, enterQuery, exitQuery, hasComponent, parentArray, pipe, registerComponent, registerComponents, removeComponent, removeEntity, removeQuery, resetChangedQuery, resetWorld, setDefaultSize }; | ||
export { Changed, DESERIALIZE_MODE, Not, Types, addComponent, addEntity, commitRemovals, createWorld, defineComponent, defineDeserializer, defineQuery, defineSerializer, defineSystem, deleteWorld, enterQuery, exitQuery, getEntityComponents, hasComponent, parentArray, pipe, registerComponent, registerComponents, removeComponent, removeEntity, removeQuery, resetChangedQuery, resetWorld, setDefaultSize }; | ||
//# sourceMappingURL=index.es.js.map |
@@ -361,3 +361,11 @@ 'use strict'; | ||
}; | ||
/** | ||
* Defines a new serializer which targets the given components to serialize the data of when called on a world or array of EIDs. | ||
* | ||
* @param {object|array} target | ||
* @param {number} [maxBytes=20000000] | ||
* @returns {ArrayBuffer} | ||
*/ | ||
const defineSerializer = (target, maxBytes = 20000000) => { | ||
@@ -484,2 +492,8 @@ const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap); | ||
const newEntities = new Map(); | ||
/** | ||
* Defines a new deserializer which targets the given components to deserialize onto a given world. | ||
* | ||
* @param {object|array} target | ||
*/ | ||
const defineDeserializer = target => { | ||
@@ -578,2 +592,3 @@ const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap); | ||
const $entityMasks = Symbol('entityMasks'); | ||
const $entityComponents = Symbol('entityMasks'); | ||
const $entitySparseSet = Symbol('entitySparseSet'); | ||
@@ -586,5 +601,4 @@ const $entityArray = Symbol('entityArray'); | ||
let globalSize = defaultSize; | ||
const threshold = globalSize - globalSize / 5; | ||
let resizeThreshold = () => threshold; | ||
let resizeThreshold = () => globalSize - globalSize / 5; | ||
@@ -600,4 +614,10 @@ const getGlobalSize = () => globalSize; // removed eids should also be global to prevent memory leaks | ||
const getDefaultSize = () => defaultSize; | ||
const setDefaultSize = x => { | ||
defaultSize = x; | ||
/** | ||
* Sets the default maximum number of entities for worlds and component stores. | ||
* | ||
* @param {number} size | ||
*/ | ||
const setDefaultSize = size => { | ||
defaultSize = size; | ||
resetGlobals(); | ||
@@ -607,2 +627,9 @@ }; | ||
const eidToWorld = new Map(); | ||
/** | ||
* Adds a new entity to the specified world. | ||
* | ||
* @param {World} world | ||
* @returns {number} eid | ||
*/ | ||
const addEntity = world => { | ||
@@ -629,4 +656,12 @@ const eid = removed.length > 0 ? removed.shift() : globalEntityCursor++; | ||
}); | ||
world[$entityComponents].set(eid, new Set()); | ||
return eid; | ||
}; | ||
/** | ||
* Removes an existing entity from the specified world. | ||
* | ||
* @param {World} world | ||
* @param {number} eid | ||
*/ | ||
const removeEntity = (world, eid) => { | ||
@@ -641,9 +676,18 @@ // Check if entity is already removed | ||
removed.push(eid); // pop swap | ||
removed.push(eid); // remove all eid state from world | ||
world[$entitySparseSet].remove(eid); // Clear entity bitmasks | ||
world[$entitySparseSet].remove(eid); | ||
world[$entityComponents].delete(eid); // Clear entity bitmasks | ||
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0; | ||
}; | ||
/** | ||
* Returns an array of components that an entity possesses. | ||
* | ||
* @param {*} world | ||
* @param {*} eid | ||
*/ | ||
const getEntityComponents = (world, eid) => Array.from(world[$entityComponents].get(eid)); | ||
function Not(c) { | ||
@@ -664,2 +708,9 @@ return function QueryNot() { | ||
const $queryComponents = Symbol('queryComponents'); | ||
/** | ||
* Given an existing query, returns a new function which returns entities who have been added to the given query since the last call of the function. | ||
* | ||
* @param {function} query | ||
* @returns {function} enteredQuery | ||
*/ | ||
const enterQuery = query => world => { | ||
@@ -670,2 +721,9 @@ if (!world[$queryMap].has(query)) registerQuery(world, query); | ||
}; | ||
/** | ||
* Given an existing query, returns a new function which returns entities who have been removed from the given query since the last call of the function. | ||
* | ||
* @param {function} query | ||
* @returns {function} enteredQuery | ||
*/ | ||
const exitQuery = query => world => { | ||
@@ -811,3 +869,10 @@ if (!world[$queryMap].has(query)) registerQuery(world, query); | ||
}; | ||
/** | ||
* Defines a query function which returns a matching set of entities when called on a world. | ||
* | ||
* @param {array} components | ||
* @returns {function} query | ||
*/ | ||
const defineQuery = components => { | ||
@@ -879,2 +944,9 @@ if (components === undefined || components[$componentMap] !== undefined) { | ||
}; | ||
/** | ||
* Resets a Changed-based query, clearing the underlying list of changed entities. | ||
* | ||
* @param {World} world | ||
* @param {function} query | ||
*/ | ||
const resetChangedQuery = (world, query) => { | ||
@@ -884,2 +956,9 @@ const q = world[$queryMap].get(query); | ||
}; | ||
/** | ||
* Removes a query from a world. | ||
* | ||
* @param {World} world | ||
* @param {function} query | ||
*/ | ||
const removeQuery = (world, query) => { | ||
@@ -896,2 +975,9 @@ const q = world[$queryMap].get(query); | ||
}; | ||
/** | ||
* Defines a new component store. | ||
* | ||
* @param {object} schema | ||
* @returns {object} | ||
*/ | ||
const defineComponent = schema => { | ||
@@ -910,2 +996,9 @@ const component = createStore(schema, getDefaultSize()); | ||
}; | ||
/** | ||
* Registers a component with a world. | ||
* | ||
* @param {World} world | ||
* @param {Component} component | ||
*/ | ||
const registerComponent = (world, component) => { | ||
@@ -936,5 +1029,21 @@ if (!component) throw new Error(`๐พ bitECS - cannot register component as it is null or undefined.`); | ||
}; | ||
/** | ||
* Registers multiple components with a world. | ||
* | ||
* @param {World} world | ||
* @param {Component} components | ||
*/ | ||
const registerComponents = (world, components) => { | ||
components.forEach(c => registerComponent(world, c)); | ||
}; | ||
/** | ||
* Checks if an entity has a component. | ||
* | ||
* @param {World} world | ||
* @param {Component} component | ||
* @param {number} eid | ||
* @returns {boolean} | ||
*/ | ||
const hasComponent = (world, component, eid) => { | ||
@@ -950,2 +1059,11 @@ const registeredComponent = world[$componentMap].get(component); | ||
}; | ||
/** | ||
* Adds a component to an entity | ||
* | ||
* @param {World} world | ||
* @param {Component} component | ||
* @param {number} eid | ||
* @param {boolean} [reset=false] | ||
*/ | ||
const addComponent = (world, component, eid, reset = false) => { | ||
@@ -977,6 +1095,16 @@ if (!Number.isInteger(eid)) { | ||
if (match) queryAddEntity(q, eid); | ||
}); // Zero out each property value | ||
}); | ||
world[$entityComponents].get(eid).add(component); // Zero out each property value | ||
if (reset) resetStoreFor(component, eid); | ||
}; | ||
/** | ||
* Removes a component from an entity and resets component state unless otherwise specified. | ||
* | ||
* @param {World} world | ||
* @param {Component} component | ||
* @param {number} eid | ||
* @param {boolean} [reset=true] | ||
*/ | ||
const removeComponent = (world, component, eid, reset = true) => { | ||
@@ -1007,3 +1135,4 @@ if (!Number.isInteger(eid)) { | ||
if (match) queryAddEntity(q, eid); | ||
}); // Zero out each property value | ||
}); | ||
world[$entityComponents].get(eid).delete(component); // Zero out each property value | ||
@@ -1021,4 +1150,3 @@ if (reset) resetStoreFor(component, eid); | ||
worlds.forEach(world => { | ||
world[$size] = size; // resize(world[$entitySparseSet].sparse, size) | ||
// resize(world[$entitySparseSet].dense, size) | ||
world[$size] = size; | ||
@@ -1033,2 +1161,8 @@ for (let i = 0; i < world[$entityMasks].length; i++) { | ||
}; | ||
/** | ||
* Creates a new world. | ||
* | ||
* @returns {object} | ||
*/ | ||
const createWorld = () => { | ||
@@ -1040,2 +1174,9 @@ const world = {}; | ||
}; | ||
/** | ||
* Resets a world. | ||
* | ||
* @param {World} world | ||
* @returns {object} | ||
*/ | ||
const resetWorld = world => { | ||
@@ -1046,5 +1187,5 @@ const size = getGlobalSize(); | ||
world[$entityMasks] = [new Uint32Array(size)]; | ||
world[$entityComponents] = new Map(); | ||
world[$archetypes] = []; | ||
world[$entitySparseSet] = SparseSet(); // world[$entitySparseSet] = Uint32SparseSet(size) | ||
world[$entitySparseSet] = SparseSet(); | ||
world[$entityArray] = world[$entitySparseSet].dense; | ||
@@ -1060,2 +1201,8 @@ world[$bitflag] = 1; | ||
}; | ||
/** | ||
* Deletes a world. | ||
* | ||
* @param {World} world | ||
*/ | ||
const deleteWorld = world => { | ||
@@ -1071,2 +1218,9 @@ Object.getOwnPropertySymbols(world).forEach($ => { | ||
/** | ||
* Defines a new system function. | ||
* | ||
* @param {function} update | ||
* @returns {function} | ||
*/ | ||
const defineSystem = (fn1, fn2) => { | ||
@@ -1132,2 +1286,3 @@ const update = fn2 !== undefined ? fn2 : fn1; | ||
exports.exitQuery = exitQuery; | ||
exports.getEntityComponents = getEntityComponents; | ||
exports.hasComponent = hasComponent; | ||
@@ -1134,0 +1289,0 @@ exports.parentArray = parentArray; |
@@ -13,2 +13,4 @@ declare module 'bitecs' { | ||
export type ListType = readonly [Type, number]; | ||
export const Types: { | ||
@@ -57,4 +59,12 @@ i8: "i8" | ||
export type ComponentType<T extends ISchema> = { | ||
[key in keyof T]: T[key] extends Type ? ArrayByType[T[key]] : T[key] extends ISchema ? ComponentType<T[key]> : unknown; | ||
} | ||
[key in keyof T]: T[key] extends Type | ||
? ArrayByType[T[key]] | ||
: T[key] extends [infer RT, number] | ||
? RT extends Type | ||
? Array<ArrayByType[RT]> | ||
: unknown | ||
: T[key] extends ISchema | ||
? ComponentType<T[key]> | ||
: unknown; | ||
}; | ||
@@ -66,7 +76,7 @@ export interface IWorld { | ||
export interface ISchema { | ||
[key: string]: Type | [Type, number] | ISchema | ||
[key: string]: Type | ListType | ISchema | ||
} | ||
export interface IComponentProp { | ||
[key: string]: TypedArray | ||
[key: string]: TypedArray | Array<TypedArray> | ||
} | ||
@@ -84,4 +94,8 @@ | ||
export type System = (world: IWorld) => IWorld | ||
export type System = (world: IWorld, ...args: any[]) => IWorld | ||
export type Serializer = (target: IWorld | number[]) => ArrayBuffer | ||
export type Deserializer = (world: IWorld, packet: ArrayBuffer, mode?: DESERIALIZE_MODE) => void | ||
export function setDefaultSize(size: number): void | ||
export function createWorld(size?: number): IWorld | ||
@@ -111,6 +125,8 @@ export function resetWorld(world: IWorld): IWorld | ||
export function defineSerializer(target: IWorld | Component[] | IComponentProp[] | QueryModifier, maxBytes?: number): (target: IWorld | number[]) => ArrayBuffer | ||
export function defineDeserializer(target: IWorld | Component[] | IComponentProp[] | QueryModifier): (world: IWorld, packet: ArrayBuffer, mode?: DESERIALIZE_MODE) => void | ||
export function defineSerializer(target: IWorld | Component[] | IComponentProp[] | QueryModifier, maxBytes?: number): Serializer | ||
export function defineDeserializer(target: IWorld | Component[] | IComponentProp[] | QueryModifier): Deserializer | ||
export function pipe(...fns: ((...args: any[]) => any)[]): (...input: any[]) => any | ||
export const parentArray: Symbol | ||
} |
{ | ||
"name": "bitecs", | ||
"version": "0.3.15", | ||
"version": "0.3.16-1", | ||
"description": "Functional, minimal, data-driven, ultra-high performance ECS library written in Javascript", | ||
@@ -37,2 +37,3 @@ "license": "MPL-2.0", | ||
"c8": "^7.7.2", | ||
"dmd-readable": "^1.2.4", | ||
"globby": "^11.0.1", | ||
@@ -57,2 +58,2 @@ "jsdoc-to-markdown": "^7.0.1", | ||
"type": "module" | ||
} | ||
} |
288
README.md
@@ -13,3 +13,3 @@ # ๐พ bitECS ๐พ [](https://www.npmjs.com/package/bitecs) [](https://www.npmjs.com/package/bitecs) [](https://www.npmjs.com/package/bitecs) [](https://www.npmjs.com/package/bitecs) | ||
| ๐ Zero dependencies | ๐ Node or browser | | ||
| ๐ค `~4kb` gzipped | ๐ท TypeScript support | | ||
| ๐ค `<5kb` minzipped | ๐ท TypeScript support | | ||
| โค Made with love | ๐บ [glMatrix](https://github.com/toji/gl-matrix) support | | ||
@@ -32,3 +32,2 @@ | ||
## ๐ฟ Install | ||
@@ -39,281 +38,6 @@ ``` | ||
## ๐บ Overview | ||
Essentials of the API: | ||
```js | ||
import { | ||
createWorld, | ||
addEntity, | ||
removeEntity, | ||
defineComponent, | ||
addComponent, | ||
removeComponent, | ||
hasComponent, | ||
defineQuery, | ||
Changed, | ||
Not, | ||
enterQuery, | ||
exitQuery, | ||
defineSystem, | ||
defineSerializer, | ||
defineDeserializer, | ||
pipe, | ||
Types | ||
} from 'bitecs' | ||
``` | ||
## ๐ World | ||
A world represents a set of entities and the components that they each possess. | ||
Worlds do not store actual component data, only their relationships with entities. | ||
Any number of worlds can be created. An empty object is returned which you can use as a context. | ||
```js | ||
const world = createWorld() | ||
world.name = 'MyWorld' | ||
``` | ||
## ๐พ Entity | ||
An entity is an integer, technically a pointer, which components can be associated with. | ||
Entities are accessed via queries, components of whom are mutated with systems. | ||
Add entities to the world: | ||
```js | ||
const eid = addEntity(world) | ||
const eid2 = addEntity(world) | ||
``` | ||
Remove entities from the world: | ||
```js | ||
removeEntity(world, eid2) | ||
``` | ||
## ๐ฆ Component | ||
Components are pure data and added to entities to give them state. | ||
The object returned from `defineComponent` is a SoA (Structure of Arrays). This is what actually stores the component data. | ||
Define component stores: | ||
```js | ||
const Vector3 = { x: Types.f32, y: Types.f32, z: Types.f32 } | ||
const Position = defineComponent(Vector3) | ||
const Velocity = defineComponent(Vector3) | ||
const List = defineComponent({ values: [Types.f32, 3] }) // [type, length] | ||
const Tag = defineComponent() | ||
``` | ||
Add components to an entity in a world: | ||
```js | ||
addComponent(world, Position, eid) | ||
addComponent(world, Velocity, eid) | ||
addComponent(world, List, eid) | ||
addComponent(world, Tag, eid) | ||
``` | ||
Component data is accessed directly via `eid`, there are no getters or setters: | ||
* This is how high performance iteration is achieved | ||
```js | ||
Velocity.x[eid] = 1 | ||
Velocity.y[eid] = 1 | ||
List.values[eid].set([1,2,3]) | ||
``` | ||
## ๐ Query | ||
A query is defined with components and is used to obtain a specific set of entities from a world. | ||
Define a query: | ||
```js | ||
const movementQuery = defineQuery([Position, Velocity]) | ||
``` | ||
Use the query on a world to obtain an array of entities with those components: | ||
```js | ||
const ents = movementQuery(world) | ||
``` | ||
Wrapping a component with the `Not` modifier defines a query which returns entities who explicitly do not have the component: | ||
```js | ||
const positionWithoutVelocityQuery = defineQuery([ Position, Not(Velocity) ]) | ||
``` | ||
Wrapping a component with the `Change` modifier creates a query which returns entities whose component's state has changed since last call of the function: | ||
```js | ||
const changedPositionQuery = defineQuery([ Changed(Position) ]) | ||
let ents = changedPositionQuery(world) | ||
console.log(ents) // => [] | ||
Position.x[eid]++ | ||
ents = changedPositionQuery(world) | ||
console.log(ents) // => [0] | ||
``` | ||
`enterQuery` returns a function which can be used to capture entities whose components match the query: | ||
```js | ||
const enteredMovementQuery = enterQuery(movementQuery) | ||
const enteredEnts = enteredMovementQuery(world) | ||
``` | ||
`exitQuery` returns a function which can be used to capture entities whose components no longer match the query: | ||
```js | ||
const exitedMovementQuery = exitQuery(movementQuery) | ||
const enteredEnts = exitedMovementQuery(world) | ||
``` | ||
## ๐ธ System | ||
Systems are functions and are run against a world to update component state of entities, or anything else. | ||
Queries are used inside of systems to obtain a relevant set of entities and perform operations on their component data. | ||
While not required, it is greatly encouraged that you keep all component data mutations inside of systems. | ||
Define a system that moves entity positions based on their velocity: | ||
```js | ||
const movementSystem = defineSystem((world) => { | ||
// optionally apply logic to entities added to the query | ||
const entered = enteredMovementQuery(world) | ||
for (let i = 0; i < entered.length; i++) { | ||
const eid = ents[i] | ||
} | ||
// apply system logic | ||
const ents = movementQuery(world) | ||
for (let i = 0; i < ents.length; i++) { | ||
const eid = ents[i] | ||
Position.x[eid] += Velocity.x[eid] | ||
Position.y[eid] += Velocity.y[eid] | ||
} | ||
// optionally apply logic to entities removed from the query | ||
const exited = exitedMovementQuery(world) | ||
for (let i = 0; i < exited.length; i++) { | ||
const eid = ents[i] | ||
} | ||
}) | ||
``` | ||
Define a system which tracks time: | ||
```js | ||
world.time = { | ||
delta: 0, | ||
elapsed: 0, | ||
then: performance.now() | ||
} | ||
const timeSystem = defineSystem(world => { | ||
const now = performance.now() | ||
const delta = now - world.time.then | ||
world.time.delta = delta | ||
world.time.elapsed += delta | ||
world.time.then = now | ||
}) | ||
``` | ||
Systems are used to update entities of a world: | ||
```js | ||
movementSystem(world) | ||
``` | ||
Pipelines of systems should be created with the `pipe` function: | ||
```js | ||
const pipeline = pipe( | ||
movementSystem, | ||
timeSystem | ||
) | ||
pipeline(world) | ||
``` | ||
## ๐พ Serialization | ||
Performant and highly customizable serialization is built-in. Any subset of data can be targeted and serialized/deserialized with great efficiency and ease. | ||
Serializers and deserializers need the same configs in order to work properly. Any combination of components and component properties may be used as configs. | ||
Serialization can take a world as a config and will serialize all component stores registered in that world: | ||
```js | ||
const serialize = defineSerializer(world) | ||
const deserialize = defineDeserializer(world) | ||
``` | ||
Serialize all of the world's entities and thier component data: | ||
```js | ||
const packet = serialize(world) | ||
``` | ||
Use the deserializer to apply state onto the same or any other world: | ||
* Note: serialized entities and components are automatically (re)created if they do not exist in the target world | ||
```js | ||
deserialize(world, packet) | ||
``` | ||
Serialize a more specific set of entities using queries: | ||
```js | ||
const ents = movementQuery(world) | ||
const packet = serialize(ents) | ||
deserialize(world, packet) | ||
``` | ||
Serialization for any mixture of components and component properties: | ||
```js | ||
const config = [Position, Velocity.x, Velocity.y] | ||
const serializeMovement = defineSerializer(config) | ||
const deserializeMovement = defineDeserializer(config) | ||
``` | ||
Serialize Position data for entities matching the movementQuery, defined with pipe: | ||
```js | ||
const serializeMovementQueryPositions = pipe(movementQuery, serializePositions) | ||
const packet = serializeMovementQueryPositions(world) | ||
deserializePositions(world, packet) | ||
``` | ||
Serialization which targets select component stores of entities | ||
whose component state has changed since the last call of the function: | ||
```js | ||
const serializeOnlyChangedPositions = defineSerializer([Changed(Position)]) | ||
const serializeChangedMovementQuery = pipe(movementQuery, serializeOnlyChangedPositions) | ||
let packet = serializeChangedMovementQuery(world) | ||
console.log(packet) // => undefined | ||
Position.x[eid]++ | ||
packet = serializeChangedMovementQuery(world) | ||
console.log(packet.byteLength) // => 13 | ||
``` | ||
### Deserialize Modes | ||
There are 3 modes of deserilization, all of which are additive in nature. | ||
Deserialization will never remove entities, and will only add them. | ||
- `REPLACE` - (default) overwrites entity data, or creates new entities if the serialized EIDs don't exist in the target world. | ||
- `APPEND` - only creates new entities, does not overwrite any existing entity data. | ||
- `MAP` - acts like `REPLACE` but every serialized EID is assigned a local EID which is memorized for all subsequent deserializations onto the target world. | ||
- useful when deserializing server ECS state on a client-side ECS to avoid EID collisions | ||
```js | ||
const mode = DESERIALIZE_MODE.MAP | ||
deserialize(world, packet, mode) | ||
``` | ||
## โน Resources | ||
| | | ||
| ---------------- | | ||
| ๐ [Getting Started](https://github.com/NateTheGreatt/bitECS/blob/master/docs/INTRO.md) | | ||
| ๐ [API Documentation](https://github.com/NateTheGreatt/bitECS/blob/master/docs/API.md) | |
import { strictEqual } from 'assert' | ||
import { $entityEnabled, getEntityCursor, getRemovedEntities, resetGlobals } from '../../src/Entity.js' | ||
import { getEntityCursor, getRemovedEntities, resetGlobals } from '../../src/Entity.js' | ||
import { createWorld, addEntity, removeEntity } from '../../src/index.js' | ||
@@ -4,0 +4,0 @@ |
import assert, { strictEqual } from 'assert' | ||
import { $componentMap } from '../../src/Component.js' | ||
import { $entityEnabled, $entityMasks, resetGlobals, addEntity, getDefaultSize } from '../../src/Entity.js' | ||
import { $dirtyQueries, $queries, $queryMap } from '../../src/Query.js' | ||
import { createWorld, $size, $bitflag } from '../../src/World.js' | ||
import { $entityMasks, resetGlobals, addEntity, getDefaultSize } from '../../src/Entity.js' | ||
import { createWorld } from '../../src/World.js' | ||
@@ -7,0 +5,0 @@ const defaultSize = getDefaultSize() |
import assert, { strictEqual } from 'assert' | ||
import { $componentMap } from '../../src/Component.js' | ||
import { $entityEnabled, $entityMasks, resetGlobals, addEntity, getDefaultSize } from '../../src/Entity.js' | ||
import { $entityMasks, resetGlobals, getDefaultSize } from '../../src/Entity.js' | ||
import { $dirtyQueries, $queries, $queryMap } from '../../src/Query.js' | ||
@@ -5,0 +5,0 @@ import { createWorld, $size, $bitflag } from '../../src/World.js' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
318871
23
2843
11
41