@typeonce/ecs
Version:
Entity Component System (ECS) implementation in TypeScript, extensible, working with any renderer, type safe and composable
131 lines (127 loc) • 5.91 kB
text/typescript
type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
declare const EntityIdTypeId: unique symbol;
type EntityId = number & {
readonly [EntityIdTypeId]: {
readonly EntityId: "EntityId";
};
};
interface ComponentType {
readonly _tag: string;
}
interface ComponentClass<T extends ComponentType> {
new (...args: any[]): Readonly<T>;
readonly _tag: string;
}
type ComponentClassMap = Record<string, ComponentClass<any>>;
type ComponentInstanceMap<T extends ComponentClassMap> = {
[K in keyof T]: InstanceType<T[K]>;
};
type EventMap = {
[K: symbol]: any;
};
type EventData<E extends EventMap, ET extends EventType<E>> = E[ET];
type EventType<E extends EventMap> = keyof E & symbol;
interface Event<E extends EventMap, ET extends EventType<E>> {
type: ET;
data: EventData<E, ET>;
}
type EventEmit<E extends EventMap> = <ET extends EventType<E>>(event: Event<E, ET>) => void;
type EventPoll<E extends EventMap> = <ET extends EventType<E>>(eventType: ET) => Event<E, ET>[];
type GetComponentRequired<Tag extends string, E extends EventMap> = (world: World<Tag, E>) => <M extends ComponentClassMap>(componentMap: M) => (entityId: EntityId) => {
entityId: EntityId;
} & ComponentInstanceMap<M>;
type GetComponent<Tag extends string, E extends EventMap> = (world: World<Tag, E>) => <M extends ComponentClassMap>(componentMap: M) => (entityId: EntityId) => ({
entityId: EntityId;
} & ComponentInstanceMap<M>) | undefined;
type AddComponent<Tag extends string, E extends EventMap> = (world: World<Tag, E>) => <T extends ComponentType>(entityId: EntityId, ...components: NoInfer<T>[]) => void;
type RemoveComponent<Tag extends string, E extends EventMap> = (world: World<Tag, E>) => <T extends ComponentType>(entityId: EntityId, componentClass: ComponentClass<T>) => void;
type AddSystem<Tag extends string, E extends EventMap> = (world: World<Tag, E>) => (...systems: SystemType<Tag, E, any>[]) => void;
type InitFunctions<Tag extends string, E extends EventMap> = {
addComponent: ReturnType<AddComponent<Tag, E>>;
createEntity: () => EntityId;
addSystem: ReturnType<AddSystem<Tag, E>>;
};
type SystemFunctions<Tag extends string, E extends EventMap> = InitFunctions<Tag, E> & {
world: World<Tag, E>;
deltaTime: number;
getComponentRequired: ReturnType<GetComponentRequired<Tag, E>>;
getComponent: ReturnType<GetComponent<Tag, E>>;
removeComponent: ReturnType<RemoveComponent<Tag, E>>;
destroyEntity: (entityId: EntityId) => void;
};
type SystemExecute<Tag extends string, E extends EventMap> = InitFunctions<Tag, E> & SystemFunctions<Tag, E> & {
emit: EventEmit<E>;
poll: EventPoll<E>;
};
type SystemType<Tag extends string, E extends EventMap, Exe extends SystemExecute<Tag, E>> = {
readonly _tag: Tag;
readonly dependencies: Tag[];
readonly execute: (_: Exe) => void;
};
type AnySystem<Tag extends string, E extends EventMap> = SystemType<Tag, E, any>;
type Mutation = {
type: "addComponent";
entityId: EntityId;
component: ComponentType;
} | {
type: "removeComponent";
entityId: EntityId;
component: ComponentType;
} | {
type: "destroyEntity";
entityId: EntityId;
};
interface World<Tag extends string, E extends EventMap> {
entities: Set<EntityId>;
components: Map<EntityId, Map<string, ComponentType>>;
nextEntityId: EntityId;
registry: SystemRegistry<Tag, E>;
update(deltaTime: number): void;
}
declare class SystemRegistry<Tags extends string, E extends EventMap = {}> {
private systems;
private dependencies;
registerSystem(system: AnySystem<Tags, E>): void;
private resolveExecutionOrder;
execute(params: SystemExecute<Tags, E>): void;
}
declare const Component: <Tag extends string>(tag: Tag) => {
new <A extends Record<string, any> = {}>(args: Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }): {
readonly _tag: Tag;
} & A;
readonly _tag: Tag;
};
declare const System: <Tags extends string, E extends EventMap = {}>() => <A extends Record<string, any> = {}>(tag: Tags, params: {
execute: (_: SystemExecute<Tags, E> & {
input: A;
}) => void;
dependencies?: Tags[];
}) => {
new (args: Equals<A, {}> extends true ? void : {
readonly [P in keyof A as P extends "_tag" ? never : P]: A[P];
}): {
readonly _tag: Tags;
readonly execute: (_: SystemExecute<Tags, E> & {
input: A;
}) => void;
readonly dependencies: Tags[];
} & A;
};
declare const query: <M extends ComponentClassMap, T extends ComponentType>(componentMap: M, notComponents?: ComponentClass<NoInfer<T>>[]) => <Tag extends string, E extends EventMap>(world: World<Tag, E>) => ({
entityId: EntityId;
} & ComponentInstanceMap<M>)[];
declare const queryRequired: <M extends ComponentClassMap>(componentMap: M) => <Tag extends string, E extends EventMap>(world: World<Tag, E>) => [{
entityId: EntityId;
} & ComponentInstanceMap<M>, ...({
entityId: EntityId;
} & ComponentInstanceMap<M>)[]];
declare class ECS<Tags extends string = string, E extends EventMap = {}> implements World<Tags, E> {
static create<Tags extends string, E extends EventMap = {}>(init: (_: InitFunctions<Tags, E>) => void): World<Tags, E>;
registry: SystemRegistry<Tags, E>;
entities: Set<EntityId>;
components: Map<EntityId, Map<string, ComponentType>>;
nextEntityId: EntityId;
update(deltaTime: number): void;
private applyMutations;
}
export { type AnySystem, Component, type ComponentClass, type ComponentClassMap, type ComponentInstanceMap, type ComponentType, ECS, type EntityId, type Equals, type Event, type EventMap, type EventType, type InitFunctions, type Mutation, System, type SystemExecute, type SystemType, type World, query, queryRequired };