Networking focused entity component system (ECS) for TypeScript.
- 0 dependencies
- Optimized for networking with serialization support
- First class TypeScript support
- Cached queries
- Typed components with ArrayBuffer support
- Enter/Exit query support
- Change detection
npm install bytenecs
import {
createWorld,
defineComponent,
addEntity,
addComponent,
Types,
} from "bytenecs";
// Create a world where entities are stored and queries are run
const world = createWorld("game");
// Define components, the first argument is the name of the component which is used for serialization
const Position = defineComponent("position", {
x: Types.f32,
y: Types.f32,
});
const Velocity = defineComponent("velocity", {
x: Types.f32,
y: Types.f32,
});
// Register components
registerComponents(world, [Position, Velocity]);
// Create an entity
const player = addEntity(world);
// Add registered components to entity
addComponent(world, Position, player);
addComponent(world, Velocity, player);
// Set component values
Position.x[player] = 100;
Position.y[player] = 200;
Velocity.x[player] = 5;
Velocity.y[player] = 10;
Components are defined using defineComponent
with a schema that specifies the type of each field:
const Position = defineComponent("position", {
x: Types.f32,
y: Types.f32,
});
// Array types are also supported
const Inventory = defineComponent("inventory", {
items: [Types.ui8, 10], // Array of 10 uint8 values
});
Available types:
i8
,i16
,i32
- Signed integersui8
,ui16
,ui32
- Unsigned integersf32
,f64
- Floating point numbers
Query entities based on their components:
import { defineQuery, Not, Changed } from "bytenecs";
// Query entities with both Position and Velocity
const movingEntities = defineQuery(Position, Velocity);
// Query entities with Position but without Velocity
const staticEntities = defineQuery(Position, Not(Velocity));
// Query entities with changed Position values
const changedPositions = defineQuery(Changed(Position));
// Use queries
const entities = movingEntities(world);
Track when entities enter or leave a query:
const query = defineQuery(Position, Velocity);
const enterQuery = enterQuery(query);
const exitQuery = exitQuery(query);
function gameLoop() {
const newEntities = enterQuery(world); // Entities that just matched the query
const removedEntities = exitQuery(world); // Entities that no longer match
const currentEntities = query(world); // All current matching entities
}
Serialize and deserialize entities for networking:
// Serialize a single entity
const data = serializeEntity(world, entityId);
// Deserialize entity data
deserializeEntity(world, data);
// Create custom serializers for specific components
const serializePosition = defineSerializer([Position]);
const deserializePosition = defineDeserializer([Position]);
// Serialize multiple entities
const data = serializePosition(world, entities);
deserializePosition(world, data); // this will deserialize the entities into the world
const serialized = serialize(world, entity);
const deserialized = deserialize(world, serialized);
Systems are pure functions that operate on entities:
import { defineSystem } from "bytenecs";
const movementSystem = defineSystem((state) => {
const entities = movingEntities(world);
for (const eid of entities) {
Position.x[eid] += Velocity.x[eid];
Position.y[eid] += Velocity.y[eid];
}
return state;
});
- Uses TypedArrays for component storage
- Query caching
- Optimized for bulk operations
- Maximum 3000 entities per world
MIT