对query/entity进行安全类型扩展
This commit is contained in:
404
packages/core/src/ECS/Core/Query/TypedQuery.ts
Normal file
404
packages/core/src/ECS/Core/Query/TypedQuery.ts
Normal file
@@ -0,0 +1,404 @@
|
||||
/**
|
||||
* 类型安全的Query查询系统
|
||||
*
|
||||
* 提供完整的TypeScript类型推断,在编译时确保类型安全
|
||||
*/
|
||||
|
||||
import type { Entity } from '../../Entity';
|
||||
import type { ComponentConstructor, ComponentInstance, ComponentTypeMap } from '../../../Types/TypeHelpers';
|
||||
import { Matcher, type QueryCondition } from '../../Utils/Matcher';
|
||||
|
||||
/**
|
||||
* 类型安全的查询结果
|
||||
*
|
||||
* 根据查询条件自动推断实体必定拥有的组件类型
|
||||
*/
|
||||
export class TypedQueryResult<TAll extends readonly ComponentConstructor[]> {
|
||||
private _entities: readonly Entity[];
|
||||
private _componentTypes: TAll;
|
||||
|
||||
constructor(entities: readonly Entity[], componentTypes: TAll) {
|
||||
this._entities = entities;
|
||||
this._componentTypes = componentTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体列表
|
||||
*/
|
||||
get entities(): readonly Entity[] {
|
||||
return this._entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体数量
|
||||
*/
|
||||
get length(): number {
|
||||
return this._entities.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历所有实体
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* query.forEach((entity) => {
|
||||
* // entity.getComponent返回类型自动推断
|
||||
* const pos = entity.getComponent(Position); // Position类型
|
||||
* const vel = entity.getComponent(Velocity); // Velocity类型
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
forEach(callback: (entity: Entity, index: number) => void): void {
|
||||
this._entities.forEach(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射转换实体
|
||||
*/
|
||||
map<R>(callback: (entity: Entity, index: number) => R): R[] {
|
||||
return this._entities.map(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤实体
|
||||
*/
|
||||
filter(predicate: (entity: Entity, index: number) => boolean): TypedQueryResult<TAll> {
|
||||
return new TypedQueryResult(this._entities.filter(predicate), this._componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找第一个匹配的实体
|
||||
*/
|
||||
find(predicate: (entity: Entity, index: number) => boolean): Entity | undefined {
|
||||
return this._entities.find(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在匹配的实体
|
||||
*/
|
||||
some(predicate: (entity: Entity, index: number) => boolean): boolean {
|
||||
return this._entities.some(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否所有实体都匹配
|
||||
*/
|
||||
every(predicate: (entity: Entity, index: number) => boolean): boolean {
|
||||
return this._entities.every(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定索引的实体
|
||||
*/
|
||||
get(index: number): Entity | undefined {
|
||||
return this._entities[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第一个实体
|
||||
*/
|
||||
get first(): Entity | undefined {
|
||||
return this._entities[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后一个实体
|
||||
*/
|
||||
get last(): Entity | undefined {
|
||||
return this._entities[this._entities.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查查询结果是否为空
|
||||
*/
|
||||
get isEmpty(): boolean {
|
||||
return this._entities.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为数组
|
||||
*/
|
||||
toArray(): Entity[] {
|
||||
return [...this._entities];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件类型信息(用于调试)
|
||||
*/
|
||||
getComponentTypes(): readonly ComponentConstructor[] {
|
||||
return this._componentTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代器支持
|
||||
*/
|
||||
[Symbol.iterator](): Iterator<Entity> {
|
||||
return this._entities[Symbol.iterator]();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型安全的查询构建器
|
||||
*
|
||||
* 支持链式调用,自动推断查询结果的类型
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 基础查询
|
||||
* const query = new TypedQueryBuilder()
|
||||
* .withAll(Position, Velocity)
|
||||
* .build();
|
||||
*
|
||||
* // 复杂查询
|
||||
* const complexQuery = new TypedQueryBuilder()
|
||||
* .withAll(Transform, Renderer)
|
||||
* .withAny(BoxCollider, CircleCollider)
|
||||
* .withNone(Disabled)
|
||||
* .withTag(EntityTags.Enemy)
|
||||
* .build();
|
||||
* ```
|
||||
*/
|
||||
export class TypedQueryBuilder<
|
||||
TAll extends readonly ComponentConstructor[] = [],
|
||||
TAny extends readonly ComponentConstructor[] = [],
|
||||
TNone extends readonly ComponentConstructor[] = []
|
||||
> {
|
||||
private _all: TAll;
|
||||
private _any: TAny;
|
||||
private _none: TNone;
|
||||
private _tag?: number;
|
||||
private _name?: string;
|
||||
|
||||
constructor(
|
||||
all?: TAll,
|
||||
any?: TAny,
|
||||
none?: TNone,
|
||||
tag?: number,
|
||||
name?: string
|
||||
) {
|
||||
this._all = (all || []) as TAll;
|
||||
this._any = (any || []) as TAny;
|
||||
this._none = (none || []) as TNone;
|
||||
this._tag = tag;
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 要求实体拥有所有指定的组件
|
||||
*
|
||||
* @param types 组件类型
|
||||
* @returns 新的查询构建器,类型参数更新
|
||||
*/
|
||||
withAll<TNewAll extends readonly ComponentConstructor[]>(
|
||||
...types: TNewAll
|
||||
): TypedQueryBuilder<
|
||||
readonly [...TAll, ...TNewAll],
|
||||
TAny,
|
||||
TNone
|
||||
> {
|
||||
return new TypedQueryBuilder(
|
||||
[...this._all, ...types] as readonly [...TAll, ...TNewAll],
|
||||
this._any,
|
||||
this._none,
|
||||
this._tag,
|
||||
this._name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 要求实体至少拥有一个指定的组件
|
||||
*
|
||||
* @param types 组件类型
|
||||
* @returns 新的查询构建器
|
||||
*/
|
||||
withAny<TNewAny extends readonly ComponentConstructor[]>(
|
||||
...types: TNewAny
|
||||
): TypedQueryBuilder<
|
||||
TAll,
|
||||
readonly [...TAny, ...TNewAny],
|
||||
TNone
|
||||
> {
|
||||
return new TypedQueryBuilder(
|
||||
this._all,
|
||||
[...this._any, ...types] as readonly [...TAny, ...TNewAny],
|
||||
this._none,
|
||||
this._tag,
|
||||
this._name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 排除拥有指定组件的实体
|
||||
*
|
||||
* @param types 组件类型
|
||||
* @returns 新的查询构建器
|
||||
*/
|
||||
withNone<TNewNone extends readonly ComponentConstructor[]>(
|
||||
...types: TNewNone
|
||||
): TypedQueryBuilder<
|
||||
TAll,
|
||||
TAny,
|
||||
readonly [...TNone, ...TNewNone]
|
||||
> {
|
||||
return new TypedQueryBuilder(
|
||||
this._all,
|
||||
this._any,
|
||||
[...this._none, ...types] as readonly [...TNone, ...TNewNone],
|
||||
this._tag,
|
||||
this._name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按标签过滤实体
|
||||
*
|
||||
* @param tag 标签值
|
||||
* @returns 新的查询构建器
|
||||
*/
|
||||
withTag(tag: number): TypedQueryBuilder<TAll, TAny, TNone> {
|
||||
return new TypedQueryBuilder(
|
||||
this._all,
|
||||
this._any,
|
||||
this._none,
|
||||
tag,
|
||||
this._name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按名称过滤实体
|
||||
*
|
||||
* @param name 实体名称
|
||||
* @returns 新的查询构建器
|
||||
*/
|
||||
withName(name: string): TypedQueryBuilder<TAll, TAny, TNone> {
|
||||
return new TypedQueryBuilder(
|
||||
this._all,
|
||||
this._any,
|
||||
this._none,
|
||||
this._tag,
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Matcher对象
|
||||
*
|
||||
* @returns Matcher实例,用于传统查询API
|
||||
*/
|
||||
buildMatcher(): Matcher {
|
||||
let matcher = Matcher.complex();
|
||||
|
||||
if (this._all.length > 0) {
|
||||
matcher = matcher.all(...(this._all as unknown as ComponentConstructor[]));
|
||||
}
|
||||
|
||||
if (this._any.length > 0) {
|
||||
matcher = matcher.any(...(this._any as unknown as ComponentConstructor[]));
|
||||
}
|
||||
|
||||
if (this._none.length > 0) {
|
||||
matcher = matcher.none(...(this._none as unknown as ComponentConstructor[]));
|
||||
}
|
||||
|
||||
if (this._tag !== undefined) {
|
||||
matcher = matcher.withTag(this._tag);
|
||||
}
|
||||
|
||||
if (this._name !== undefined) {
|
||||
matcher = matcher.withName(this._name);
|
||||
}
|
||||
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询条件
|
||||
*
|
||||
* @returns 查询条件对象
|
||||
*/
|
||||
getCondition(): QueryCondition {
|
||||
return {
|
||||
all: [...this._all] as ComponentConstructor[],
|
||||
any: [...this._any] as ComponentConstructor[],
|
||||
none: [...this._none] as ComponentConstructor[],
|
||||
tag: this._tag,
|
||||
name: this._name
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取required组件类型(用于类型推断)
|
||||
*/
|
||||
getRequiredTypes(): TAll {
|
||||
return this._all;
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆查询构建器
|
||||
*/
|
||||
clone(): TypedQueryBuilder<TAll, TAny, TNone> {
|
||||
return new TypedQueryBuilder(
|
||||
[...this._all] as unknown as TAll,
|
||||
[...this._any] as unknown as TAny,
|
||||
[...this._none] as unknown as TNone,
|
||||
this._tag,
|
||||
this._name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建类型安全的查询构建器
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const query = createQuery()
|
||||
* .withAll(Position, Velocity)
|
||||
* .withNone(Disabled);
|
||||
*
|
||||
* // 在System或Scene中使用
|
||||
* const entities = scene.query(query);
|
||||
* entities.forEach(entity => {
|
||||
* const pos = entity.getComponent(Position); // 自动推断为Position
|
||||
* const vel = entity.getComponent(Velocity); // 自动推断为Velocity
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function createQuery(): TypedQueryBuilder<[], [], []> {
|
||||
return new TypedQueryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建单组件查询的便捷方法
|
||||
*
|
||||
* @param componentType 组件类型
|
||||
* @returns 查询构建器
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const healthEntities = queryFor(HealthComponent);
|
||||
* ```
|
||||
*/
|
||||
export function queryFor<T extends ComponentConstructor>(
|
||||
componentType: T
|
||||
): TypedQueryBuilder<readonly [T], [], []> {
|
||||
return new TypedQueryBuilder([componentType] as readonly [T]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建多组件查询的便捷方法
|
||||
*
|
||||
* @param types 组件类型数组
|
||||
* @returns 查询构建器
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const movableEntities = queryForAll(Position, Velocity);
|
||||
* ```
|
||||
*/
|
||||
export function queryForAll<T extends readonly ComponentConstructor[]>(
|
||||
...types: T
|
||||
): TypedQueryBuilder<T, [], []> {
|
||||
return new TypedQueryBuilder(types);
|
||||
}
|
||||
@@ -346,13 +346,19 @@ export class Entity {
|
||||
|
||||
/**
|
||||
* 创建并添加组件
|
||||
*
|
||||
* @param componentType - 组件类型
|
||||
*
|
||||
* @param componentType - 组件类型构造函数
|
||||
* @param args - 组件构造函数参数
|
||||
* @returns 创建的组件实例
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const position = entity.createComponent(Position, 100, 200);
|
||||
* const health = entity.createComponent(Health, 100);
|
||||
* ```
|
||||
*/
|
||||
public createComponent<T extends Component>(
|
||||
componentType: ComponentType<T>,
|
||||
componentType: ComponentType<T>,
|
||||
...args: any[]
|
||||
): T {
|
||||
const component = new componentType(...args);
|
||||
@@ -387,10 +393,16 @@ export class Entity {
|
||||
|
||||
/**
|
||||
* 添加组件到实体
|
||||
*
|
||||
*
|
||||
* @param component - 要添加的组件实例
|
||||
* @returns 添加的组件实例
|
||||
* @throws {Error} 如果组件类型已存在
|
||||
* @throws {Error} 如果实体已存在该类型的组件
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const position = new Position(100, 200);
|
||||
* entity.addComponent(position);
|
||||
* ```
|
||||
*/
|
||||
public addComponent<T extends Component>(component: T): T {
|
||||
const componentType = component.constructor as ComponentType<T>;
|
||||
@@ -429,8 +441,17 @@ export class Entity {
|
||||
/**
|
||||
* 获取指定类型的组件
|
||||
*
|
||||
* @param type - 组件类型
|
||||
* @returns 组件实例或null
|
||||
* @param type - 组件类型构造函数
|
||||
* @returns 组件实例,如果不存在则返回null
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const position = entity.getComponent(Position);
|
||||
* if (position) {
|
||||
* position.x += 10;
|
||||
* position.y += 20;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public getComponent<T extends Component>(type: ComponentType<T>): T | null {
|
||||
// 快速检查:位掩码
|
||||
@@ -442,7 +463,7 @@ export class Entity {
|
||||
if (this.scene?.componentStorageManager) {
|
||||
const component = this.scene.componentStorageManager.getComponent(this.id, type);
|
||||
if (component) {
|
||||
return component;
|
||||
return component as T;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,10 +475,18 @@ export class Entity {
|
||||
|
||||
|
||||
/**
|
||||
* 检查实体是否有指定类型的组件
|
||||
*
|
||||
* @param type - 组件类型
|
||||
* @returns 如果有该组件则返回true
|
||||
* 检查实体是否拥有指定类型的组件
|
||||
*
|
||||
* @param type - 组件类型构造函数
|
||||
* @returns 如果实体拥有该组件返回true,否则返回false
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* if (entity.hasComponent(Position)) {
|
||||
* const position = entity.getComponent(Position)!;
|
||||
* position.x += 10;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public hasComponent<T extends Component>(type: ComponentType<T>): boolean {
|
||||
if (!ComponentRegistry.isRegistered(type)) {
|
||||
@@ -470,13 +499,22 @@ export class Entity {
|
||||
|
||||
/**
|
||||
* 获取或创建指定类型的组件
|
||||
*
|
||||
* @param type - 组件类型
|
||||
* @param args - 组件构造函数参数(仅在创建时使用)
|
||||
*
|
||||
* 如果组件已存在则返回现有组件,否则创建新组件并添加到实体
|
||||
*
|
||||
* @param type - 组件类型构造函数
|
||||
* @param args - 组件构造函数参数(仅在创建新组件时使用)
|
||||
* @returns 组件实例
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 确保实体拥有Position组件
|
||||
* const position = entity.getOrCreateComponent(Position, 0, 0);
|
||||
* position.x = 100;
|
||||
* ```
|
||||
*/
|
||||
public getOrCreateComponent<T extends Component>(
|
||||
type: ComponentType<T>,
|
||||
type: ComponentType<T>,
|
||||
...args: any[]
|
||||
): T {
|
||||
let component = this.getComponent(type);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||
import { EventBus } from './Core/EventBus';
|
||||
import { IScene, ISceneConfig } from './IScene';
|
||||
import { getComponentInstanceTypeName, getSystemInstanceTypeName } from './Decorators';
|
||||
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
||||
|
||||
/**
|
||||
* 游戏场景默认实现类
|
||||
@@ -319,6 +320,70 @@ export class Scene implements IScene {
|
||||
return this.findEntitiesByTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询拥有所有指定组件的实体
|
||||
*
|
||||
* @param componentTypes - 组件类型数组
|
||||
* @returns 查询结果
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const result = scene.queryAll(Position, Velocity);
|
||||
* for (const entity of result.entities) {
|
||||
* const pos = entity.getComponent(Position);
|
||||
* const vel = entity.getComponent(Velocity);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public queryAll(...componentTypes: any[]): { entities: readonly Entity[] } {
|
||||
return this.querySystem.queryAll(...componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询拥有任意一个指定组件的实体
|
||||
*
|
||||
* @param componentTypes - 组件类型数组
|
||||
* @returns 查询结果
|
||||
*/
|
||||
public queryAny(...componentTypes: any[]): { entities: readonly Entity[] } {
|
||||
return this.querySystem.queryAny(...componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询不包含指定组件的实体
|
||||
*
|
||||
* @param componentTypes - 组件类型数组
|
||||
* @returns 查询结果
|
||||
*/
|
||||
public queryNone(...componentTypes: any[]): { entities: readonly Entity[] } {
|
||||
return this.querySystem.queryNone(...componentTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建类型安全的查询构建器
|
||||
*
|
||||
* @returns 查询构建器,支持链式调用
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 使用查询构建器
|
||||
* const matcher = scene.query()
|
||||
* .withAll(Position, Velocity)
|
||||
* .withNone(Disabled)
|
||||
* .buildMatcher();
|
||||
*
|
||||
* // 在System中使用
|
||||
* class MovementSystem extends EntitySystem {
|
||||
* constructor() {
|
||||
* super(matcher);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public query(): TypedQueryBuilder {
|
||||
return new TypedQueryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在场景中添加一个EntitySystem处理器
|
||||
* @param processor 处理器
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { QuerySystem } from '../Core/QuerySystem';
|
||||
import { getSystemInstanceTypeName } from '../Decorators';
|
||||
import { createLogger } from '../../Utils/Logger';
|
||||
import type { EventListenerConfig, TypeSafeEventSystem, EventHandler } from '../Core/EventSystem';
|
||||
import type { ComponentConstructor, ComponentInstance } from '../../Types/TypeHelpers';
|
||||
|
||||
/**
|
||||
* 事件监听器记录
|
||||
@@ -21,17 +22,22 @@ interface EventListenerRecord {
|
||||
|
||||
/**
|
||||
* 实体系统的基类
|
||||
*
|
||||
*
|
||||
* 用于处理一组符合特定条件的实体。系统是ECS架构中的逻辑处理单元,
|
||||
* 负责对拥有特定组件组合的实体执行业务逻辑。
|
||||
*
|
||||
*
|
||||
* 支持泛型参数以提供类型安全的组件访问:
|
||||
*
|
||||
* @template TComponents - 系统需要的组件类型数组
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 传统方式
|
||||
* class MovementSystem extends EntitySystem {
|
||||
* constructor() {
|
||||
* super(Transform, Velocity);
|
||||
* super(Matcher.of(Transform, Velocity));
|
||||
* }
|
||||
*
|
||||
*
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* for (const entity of entities) {
|
||||
* const transform = entity.getComponent(Transform);
|
||||
@@ -40,9 +46,26 @@ interface EventListenerRecord {
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // 类型安全方式
|
||||
* class MovementSystem extends EntitySystem<[typeof Transform, typeof Velocity]> {
|
||||
* constructor() {
|
||||
* super(Matcher.of(Transform, Velocity));
|
||||
* }
|
||||
*
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* for (const entity of entities) {
|
||||
* // 类型安全的组件访问
|
||||
* const [transform, velocity] = this.getComponents(entity);
|
||||
* transform.position.add(velocity.value);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export abstract class EntitySystem implements ISystemBase {
|
||||
export abstract class EntitySystem<
|
||||
TComponents extends readonly ComponentConstructor[] = []
|
||||
> implements ISystemBase {
|
||||
private _updateOrder: number;
|
||||
private _enabled: boolean;
|
||||
private _performanceMonitor: PerformanceMonitor;
|
||||
@@ -793,4 +816,235 @@ export abstract class EntitySystem implements ISystemBase {
|
||||
protected onDestroy(): void {
|
||||
// 子类可以重写此方法进行清理操作
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 类型安全的辅助方法
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* 类型安全地获取单个组件
|
||||
*
|
||||
* 相比Entity.getComponent,此方法保证返回非空值,
|
||||
* 如果组件不存在会抛出错误而不是返回null
|
||||
*
|
||||
* @param entity 实体
|
||||
* @param componentType 组件类型
|
||||
* @returns 组件实例(保证非空)
|
||||
* @throws 如果组件不存在则抛出错误
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* for (const entity of entities) {
|
||||
* const transform = this.requireComponent(entity, Transform);
|
||||
* // transform 保证非空,类型为 Transform
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected requireComponent<T extends ComponentConstructor>(
|
||||
entity: Entity,
|
||||
componentType: T
|
||||
): ComponentInstance<T> {
|
||||
const component = entity.getComponent(componentType as any);
|
||||
if (!component) {
|
||||
throw new Error(
|
||||
`Component ${componentType.name} not found on entity ${entity.name} in ${this.systemName}`
|
||||
);
|
||||
}
|
||||
return component as ComponentInstance<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取实体的所有必需组件
|
||||
*
|
||||
* 根据泛型参数TComponents推断返回类型,
|
||||
* 返回一个元组,包含所有组件实例
|
||||
*
|
||||
* @param entity 实体
|
||||
* @param components 组件类型数组
|
||||
* @returns 组件实例元组
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class MySystem extends EntitySystem<[typeof Position, typeof Velocity]> {
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* for (const entity of entities) {
|
||||
* const [pos, vel] = this.getComponents(entity, Position, Velocity);
|
||||
* // pos: Position, vel: Velocity (自动类型推断)
|
||||
* pos.x += vel.x;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected getComponents<T extends readonly ComponentConstructor[]>(
|
||||
entity: Entity,
|
||||
...components: T
|
||||
): { [K in keyof T]: ComponentInstance<T[K]> } {
|
||||
return components.map((type) =>
|
||||
this.requireComponent(entity, type)
|
||||
) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历实体并处理每个实体
|
||||
*
|
||||
* 提供更简洁的语法糖,避免手动遍历
|
||||
*
|
||||
* @param entities 实体列表
|
||||
* @param processor 处理函数
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* this.forEach(entities, (entity) => {
|
||||
* const transform = this.requireComponent(entity, Transform);
|
||||
* transform.position.y -= 9.8 * Time.deltaTime;
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected forEach(
|
||||
entities: readonly Entity[],
|
||||
processor: (entity: Entity, index: number) => void
|
||||
): void {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
processor(entities[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤实体
|
||||
*
|
||||
* @param entities 实体列表
|
||||
* @param predicate 过滤条件
|
||||
* @returns 过滤后的实体数组
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* const activeEntities = this.filterEntities(entities, (entity) => {
|
||||
* const health = this.requireComponent(entity, Health);
|
||||
* return health.value > 0;
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected filterEntities(
|
||||
entities: readonly Entity[],
|
||||
predicate: (entity: Entity, index: number) => boolean
|
||||
): Entity[] {
|
||||
return Array.from(entities).filter(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射实体到另一种类型
|
||||
*
|
||||
* @param entities 实体列表
|
||||
* @param mapper 映射函数
|
||||
* @returns 映射后的结果数组
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* const positions = this.mapEntities(entities, (entity) => {
|
||||
* const transform = this.requireComponent(entity, Transform);
|
||||
* return transform.position;
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected mapEntities<R>(
|
||||
entities: readonly Entity[],
|
||||
mapper: (entity: Entity, index: number) => R
|
||||
): R[] {
|
||||
return Array.from(entities).map(mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找第一个满足条件的实体
|
||||
*
|
||||
* @param entities 实体列表
|
||||
* @param predicate 查找条件
|
||||
* @returns 第一个满足条件的实体,或undefined
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* const player = this.findEntity(entities, (entity) =>
|
||||
* entity.hasComponent(PlayerTag)
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected findEntity(
|
||||
entities: readonly Entity[],
|
||||
predicate: (entity: Entity, index: number) => boolean
|
||||
): Entity | undefined {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
if (predicate(entities[i], i)) {
|
||||
return entities[i];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在满足条件的实体
|
||||
*
|
||||
* @param entities 实体列表
|
||||
* @param predicate 检查条件
|
||||
* @returns 是否存在满足条件的实体
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* const hasLowHealth = this.someEntity(entities, (entity) => {
|
||||
* const health = this.requireComponent(entity, Health);
|
||||
* return health.value < 20;
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected someEntity(
|
||||
entities: readonly Entity[],
|
||||
predicate: (entity: Entity, index: number) => boolean
|
||||
): boolean {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
if (predicate(entities[i], i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否所有实体都满足条件
|
||||
*
|
||||
* @param entities 实体列表
|
||||
* @param predicate 检查条件
|
||||
* @returns 是否所有实体都满足条件
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* protected process(entities: readonly Entity[]): void {
|
||||
* const allHealthy = this.everyEntity(entities, (entity) => {
|
||||
* const health = this.requireComponent(entity, Health);
|
||||
* return health.value > 50;
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected everyEntity(
|
||||
entities: readonly Entity[],
|
||||
predicate: (entity: Entity, index: number) => boolean
|
||||
): boolean {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
if (!predicate(entities[i], i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
307
packages/core/src/ECS/TypedEntity.ts
Normal file
307
packages/core/src/ECS/TypedEntity.ts
Normal file
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* Entity类型安全工具函数
|
||||
*
|
||||
* 提供类型安全的组件操作工具函数,无需修改Entity类
|
||||
*/
|
||||
|
||||
import { Entity } from './Entity';
|
||||
import type { Component } from './Component';
|
||||
import type { ComponentType } from './Core/ComponentStorage';
|
||||
import type { ComponentConstructor, ComponentInstance } from '../Types/TypeHelpers';
|
||||
|
||||
/**
|
||||
* 获取组件,如果不存在则抛出错误
|
||||
*
|
||||
* @param entity - 实体实例
|
||||
* @param componentType - 组件类型构造函数
|
||||
* @returns 组件实例(保证非空)
|
||||
* @throws {Error} 如果组件不存在
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const position = requireComponent(entity, Position);
|
||||
* position.x += 10;
|
||||
* ```
|
||||
*/
|
||||
export function requireComponent<T extends ComponentConstructor>(
|
||||
entity: Entity,
|
||||
componentType: T
|
||||
): ComponentInstance<T> {
|
||||
const component = entity.getComponent(componentType as unknown as ComponentType<ComponentInstance<T>>);
|
||||
if (!component) {
|
||||
throw new Error(
|
||||
`Component ${componentType.name} not found on entity ${entity.name} (id: ${entity.id})`
|
||||
);
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取组件
|
||||
*
|
||||
* @param entity - 实体实例
|
||||
* @param componentType - 组件类型构造函数
|
||||
* @returns 组件实例或undefined
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const health = tryGetComponent(entity, Health);
|
||||
* if (health) {
|
||||
* health.value -= 10;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function tryGetComponent<T extends ComponentConstructor>(
|
||||
entity: Entity,
|
||||
componentType: T
|
||||
): ComponentInstance<T> | undefined {
|
||||
const component = entity.getComponent(componentType as unknown as ComponentType<ComponentInstance<T>>);
|
||||
return component !== null ? component : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取组件
|
||||
*
|
||||
* @param entity - 实体实例
|
||||
* @param types - 组件类型构造函数数组
|
||||
* @returns 组件实例元组,每个元素可能为null
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const [pos, vel, health] = getComponents(entity, Position, Velocity, Health);
|
||||
* if (pos && vel && health) {
|
||||
* pos.x += vel.dx;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function getComponents<T extends readonly ComponentConstructor[]>(
|
||||
entity: Entity,
|
||||
...types: T
|
||||
): { [K in keyof T]: ComponentInstance<T[K]> | null } {
|
||||
return types.map((type) =>
|
||||
entity.getComponent(type as unknown as ComponentType<ComponentInstance<typeof type>>)
|
||||
) as { [K in keyof T]: ComponentInstance<T[K]> | null };
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否拥有所有指定的组件
|
||||
*
|
||||
* @param entity - 实体实例
|
||||
* @param types - 组件类型构造函数数组
|
||||
* @returns 如果拥有所有组件返回true,否则返回false
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* if (hasComponents(entity, Position, Velocity)) {
|
||||
* const pos = entity.getComponent(Position)!;
|
||||
* const vel = entity.getComponent(Velocity)!;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function hasComponents(entity: Entity, ...types: ComponentConstructor[]): boolean {
|
||||
return types.every((type) => entity.hasComponent(type as unknown as ComponentType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查实体是否拥有至少一个指定的组件
|
||||
*
|
||||
* @param entity - 实体实例
|
||||
* @param types - 组件类型构造函数数组
|
||||
* @returns 如果拥有任意一个组件返回true,否则返回false
|
||||
*/
|
||||
export function hasAnyComponent(entity: Entity, ...types: ComponentConstructor[]): boolean {
|
||||
return types.some((type) => entity.hasComponent(type as unknown as ComponentType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组件并立即配置
|
||||
*
|
||||
* @param entity - 实体实例
|
||||
* @param component - 组件实例
|
||||
* @param configure - 配置回调函数
|
||||
* @returns 实体实例(支持链式调用)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* addAndConfigure(entity, new Health(), health => {
|
||||
* health.maxValue = 100;
|
||||
* health.value = 50;
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function addAndConfigure<T extends Component>(
|
||||
entity: Entity,
|
||||
component: T,
|
||||
configure: (component: T) => void
|
||||
): Entity {
|
||||
entity.addComponent(component);
|
||||
configure(component);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或添加组件
|
||||
*
|
||||
* 如果组件已存在则返回现有组件,否则通过工厂函数创建并添加
|
||||
*
|
||||
* @param entity - 实体实例
|
||||
* @param componentType - 组件类型构造函数
|
||||
* @param factory - 组件工厂函数(仅在组件不存在时调用)
|
||||
* @returns 组件实例
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const health = getOrAddComponent(entity, Health, () => new Health(100));
|
||||
* ```
|
||||
*/
|
||||
export function getOrAddComponent<T extends ComponentConstructor>(
|
||||
entity: Entity,
|
||||
componentType: T,
|
||||
factory: () => ComponentInstance<T>
|
||||
): ComponentInstance<T> {
|
||||
let component = entity.getComponent(componentType as unknown as ComponentType<ComponentInstance<T>>);
|
||||
if (!component) {
|
||||
component = factory();
|
||||
entity.addComponent(component);
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新组件的部分字段
|
||||
*
|
||||
* @param entity - 实体实例
|
||||
* @param componentType - 组件类型构造函数
|
||||
* @param data - 要更新的部分数据
|
||||
* @returns 如果更新成功返回true,组件不存在返回false
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* updateComponent(entity, Position, { x: 100 });
|
||||
* ```
|
||||
*/
|
||||
export function updateComponent<T extends ComponentConstructor>(
|
||||
entity: Entity,
|
||||
componentType: T,
|
||||
data: Partial<ComponentInstance<T>>
|
||||
): boolean {
|
||||
const component = entity.getComponent(componentType as unknown as ComponentType<ComponentInstance<T>>);
|
||||
if (!component) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object.assign(component, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型安全的实体构建器
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const player = buildEntity(scene.createEntity("Player"))
|
||||
* .with(new Position(100, 100))
|
||||
* .with(new Velocity(0, 0))
|
||||
* .withTag(1)
|
||||
* .build();
|
||||
* ```
|
||||
*/
|
||||
export class TypedEntityBuilder {
|
||||
private _entity: Entity;
|
||||
|
||||
constructor(entity: Entity) {
|
||||
this._entity = entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组件
|
||||
*/
|
||||
with<T extends Component>(component: T): this {
|
||||
this._entity.addComponent(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加并配置组件
|
||||
*/
|
||||
withConfigured<T extends Component>(
|
||||
component: T,
|
||||
configure: (component: T) => void
|
||||
): this {
|
||||
this._entity.addComponent(component);
|
||||
configure(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标签
|
||||
*/
|
||||
withTag(tag: number): this {
|
||||
this._entity.tag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置名称
|
||||
*/
|
||||
withName(name: string): this {
|
||||
this._entity.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置激活状态
|
||||
*/
|
||||
withActive(active: boolean): this {
|
||||
this._entity.active = active;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置启用状态
|
||||
*/
|
||||
withEnabled(enabled: boolean): this {
|
||||
this._entity.enabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置更新顺序
|
||||
*/
|
||||
withUpdateOrder(order: number): this {
|
||||
this._entity.updateOrder = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加子实体
|
||||
*/
|
||||
withChild(child: Entity): this {
|
||||
this._entity.addChild(child);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成构建
|
||||
*/
|
||||
build(): Entity {
|
||||
return this._entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正在构建的实体
|
||||
*/
|
||||
get entity(): Entity {
|
||||
return this._entity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建类型安全的实体构建器
|
||||
*
|
||||
* @param entity - 要包装的实体
|
||||
* @returns 实体构建器
|
||||
*/
|
||||
export function buildEntity(entity: Entity): TypedEntityBuilder {
|
||||
return new TypedEntityBuilder(entity);
|
||||
}
|
||||
295
packages/core/src/Types/TypeHelpers.ts
Normal file
295
packages/core/src/Types/TypeHelpers.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
/**
|
||||
* TypeScript类型工具集
|
||||
*
|
||||
* 提供高级类型推断和类型安全的工具类型
|
||||
*/
|
||||
|
||||
import type { IComponent } from './index';
|
||||
import { Component } from '../ECS/Component';
|
||||
|
||||
/**
|
||||
* 组件类型提取器
|
||||
* 从组件构造函数中提取实例类型
|
||||
*/
|
||||
export type ComponentInstance<T> = T extends new (...args: any[]) => infer R ? R : never;
|
||||
|
||||
/**
|
||||
* 组件构造函数类型
|
||||
*
|
||||
* 与 ComponentType 保持一致,避免类型转换
|
||||
*/
|
||||
export type ComponentConstructor<T extends IComponent = IComponent> = new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* 组件类型的通用约束
|
||||
*
|
||||
* 用于确保类型参数是有效的组件构造函数
|
||||
*/
|
||||
export type AnyComponentConstructor = ComponentConstructor<any>;
|
||||
|
||||
/**
|
||||
* 多组件类型提取
|
||||
* 从组件构造函数数组中提取所有实例类型的联合
|
||||
*/
|
||||
export type ExtractComponents<T extends readonly ComponentConstructor[]> = {
|
||||
[K in keyof T]: ComponentInstance<T[K]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 组件类型映射
|
||||
* 将组件构造函数数组转换为实例类型的元组
|
||||
*/
|
||||
export type ComponentTypeMap<T extends readonly ComponentConstructor[]> = {
|
||||
[K in keyof T]: T[K] extends ComponentConstructor<infer C> ? C : never;
|
||||
};
|
||||
|
||||
/**
|
||||
* 实体with组件的类型
|
||||
* 表示一个实体确定拥有某些组件
|
||||
*/
|
||||
export interface EntityWithComponents<T extends readonly ComponentConstructor[]> {
|
||||
readonly id: number;
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* 类型安全的组件获取
|
||||
* 确保返回非空的组件实例
|
||||
*/
|
||||
getComponent<C extends ComponentConstructor>(componentType: C): ComponentInstance<C>;
|
||||
|
||||
/**
|
||||
* 检查是否拥有组件
|
||||
*/
|
||||
hasComponent<C extends ComponentConstructor>(componentType: C): boolean;
|
||||
|
||||
/**
|
||||
* 获取所有组件
|
||||
*/
|
||||
readonly components: ComponentTypeMap<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询结果类型
|
||||
* 根据查询条件推断实体必定拥有的组件
|
||||
*/
|
||||
export type QueryResult<
|
||||
All extends readonly ComponentConstructor[] = [],
|
||||
Any extends readonly ComponentConstructor[] = [],
|
||||
None extends readonly ComponentConstructor[] = []
|
||||
> = {
|
||||
/**
|
||||
* 实体列表,确保拥有All中的所有组件
|
||||
*/
|
||||
readonly entities: ReadonlyArray<EntityWithComponents<All>>;
|
||||
|
||||
/**
|
||||
* 实体数量
|
||||
*/
|
||||
readonly length: number;
|
||||
|
||||
/**
|
||||
* 遍历实体
|
||||
*/
|
||||
forEach(callback: (entity: EntityWithComponents<All>, index: number) => void): void;
|
||||
|
||||
/**
|
||||
* 映射转换
|
||||
*/
|
||||
map<R>(callback: (entity: EntityWithComponents<All>, index: number) => R): R[];
|
||||
|
||||
/**
|
||||
* 过滤实体
|
||||
*/
|
||||
filter(predicate: (entity: EntityWithComponents<All>, index: number) => boolean): QueryResult<All, Any, None>;
|
||||
};
|
||||
|
||||
/**
|
||||
* System处理的实体类型
|
||||
* 根据Matcher推断System处理的实体类型
|
||||
*/
|
||||
export type SystemEntityType<M> = M extends {
|
||||
getCondition(): {
|
||||
all: infer All extends readonly ComponentConstructor[];
|
||||
};
|
||||
}
|
||||
? EntityWithComponents<All>
|
||||
: never;
|
||||
|
||||
/**
|
||||
* 组件字段类型提取
|
||||
* 提取组件中所有可序列化的字段
|
||||
*/
|
||||
export type SerializableFields<T> = {
|
||||
[K in keyof T]: T[K] extends Function ? never : K;
|
||||
}[keyof T];
|
||||
|
||||
/**
|
||||
* 只读组件类型
|
||||
* 将组件的所有字段转为只读
|
||||
*/
|
||||
export type ReadonlyComponent<T extends IComponent> = {
|
||||
readonly [K in keyof T]: T[K];
|
||||
};
|
||||
|
||||
/**
|
||||
* 部分组件类型
|
||||
* 用于组件更新操作
|
||||
*/
|
||||
export type PartialComponent<T extends IComponent> = {
|
||||
[K in SerializableFields<T>]?: T[K];
|
||||
};
|
||||
|
||||
/**
|
||||
* 组件类型约束
|
||||
* 确保类型参数是有效的组件
|
||||
*/
|
||||
export type ValidComponent<T> = T extends Component ? T : never;
|
||||
|
||||
/**
|
||||
* 组件数组约束
|
||||
* 确保数组中的所有元素都是组件构造函数
|
||||
*/
|
||||
export type ValidComponentArray<T extends readonly any[]> = T extends readonly ComponentConstructor[]
|
||||
? T
|
||||
: never;
|
||||
|
||||
/**
|
||||
* 事件处理器类型
|
||||
* 提供类型安全的事件处理
|
||||
*/
|
||||
export type TypedEventHandler<T> = (data: T) => void | Promise<void>;
|
||||
|
||||
/**
|
||||
* 系统生命周期钩子类型
|
||||
*/
|
||||
export interface SystemLifecycleHooks<T extends readonly ComponentConstructor[]> {
|
||||
/**
|
||||
* 实体添加到系统时调用
|
||||
*/
|
||||
onAdded?: (entity: EntityWithComponents<T>) => void;
|
||||
|
||||
/**
|
||||
* 实体从系统移除时调用
|
||||
*/
|
||||
onRemoved?: (entity: EntityWithComponents<T>) => void;
|
||||
|
||||
/**
|
||||
* 系统初始化时调用
|
||||
*/
|
||||
onInitialize?: () => void;
|
||||
|
||||
/**
|
||||
* 系统销毁时调用
|
||||
*/
|
||||
onDestroy?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent API构建器类型
|
||||
*/
|
||||
export interface TypeSafeBuilder<T> {
|
||||
/**
|
||||
* 完成构建
|
||||
*/
|
||||
build(): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件池类型
|
||||
*/
|
||||
export interface ComponentPool<T extends IComponent> {
|
||||
/**
|
||||
* 从池中获取组件实例
|
||||
*/
|
||||
obtain(): T;
|
||||
|
||||
/**
|
||||
* 归还组件到池中
|
||||
*/
|
||||
free(component: T): void;
|
||||
|
||||
/**
|
||||
* 清空池
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
* 池中可用对象数量
|
||||
*/
|
||||
readonly available: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体查询条件类型
|
||||
*/
|
||||
export interface TypedQueryCondition<
|
||||
All extends readonly ComponentConstructor[] = [],
|
||||
Any extends readonly ComponentConstructor[] = [],
|
||||
None extends readonly ComponentConstructor[] = []
|
||||
> {
|
||||
all: All;
|
||||
any: Any;
|
||||
none: None;
|
||||
tag?: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件类型守卫
|
||||
*/
|
||||
export function isComponentType<T extends IComponent>(
|
||||
value: any
|
||||
): value is ComponentConstructor<T> {
|
||||
return typeof value === 'function' && value.prototype instanceof Component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型安全的组件数组守卫
|
||||
*/
|
||||
export function isComponentArray(
|
||||
value: any[]
|
||||
): value is ComponentConstructor[] {
|
||||
return value.every(isComponentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取组件类型名称(编译时)
|
||||
*/
|
||||
export type ComponentTypeName<T extends ComponentConstructor> = T extends {
|
||||
prototype: { constructor: { name: infer N } };
|
||||
}
|
||||
? N
|
||||
: string;
|
||||
|
||||
/**
|
||||
* 多组件类型名称联合
|
||||
*/
|
||||
export type ComponentTypeNames<T extends readonly ComponentConstructor[]> = {
|
||||
[K in keyof T]: ComponentTypeName<T[K]>;
|
||||
}[number];
|
||||
|
||||
/**
|
||||
* 深度只读类型
|
||||
*/
|
||||
export type DeepReadonly<T> = {
|
||||
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
|
||||
};
|
||||
|
||||
/**
|
||||
* 深度可选类型
|
||||
*/
|
||||
export type DeepPartial<T> = {
|
||||
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
||||
};
|
||||
|
||||
/**
|
||||
* 排除方法的类型
|
||||
*/
|
||||
export type DataOnly<T> = {
|
||||
[K in keyof T as T[K] extends Function ? never : K]: T[K];
|
||||
};
|
||||
|
||||
/**
|
||||
* 可序列化的组件数据
|
||||
*/
|
||||
export type SerializableComponent<T extends IComponent> = DeepPartial<DataOnly<T>>;
|
||||
@@ -2,6 +2,9 @@
|
||||
* 框架核心类型定义
|
||||
*/
|
||||
|
||||
// 导出TypeScript类型增强工具
|
||||
export * from './TypeHelpers';
|
||||
|
||||
/**
|
||||
* 组件接口
|
||||
*
|
||||
|
||||
@@ -27,6 +27,11 @@ export type { ILogger, LoggerConfig } from './Utils/Logger';
|
||||
// ECS核心组件
|
||||
export * from './ECS';
|
||||
|
||||
// TypeScript类型增强API
|
||||
export * from './ECS/TypedEntity';
|
||||
export * from './ECS/Systems/TypedEntitySystem';
|
||||
export * from './ECS/Core/Query/TypedQuery';
|
||||
|
||||
// 事件系统
|
||||
export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './ECS/CoreEvents';
|
||||
|
||||
|
||||
145
packages/core/tests/SceneQuery.test.ts
Normal file
145
packages/core/tests/SceneQuery.test.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Scene查询方法测试
|
||||
*/
|
||||
|
||||
import { Component } from '../src/ECS/Component';
|
||||
import { Entity } from '../src/ECS/Entity';
|
||||
import { Scene } from '../src/ECS/Scene';
|
||||
import { Core } from '../src/Core';
|
||||
import { ECSComponent } from '../src/ECS/Decorators';
|
||||
import { EntitySystem } from '../src/ECS/Systems/EntitySystem';
|
||||
|
||||
@ECSComponent('Position')
|
||||
class Position extends Component {
|
||||
constructor(public x: number = 0, public y: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
@ECSComponent('Velocity')
|
||||
class Velocity extends Component {
|
||||
constructor(public dx: number = 0, public dy: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
@ECSComponent('Disabled')
|
||||
class Disabled extends Component {}
|
||||
|
||||
describe('Scene查询方法', () => {
|
||||
let scene: Scene;
|
||||
|
||||
beforeEach(() => {
|
||||
Core.create({ debug: false, enableEntitySystems: true });
|
||||
scene = new Scene();
|
||||
scene.initialize();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
scene.end();
|
||||
});
|
||||
|
||||
describe('基础查询方法', () => {
|
||||
test('queryAll 查询拥有所有组件的实体', () => {
|
||||
const e1 = scene.createEntity('E1');
|
||||
e1.addComponent(new Position(10, 20));
|
||||
e1.addComponent(new Velocity(1, 2));
|
||||
|
||||
const e2 = scene.createEntity('E2');
|
||||
e2.addComponent(new Position(30, 40));
|
||||
|
||||
const result = scene.queryAll(Position, Velocity);
|
||||
|
||||
expect(result.entities).toHaveLength(1);
|
||||
expect(result.entities[0]).toBe(e1);
|
||||
});
|
||||
|
||||
test('queryAny 查询拥有任意组件的实体', () => {
|
||||
const e1 = scene.createEntity('E1');
|
||||
e1.addComponent(new Position(10, 20));
|
||||
|
||||
const e2 = scene.createEntity('E2');
|
||||
e2.addComponent(new Velocity(1, 2));
|
||||
|
||||
const e3 = scene.createEntity('E3');
|
||||
e3.addComponent(new Disabled());
|
||||
|
||||
const result = scene.queryAny(Position, Velocity);
|
||||
|
||||
expect(result.entities).toHaveLength(2);
|
||||
});
|
||||
|
||||
test('queryNone 查询不包含指定组件的实体', () => {
|
||||
const e1 = scene.createEntity('E1');
|
||||
e1.addComponent(new Position(10, 20));
|
||||
|
||||
const e2 = scene.createEntity('E2');
|
||||
e2.addComponent(new Position(30, 40));
|
||||
e2.addComponent(new Disabled());
|
||||
|
||||
const result = scene.queryNone(Disabled);
|
||||
|
||||
expect(result.entities).toHaveLength(1);
|
||||
expect(result.entities[0]).toBe(e1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TypedQueryBuilder', () => {
|
||||
test('scene.query() 创建类型安全的查询构建器', () => {
|
||||
const e1 = scene.createEntity('E1');
|
||||
e1.addComponent(new Position(10, 20));
|
||||
e1.addComponent(new Velocity(1, 2));
|
||||
|
||||
const e2 = scene.createEntity('E2');
|
||||
e2.addComponent(new Position(30, 40));
|
||||
e2.addComponent(new Velocity(3, 4));
|
||||
e2.addComponent(new Disabled());
|
||||
|
||||
// 构建查询
|
||||
const query = scene.query()
|
||||
.withAll(Position, Velocity)
|
||||
.withNone(Disabled);
|
||||
|
||||
const matcher = query.buildMatcher();
|
||||
|
||||
// 创建System使用这个matcher
|
||||
class TestSystem extends EntitySystem {
|
||||
public processedCount = 0;
|
||||
|
||||
constructor() {
|
||||
super(matcher);
|
||||
}
|
||||
|
||||
protected override process(entities: readonly Entity[]): void {
|
||||
this.processedCount = entities.length;
|
||||
}
|
||||
}
|
||||
|
||||
const system = new TestSystem();
|
||||
scene.addSystem(system);
|
||||
scene.update();
|
||||
|
||||
// 应该只处理e1(e2被Disabled排除)
|
||||
expect(system.processedCount).toBe(1);
|
||||
});
|
||||
|
||||
test('TypedQueryBuilder 支持复杂查询', () => {
|
||||
const e1 = scene.createEntity('E1');
|
||||
e1.addComponent(new Position(10, 20));
|
||||
e1.tag = 100;
|
||||
|
||||
const e2 = scene.createEntity('E2');
|
||||
e2.addComponent(new Position(30, 40));
|
||||
e2.tag = 200;
|
||||
|
||||
const query = scene.query()
|
||||
.withAll(Position)
|
||||
.withTag(100);
|
||||
|
||||
const condition = query.getCondition();
|
||||
|
||||
expect(condition.all).toContain(Position as any);
|
||||
expect(condition.tag).toBe(100);
|
||||
});
|
||||
});
|
||||
});
|
||||
205
packages/core/tests/TypeInference.test.ts
Normal file
205
packages/core/tests/TypeInference.test.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* TypeScript类型推断测试
|
||||
*
|
||||
* 验证组件类型自动推断功能
|
||||
*/
|
||||
|
||||
import { Component } from '../src/ECS/Component';
|
||||
import { Entity } from '../src/ECS/Entity';
|
||||
import { Scene } from '../src/ECS/Scene';
|
||||
import { Core } from '../src/Core';
|
||||
import { ECSComponent } from '../src/ECS/Decorators';
|
||||
import { requireComponent, tryGetComponent, getComponents } from '../src/ECS/TypedEntity';
|
||||
|
||||
// 测试组件
|
||||
@ECSComponent('Position')
|
||||
class Position extends Component {
|
||||
constructor(public x: number = 0, public y: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
@ECSComponent('Velocity')
|
||||
class Velocity extends Component {
|
||||
constructor(public dx: number = 0, public dy: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
@ECSComponent('Health')
|
||||
class Health extends Component {
|
||||
constructor(public value: number = 100, public maxValue: number = 100) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
describe('TypeScript类型推断', () => {
|
||||
let scene: Scene;
|
||||
let entity: Entity;
|
||||
|
||||
beforeEach(() => {
|
||||
Core.create({ debug: false, enableEntitySystems: true });
|
||||
scene = new Scene();
|
||||
scene.initialize();
|
||||
entity = scene.createEntity('TestEntity');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
scene.end();
|
||||
});
|
||||
|
||||
describe('Entity.getComponent 类型推断', () => {
|
||||
test('getComponent 应该自动推断正确的返回类型', () => {
|
||||
entity.addComponent(new Position(100, 200));
|
||||
|
||||
// 类型推断为 Position | null
|
||||
const position = entity.getComponent(Position);
|
||||
|
||||
// TypeScript应该知道position可能为null
|
||||
expect(position).not.toBeNull();
|
||||
|
||||
// 在null检查后,TypeScript应该知道position是Position类型
|
||||
if (position) {
|
||||
expect(position.x).toBe(100);
|
||||
expect(position.y).toBe(200);
|
||||
|
||||
// 这些操作应该有完整的类型提示
|
||||
position.x += 10;
|
||||
position.y += 20;
|
||||
|
||||
expect(position.x).toBe(110);
|
||||
expect(position.y).toBe(220);
|
||||
}
|
||||
});
|
||||
|
||||
test('getComponent 返回null时类型安全', () => {
|
||||
// 实体没有Velocity组件
|
||||
const velocity = entity.getComponent(Velocity);
|
||||
|
||||
// 应该返回null
|
||||
expect(velocity).toBeNull();
|
||||
});
|
||||
|
||||
test('多个不同类型组件的类型推断', () => {
|
||||
entity.addComponent(new Position(10, 20));
|
||||
entity.addComponent(new Velocity(1, 2));
|
||||
entity.addComponent(new Health(100));
|
||||
|
||||
const pos = entity.getComponent(Position);
|
||||
const vel = entity.getComponent(Velocity);
|
||||
const health = entity.getComponent(Health);
|
||||
|
||||
// 所有组件都应该被正确推断
|
||||
if (pos && vel && health) {
|
||||
// Position类型的字段
|
||||
pos.x = 50;
|
||||
pos.y = 60;
|
||||
|
||||
// Velocity类型的字段
|
||||
vel.dx = 5;
|
||||
vel.dy = 10;
|
||||
|
||||
// Health类型的字段
|
||||
health.value = 80;
|
||||
health.maxValue = 150;
|
||||
|
||||
expect(pos.x).toBe(50);
|
||||
expect(vel.dx).toBe(5);
|
||||
expect(health.value).toBe(80);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Entity.createComponent 类型推断', () => {
|
||||
test('createComponent 应该自动推断返回类型', () => {
|
||||
// 应该推断为Position类型(非null)
|
||||
const position = entity.createComponent(Position, 100, 200);
|
||||
|
||||
expect(position).toBeInstanceOf(Position);
|
||||
expect(position.x).toBe(100);
|
||||
expect(position.y).toBe(200);
|
||||
|
||||
// 应该有完整的类型提示
|
||||
position.x = 300;
|
||||
expect(position.x).toBe(300);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Entity.hasComponent 类型守卫', () => {
|
||||
test('hasComponent 可以用作类型守卫', () => {
|
||||
entity.addComponent(new Position(10, 20));
|
||||
|
||||
if (entity.hasComponent(Position)) {
|
||||
// 在这个作用域内,我们知道组件存在
|
||||
const pos = entity.getComponent(Position)!;
|
||||
pos.x = 100;
|
||||
expect(pos.x).toBe(100);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Entity.getOrCreateComponent 类型推断', () => {
|
||||
test('getOrCreateComponent 应该自动推断返回类型', () => {
|
||||
// 第一次调用:创建新组件
|
||||
const position1 = entity.getOrCreateComponent(Position, 50, 60);
|
||||
expect(position1.x).toBe(50);
|
||||
expect(position1.y).toBe(60);
|
||||
|
||||
// 第二次调用:返回已存在的组件
|
||||
const position2 = entity.getOrCreateComponent(Position, 100, 200);
|
||||
|
||||
// 应该是同一个组件
|
||||
expect(position2).toBe(position1);
|
||||
expect(position2.x).toBe(50); // 值未改变
|
||||
});
|
||||
});
|
||||
|
||||
describe('TypedEntity工具函数类型推断', () => {
|
||||
test('requireComponent 返回非空类型', () => {
|
||||
entity.addComponent(new Position(100, 200));
|
||||
|
||||
// requireComponent 返回非null类型
|
||||
const position = requireComponent(entity, Position);
|
||||
|
||||
// 不需要null检查
|
||||
expect(position.x).toBe(100);
|
||||
position.x = 300;
|
||||
expect(position.x).toBe(300);
|
||||
});
|
||||
|
||||
test('tryGetComponent 返回可选类型', () => {
|
||||
entity.addComponent(new Position(50, 50));
|
||||
|
||||
const position = tryGetComponent(entity, Position);
|
||||
|
||||
// 应该返回组件
|
||||
expect(position).toBeDefined();
|
||||
if (position) {
|
||||
expect(position.x).toBe(50);
|
||||
}
|
||||
|
||||
// 不存在的组件返回undefined
|
||||
const velocity = tryGetComponent(entity, Velocity);
|
||||
expect(velocity).toBeUndefined();
|
||||
});
|
||||
|
||||
test('getComponents 批量获取组件', () => {
|
||||
entity.addComponent(new Position(10, 20));
|
||||
entity.addComponent(new Velocity(1, 2));
|
||||
entity.addComponent(new Health(100));
|
||||
|
||||
const [pos, vel, health] = getComponents(entity, Position, Velocity, Health);
|
||||
|
||||
// 应该推断为数组类型
|
||||
expect(pos).not.toBeNull();
|
||||
expect(vel).not.toBeNull();
|
||||
expect(health).not.toBeNull();
|
||||
|
||||
if (pos && vel && health) {
|
||||
expect(pos.x).toBe(10);
|
||||
expect(vel.dx).toBe(1);
|
||||
expect(health.value).toBe(100);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user