新增wasm以优化实体update速度

This commit is contained in:
YHH
2025-06-08 21:50:50 +08:00
parent 0aa4791cf7
commit 8d0ad6b871
51 changed files with 5811 additions and 10773 deletions

6
source/.gitignore vendored
View File

@@ -64,6 +64,12 @@ typings/
bin/
dev-bin/
# WASM build artifacts
src/wasm/rust-ecs-core/target/
src/wasm/rust-ecs-core/pkg/
*.wasm
wasm-pack.log
# IDE
.vscode/
.idea/

2
source/.idea/.gitignore generated vendored
View File

@@ -1,2 +0,0 @@
# Default ignored files
/workspace.xml

6
source/.idea/misc.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/source.iml" filepath="$PROJECT_DIR$/.idea/source.iml" />
</modules>
</component>
</project>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
source/.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@@ -1,57 +1,46 @@
# 源码文件(只发布编译后的文件)
# 源码文件
src/
tsconfig.json
gulpfile.js
build.config.js
.babelrc
tsconfig*.json
*.ts
!bin/**/*.d.ts
# 开发工具配置
# 开发文件
dev-bin/
scripts/
.vscode/
.idea/
.wing/
# 依赖和缓存
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.git/
.gitignore
# 测试文件
*.test.js
*.test.ts
*.spec.js
*.spec.ts
test/
tests/
__tests__/
coverage/
**/*.test.*
**/*.spec.*
**/test/
**/tests/
# 构建工具
.nyc_output
.cache
# 环境文件
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# 临时文件
# 构建缓存
node_modules/
*.log
*.tmp
*.temp
.DS_Store
Thumbs.db
# 日志文件
logs/
*.log
# Rust 构建文件保留编译后的WASM
src/wasm/rust-ecs-core/target/
src/wasm/rust-ecs-core/Cargo.lock
src/wasm/rust-ecs-core/pkg/
!bin/wasm/
# 文档草稿
docs/draft/
*.draft.md
# 编辑器文件
.DS_Store
Thumbs.db
*.swp
*.swo
*~
# 其他
.git/
.gitignore
# 环境文件
.env
.env.local
.env.*.local

View File

@@ -1,13 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "gulp",
"task": "build",
"group": "build",
"problemMatcher": []
}
]
}

View File

@@ -1,3 +0,0 @@
{
"typescript.tsdk": "./node_modules/typescript/lib"
}

21
source/asconfig.json Normal file
View File

@@ -0,0 +1,21 @@
{
"targets": {
"debug": {
"outFile": "bin/ecs-core.wasm",
"textFile": "bin/ecs-core.wat",
"sourceMap": true,
"debug": true
},
"release": {
"outFile": "bin/ecs-core.wasm",
"optimizeLevel": 3,
"shrinkLevel": 2,
"converge": false,
"noAssert": false
}
},
"options": {
"bindings": "esm",
"exportRuntime": true
}
}

7251
source/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@esengine/ecs-framework",
"version": "2.0.5",
"version": "2.1.0",
"description": "用于Laya、Cocos等游戏引擎的高性能ECS框架",
"main": "bin/index.js",
"types": "bin/index.d.ts",
@@ -19,49 +19,30 @@
"egret"
],
"scripts": {
"build": "tsc",
"clean": "rimraf bin wasm",
"clean:wasm": "rimraf src/wasm/rust-ecs-core/pkg src/wasm/rust-ecs-core/target",
"build:wasm": "cd src/wasm/rust-ecs-core && wasm-pack build --target web --out-dir ../../../bin/wasm --release",
"build:ts": "tsc",
"prebuild": "npm run clean",
"build": "npm run build:wasm && npm run build:ts",
"build:watch": "tsc --watch",
"build:dev": "tsc -p tsconfig.dev.json",
"build:dev:watch": "tsc -p tsconfig.dev.json --watch",
"clean": "rimraf bin",
"clean:dev": "rimraf dev-bin",
"clean:all": "rimraf bin dev-bin",
"rebuild": "npm run clean && npm run build",
"rebuild:dev": "npm run clean:dev && npm run build:dev",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:framework:benchmark": "npm run build:dev && node dev-bin/Testing/framework-benchmark-test.js",
"lint": "eslint src --ext .ts",
"lint:fix": "eslint src --ext .ts --fix",
"prepublishOnly": "npm run rebuild",
"publish:patch": "npm run rebuild && npm version patch && npm publish",
"publish:minor": "npm run rebuild && npm version minor && npm publish",
"publish:major": "npm run rebuild && npm version major && npm publish",
"pack:check": "npm run rebuild && npm pack --dry-run",
"check": "node scripts/check-publish.js"
"rebuild": "npm run clean && npm run clean:wasm && npm run build",
"test:benchmark": "npm run build && node bin/Testing/Performance/benchmark.js",
"test:unit": "npm run build && node bin/Testing/test-runner.js",
"benchmark": "node scripts/benchmark.js",
"preversion": "npm run rebuild",
"postversion": "npm publish",
"publish:patch": "npm version patch",
"publish:minor": "npm version minor",
"publish:major": "npm version major",
"publish": "npm publish"
},
"author": "yhh",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.27.4",
"@babel/preset-env": "^7.27.2",
"browserify": "^17.0.1",
"gulp": "^5.0.1",
"gulp-babel": "^8.0.0",
"gulp-concat": "^2.6.1",
"gulp-inject-string": "^1.1.2",
"gulp-terser": "^2.1.0",
"gulp-string-replace": "^1.1.2",
"gulp-typescript": "^6.0.0-alpha.1",
"gulp-uglify": "^3.0.2",
"merge2": "^1.4.1",
"@types/node": "^20.19.0",
"rimraf": "^5.0.0",
"tsify": "^5.0.4",
"typedoc": "^0.28.5",
"typescript": "^5.8.3",
"vinyl-source-stream": "^2.0.0",
"watchify": "^4.0.0"
"typescript": "^5.8.3"
},
"publishConfig": {
"access": "public"
@@ -70,5 +51,5 @@
"type": "git",
"url": "https://github.com/esengine/ecs-framework.git"
},
"dependencies": {}
"optionalDependencies": {}
}

View File

@@ -0,0 +1,39 @@
#!/usr/bin/env node
/**
* ECS框架性能基准测试入口
*
* 使用方法:
* node benchmark.js
*/
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
console.log('🚀 启动ECS框架性能基准测试...\n');
const sourceDir = path.join(__dirname, '..');
try {
console.log('📦 准备构建项目...');
// 构建TypeScript代码
console.log('🔨 构建TypeScript代码...');
execSync('npm run build', {
stdio: 'inherit',
cwd: sourceDir
});
console.log('✅ TypeScript构建完成\n');
// 运行性能测试
console.log('🏃 运行性能基准测试...');
execSync('node bin/Testing/Performance/benchmark.js', {
stdio: 'inherit',
cwd: sourceDir
});
} catch (error) {
console.error('❌ 性能测试失败:', error.message);
process.exit(1);
}

View File

@@ -1,76 +0,0 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
console.log('🔍 发布前检查...\n');
// 检查必要文件
const requiredFiles = [
'package.json',
'README.md',
'LICENSE',
'bin/index.js',
'bin/index.d.ts'
];
let allFilesExist = true;
requiredFiles.forEach(file => {
if (fs.existsSync(file)) {
console.log(`${file} 存在`);
} else {
console.log(`${file} 不存在`);
allFilesExist = false;
}
});
// 检查package.json配置
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
console.log('\n📦 Package.json 检查:');
console.log(`✅ 包名: ${packageJson.name}`);
console.log(`✅ 版本: ${packageJson.version}`);
console.log(`✅ 主入口: ${packageJson.main}`);
console.log(`✅ 类型定义: ${packageJson.types}`);
// 检查bin目录
if (fs.existsSync('bin')) {
const binFiles = fs.readdirSync('bin', { recursive: true });
const jsFiles = binFiles.filter(f => f.endsWith('.js')).length;
const dtsFiles = binFiles.filter(f => f.endsWith('.d.ts')).length;
console.log(`\n🏗️ 编译文件检查:`);
console.log(`✅ JavaScript 文件: ${jsFiles}`);
console.log(`✅ 类型定义文件: ${dtsFiles}`);
} else {
console.log('\n❌ bin 目录不存在,请先运行 npm run build');
allFilesExist = false;
}
// 检查git状态
const { execSync } = require('child_process');
try {
const gitStatus = execSync('git status --porcelain', { encoding: 'utf8' });
if (gitStatus.trim()) {
console.log('\n⚠ Git 状态检查:');
console.log('有未提交的更改,建议先提交代码');
} else {
console.log('\n✅ Git 状态: 工作目录干净');
}
} catch (e) {
console.log('\n⚠ 无法检查git状态');
}
console.log('\n' + '='.repeat(50));
if (allFilesExist) {
console.log('🎉 所有检查通过!可以发布了');
console.log('\n发布命令:');
console.log(' npm run publish:patch # 补丁版本');
console.log(' npm run publish:minor # 次要版本');
console.log(' npm run publish:major # 主要版本');
} else {
console.log('❌ 检查失败,请修复问题后再发布');
process.exit(1);
}

View File

@@ -0,0 +1,187 @@
/**
* 位掩码优化器,用于预计算和缓存常用的组件掩码
*/
export class BitMaskOptimizer {
private static instance: BitMaskOptimizer;
private maskCache = new Map<string, bigint>();
private componentTypeMap = new Map<string, number>();
private nextComponentId = 0;
private constructor() {}
static getInstance(): BitMaskOptimizer {
if (!BitMaskOptimizer.instance) {
BitMaskOptimizer.instance = new BitMaskOptimizer();
}
return BitMaskOptimizer.instance;
}
/**
* 注册组件类型
*/
registerComponentType(componentName: string): number {
if (!this.componentTypeMap.has(componentName)) {
this.componentTypeMap.set(componentName, this.nextComponentId++);
}
return this.componentTypeMap.get(componentName)!;
}
/**
* 获取组件类型ID
*/
getComponentTypeId(componentName: string): number | undefined {
return this.componentTypeMap.get(componentName);
}
/**
* 创建单个组件的掩码
*/
createSingleComponentMask(componentName: string): bigint {
const cacheKey = `single:${componentName}`;
if (this.maskCache.has(cacheKey)) {
return this.maskCache.get(cacheKey)!;
}
const componentId = this.getComponentTypeId(componentName);
if (componentId === undefined) {
throw new Error(`Component type not registered: ${componentName}`);
}
const mask = 1n << BigInt(componentId);
this.maskCache.set(cacheKey, mask);
return mask;
}
/**
* 创建多个组件的组合掩码
*/
createCombinedMask(componentNames: string[]): bigint {
const sortedNames = [...componentNames].sort();
const cacheKey = `combined:${sortedNames.join(',')}`;
if (this.maskCache.has(cacheKey)) {
return this.maskCache.get(cacheKey)!;
}
let mask = 0n;
for (const componentName of componentNames) {
const componentId = this.getComponentTypeId(componentName);
if (componentId === undefined) {
throw new Error(`Component type not registered: ${componentName}`);
}
mask |= 1n << BigInt(componentId);
}
this.maskCache.set(cacheKey, mask);
return mask;
}
/**
* 检查掩码是否包含指定组件
*/
maskContainsComponent(mask: bigint, componentName: string): boolean {
const componentMask = this.createSingleComponentMask(componentName);
return (mask & componentMask) !== 0n;
}
/**
* 检查掩码是否包含所有指定组件
*/
maskContainsAllComponents(mask: bigint, componentNames: string[]): boolean {
const requiredMask = this.createCombinedMask(componentNames);
return (mask & requiredMask) === requiredMask;
}
/**
* 检查掩码是否包含任一指定组件
*/
maskContainsAnyComponent(mask: bigint, componentNames: string[]): boolean {
const anyMask = this.createCombinedMask(componentNames);
return (mask & anyMask) !== 0n;
}
/**
* 添加组件到掩码
*/
addComponentToMask(mask: bigint, componentName: string): bigint {
const componentMask = this.createSingleComponentMask(componentName);
return mask | componentMask;
}
/**
* 从掩码中移除组件
*/
removeComponentFromMask(mask: bigint, componentName: string): bigint {
const componentMask = this.createSingleComponentMask(componentName);
return mask & ~componentMask;
}
/**
* 预计算常用掩码组合
*/
precomputeCommonMasks(commonCombinations: string[][]): void {
for (const combination of commonCombinations) {
this.createCombinedMask(combination);
}
}
/**
* 获取掩码缓存统计信息
*/
getCacheStats(): { size: number; componentTypes: number } {
return {
size: this.maskCache.size,
componentTypes: this.componentTypeMap.size
};
}
/**
* 清空缓存
*/
clearCache(): void {
this.maskCache.clear();
}
/**
* 重置优化器
*/
reset(): void {
this.maskCache.clear();
this.componentTypeMap.clear();
this.nextComponentId = 0;
}
/**
* 将掩码转换为组件名称数组
*/
maskToComponentNames(mask: bigint): string[] {
const componentNames: string[] = [];
for (const [componentName, componentId] of this.componentTypeMap) {
const componentMask = 1n << BigInt(componentId);
if ((mask & componentMask) !== 0n) {
componentNames.push(componentName);
}
}
return componentNames;
}
/**
* 获取掩码中组件的数量
*/
getComponentCount(mask: bigint): number {
let count = 0;
let tempMask = mask;
while (tempMask !== 0n) {
if ((tempMask & 1n) !== 0n) {
count++;
}
tempMask >>= 1n;
}
return count;
}
}

View File

@@ -0,0 +1,152 @@
import { Component } from '../Component';
/**
* 组件对象池,用于复用组件实例以减少内存分配
*/
export class ComponentPool<T extends Component> {
private pool: T[] = [];
private createFn: () => T;
private resetFn?: (component: T) => void;
private maxSize: number;
constructor(
createFn: () => T,
resetFn?: (component: T) => void,
maxSize: number = 1000
) {
this.createFn = createFn;
this.resetFn = resetFn;
this.maxSize = maxSize;
}
/**
* 获取一个组件实例
*/
acquire(): T {
if (this.pool.length > 0) {
return this.pool.pop()!;
}
return this.createFn();
}
/**
* 释放一个组件实例回池中
*/
release(component: T): void {
if (this.pool.length < this.maxSize) {
if (this.resetFn) {
this.resetFn(component);
}
this.pool.push(component);
}
}
/**
* 预填充对象池
*/
prewarm(count: number): void {
for (let i = 0; i < count && this.pool.length < this.maxSize; i++) {
this.pool.push(this.createFn());
}
}
/**
* 清空对象池
*/
clear(): void {
this.pool.length = 0;
}
/**
* 获取池中可用对象数量
*/
getAvailableCount(): number {
return this.pool.length;
}
/**
* 获取池的最大容量
*/
getMaxSize(): number {
return this.maxSize;
}
}
/**
* 全局组件池管理器
*/
export class ComponentPoolManager {
private static instance: ComponentPoolManager;
private pools = new Map<string, ComponentPool<any>>();
private constructor() {}
static getInstance(): ComponentPoolManager {
if (!ComponentPoolManager.instance) {
ComponentPoolManager.instance = new ComponentPoolManager();
}
return ComponentPoolManager.instance;
}
/**
* 注册组件池
*/
registerPool<T extends Component>(
componentName: string,
createFn: () => T,
resetFn?: (component: T) => void,
maxSize?: number
): void {
this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize));
}
/**
* 获取组件实例
*/
acquireComponent<T extends Component>(componentName: string): T | null {
const pool = this.pools.get(componentName);
return pool ? pool.acquire() : null;
}
/**
* 释放组件实例
*/
releaseComponent<T extends Component>(componentName: string, component: T): void {
const pool = this.pools.get(componentName);
if (pool) {
pool.release(component);
}
}
/**
* 预热所有池
*/
prewarmAll(count: number = 100): void {
for (const pool of this.pools.values()) {
pool.prewarm(count);
}
}
/**
* 清空所有池
*/
clearAll(): void {
for (const pool of this.pools.values()) {
pool.clear();
}
}
/**
* 获取池统计信息
*/
getPoolStats(): Map<string, { available: number; maxSize: number }> {
const stats = new Map();
for (const [name, pool] of this.pools) {
stats.set(name, {
available: pool.getAvailableCount(),
maxSize: pool.getMaxSize()
});
}
return stats;
}
}

View File

@@ -390,7 +390,7 @@ export class ECSFluentAPI {
* @returns 查询构建器
*/
public query(): QueryBuilder {
return this.querySystem.createQuery();
return new QueryBuilder(this.querySystem);
}
/**

View File

@@ -0,0 +1,256 @@
import { Entity } from '../Entity';
/**
* 索引更新操作类型
*/
export enum IndexUpdateType {
ADD_ENTITY = 'add_entity',
REMOVE_ENTITY = 'remove_entity',
UPDATE_ENTITY = 'update_entity'
}
/**
* 索引更新操作
*/
export interface IndexUpdateOperation {
type: IndexUpdateType;
entity: Entity;
oldMask?: bigint;
newMask?: bigint;
}
/**
* 延迟索引更新器,用于批量更新查询索引以提高性能
*/
export class IndexUpdateBatcher {
private pendingOperations: IndexUpdateOperation[] = [];
private isProcessing = false;
private batchSize = 1000;
private flushTimeout: NodeJS.Timeout | null = null;
private flushDelay = 16; // 16ms约60fps
/**
* 添加索引更新操作
*/
addOperation(operation: IndexUpdateOperation): void {
this.pendingOperations.push(operation);
// 如果达到批量大小,立即处理
if (this.pendingOperations.length >= this.batchSize) {
this.flush();
} else {
// 否则延迟处理
this.scheduleFlush();
}
}
/**
* 批量添加实体
*/
addEntities(entities: Entity[]): void {
for (const entity of entities) {
this.pendingOperations.push({
type: IndexUpdateType.ADD_ENTITY,
entity
});
}
if (this.pendingOperations.length >= this.batchSize) {
this.flush();
} else {
this.scheduleFlush();
}
}
/**
* 批量移除实体
*/
removeEntities(entities: Entity[]): void {
for (const entity of entities) {
this.pendingOperations.push({
type: IndexUpdateType.REMOVE_ENTITY,
entity
});
}
if (this.pendingOperations.length >= this.batchSize) {
this.flush();
} else {
this.scheduleFlush();
}
}
/**
* 批量更新实体
*/
updateEntities(updates: Array<{ entity: Entity; oldMask: bigint; newMask: bigint }>): void {
for (const update of updates) {
this.pendingOperations.push({
type: IndexUpdateType.UPDATE_ENTITY,
entity: update.entity,
oldMask: update.oldMask,
newMask: update.newMask
});
}
if (this.pendingOperations.length >= this.batchSize) {
this.flush();
} else {
this.scheduleFlush();
}
}
/**
* 安排延迟刷新
*/
private scheduleFlush(): void {
if (this.flushTimeout) {
return;
}
this.flushTimeout = setTimeout(() => {
this.flush();
}, this.flushDelay);
}
/**
* 立即处理所有待处理的操作
*/
flush(): void {
if (this.isProcessing || this.pendingOperations.length === 0) {
return;
}
this.isProcessing = true;
if (this.flushTimeout) {
clearTimeout(this.flushTimeout);
this.flushTimeout = null;
}
try {
this.processBatch();
} finally {
this.isProcessing = false;
}
}
/**
* 处理批量操作
*/
private processBatch(): void {
const operations = this.pendingOperations;
this.pendingOperations = [];
// 按操作类型分组以优化处理
const addOperations: Entity[] = [];
const removeOperations: Entity[] = [];
const updateOperations: Array<{ entity: Entity; oldMask: bigint; newMask: bigint }> = [];
for (const operation of operations) {
switch (operation.type) {
case IndexUpdateType.ADD_ENTITY:
addOperations.push(operation.entity);
break;
case IndexUpdateType.REMOVE_ENTITY:
removeOperations.push(operation.entity);
break;
case IndexUpdateType.UPDATE_ENTITY:
if (operation.oldMask !== undefined && operation.newMask !== undefined) {
updateOperations.push({
entity: operation.entity,
oldMask: operation.oldMask,
newMask: operation.newMask
});
}
break;
}
}
// 批量处理每种类型的操作
if (addOperations.length > 0) {
this.processBatchAdd(addOperations);
}
if (removeOperations.length > 0) {
this.processBatchRemove(removeOperations);
}
if (updateOperations.length > 0) {
this.processBatchUpdate(updateOperations);
}
}
/**
* 批量处理添加操作
*/
private processBatchAdd(entities: Entity[]): void {
// 这里应该调用QuerySystem的批量添加方法
// 由于需要访问QuerySystem这个方法应该由外部注入处理函数
if (this.onBatchAdd) {
this.onBatchAdd(entities);
}
}
/**
* 批量处理移除操作
*/
private processBatchRemove(entities: Entity[]): void {
if (this.onBatchRemove) {
this.onBatchRemove(entities);
}
}
/**
* 批量处理更新操作
*/
private processBatchUpdate(updates: Array<{ entity: Entity; oldMask: bigint; newMask: bigint }>): void {
if (this.onBatchUpdate) {
this.onBatchUpdate(updates);
}
}
/**
* 设置批量大小
*/
setBatchSize(size: number): void {
this.batchSize = Math.max(1, size);
}
/**
* 设置刷新延迟
*/
setFlushDelay(delay: number): void {
this.flushDelay = Math.max(0, delay);
}
/**
* 获取待处理操作数量
*/
getPendingCount(): number {
return this.pendingOperations.length;
}
/**
* 清空所有待处理操作
*/
clear(): void {
this.pendingOperations.length = 0;
if (this.flushTimeout) {
clearTimeout(this.flushTimeout);
this.flushTimeout = null;
}
}
/**
* 检查是否有待处理操作
*/
hasPendingOperations(): boolean {
return this.pendingOperations.length > 0;
}
// 回调函数,由外部设置
public onBatchAdd?: (entities: Entity[]) => void;
public onBatchRemove?: (entities: Entity[]) => void;
public onBatchUpdate?: (updates: Array<{ entity: Entity; oldMask: bigint; newMask: bigint }>) => void;
}

File diff suppressed because it is too large Load Diff

View File

@@ -90,8 +90,9 @@ export class Scene {
/**
* 创建场景实例
* @param enableWasmAcceleration 是否启用WebAssembly加速默认为true
*/
constructor() {
constructor(enableWasmAcceleration: boolean = true) {
this.entities = new EntityList(this);
this.entityProcessors = new EntityProcessorList();
this.identifierPool = new IdentifierPool();
@@ -195,13 +196,14 @@ export class Scene {
/**
* 在场景的实体列表中添加一个实体
* @param entity 要添加的实体
* @param deferCacheClear 是否延迟缓存清理(用于批量操作)
*/
public addEntity(entity: Entity) {
public addEntity(entity: Entity, deferCacheClear: boolean = false) {
this.entities.add(entity);
entity.scene = this;
// 将实体添加到查询系统
this.querySystem.addEntity(entity);
// 将实体添加到查询系统(可延迟缓存清理)
this.querySystem.addEntity(entity, deferCacheClear);
// 触发实体添加事件
this.eventSystem.emitSync('entity:added', { entity, scene: this });
@@ -209,6 +211,58 @@ export class Scene {
return entity;
}
/**
* 批量创建实体(高性能版本)
* @param count 要创建的实体数量
* @param namePrefix 实体名称前缀
* @returns 创建的实体列表
*/
public createEntities(count: number, namePrefix: string = "Entity"): Entity[] {
const entities: Entity[] = [];
// 批量创建实体对象,不立即添加到系统
for (let i = 0; i < count; i++) {
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
entity.scene = this;
entities.push(entity);
}
// 批量添加到实体列表
for (const entity of entities) {
this.entities.add(entity);
}
// 批量添加到查询系统(无重复检查,性能最优)
this.querySystem.addEntitiesUnchecked(entities);
// 批量触发事件(可选,减少事件开销)
this.eventSystem.emitSync('entities:batch_added', { entities, scene: this, count });
return entities;
}
/**
* 批量创建实体
* @param count 要创建的实体数量
* @param namePrefix 实体名称前缀
* @returns 创建的实体列表
*/
public createEntitiesOld(count: number, namePrefix: string = "Entity"): Entity[] {
const entities: Entity[] = [];
// 批量创建实体,延迟缓存清理
for (let i = 0; i < count; i++) {
const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut());
entities.push(entity);
this.addEntity(entity, true); // 延迟缓存清理
}
// 最后统一清理缓存
this.querySystem.clearCache();
return entities;
}
/**
* 从场景中删除所有实体
*/

View File

@@ -0,0 +1,768 @@
/**
* ECS框架性能基准测试
* 测试框架在不同场景下的性能表现
*/
import { Scene } from '../../ECS/Scene';
import { Entity } from '../../ECS/Entity';
import { Component } from '../../ECS/Component';
console.log('🚀 ECS框架性能基准测试');
console.log('============================================================');
console.log('测试目标: 评估ECS框架在不同场景下的性能表现');
console.log('============================================================');
/**
* 位置组件
*/
class PositionComponent extends Component {
public x: number = 0;
public y: number = 0;
constructor(x: number = 0, y: number = 0) {
super();
this.x = x;
this.y = y;
}
}
/**
* 速度组件
*/
class VelocityComponent extends Component {
public vx: number = 0;
public vy: number = 0;
constructor(vx: number = 0, vy: number = 0) {
super();
this.vx = vx;
this.vy = vy;
}
}
/**
* 生命值组件
*/
class HealthComponent extends Component {
public health: number = 100;
public maxHealth: number = 100;
constructor(health: number = 100) {
super();
this.health = health;
this.maxHealth = health;
}
}
/**
* 渲染组件
*/
class RenderComponent extends Component {
public sprite: string = '';
public visible: boolean = true;
constructor(sprite: string = '') {
super();
this.sprite = sprite;
}
}
/**
* AI组件
*/
class AIComponent extends Component {
public state: string = 'idle';
public target: Entity | null = null;
constructor(state: string = 'idle') {
super();
this.state = state;
}
}
/**
* 测试配置接口
*/
interface TestConfig {
entityCounts: number[];
queryIterations: number;
updateIterations: number;
}
/**
* 测试配置
*/
const TEST_CONFIG: TestConfig = {
entityCounts: [1000, 5000, 10000, 25000, 50000, 100000, 200000, 500000],
queryIterations: 1000,
updateIterations: 100
};
/**
* 性能测试结果
*/
interface PerformanceResult {
entityCount: number;
singleQuery: number;
multiQuery: number;
complexQuery: number;
tagQuery: number;
singleTagQuery: number;
entityUpdate: number;
memoryUsage: number;
}
/**
* 测试创建实体的性能
*/
function testEntityCreation(scene: Scene, count: number): {
totalTime: number;
averageTime: number;
entitiesPerSecond: number;
breakdown: {
entityCreation: number;
componentAddition: number;
tagAssignment: number;
};
} {
const startTime = performance.now();
let entityCreationTime = 0;
let componentAdditionTime = 0;
let tagAssignmentTime = 0;
// 批量创建实体(不添加组件)
const entityStart = performance.now();
const entities = scene.createEntities(count, "Entity");
entityCreationTime = performance.now() - entityStart;
// 批量添加组件
const componentStart = performance.now();
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
// 所有实体都有位置组件
entity.addComponent(new PositionComponent(
Math.random() * 1000,
Math.random() * 1000
));
// 70%的实体有速度组件
if (Math.random() < 0.7) {
entity.addComponent(new VelocityComponent(
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10
));
}
// 50%的实体有生命值组件
if (Math.random() < 0.5) {
entity.addComponent(new HealthComponent(
Math.floor(Math.random() * 100) + 50
));
}
// 30%的实体有渲染组件
if (Math.random() < 0.3) {
entity.addComponent(new RenderComponent(`sprite_${i % 10}`));
}
// 20%的实体有AI组件
if (Math.random() < 0.2) {
entity.addComponent(new AIComponent(['idle', 'patrol', 'chase'][Math.floor(Math.random() * 3)]));
}
}
componentAdditionTime = performance.now() - componentStart;
// 批量设置标签
const tagStart = performance.now();
for (const entity of entities) {
entity.tag = Math.floor(Math.random() * 10);
}
tagAssignmentTime = performance.now() - tagStart;
const totalTime = performance.now() - startTime;
return {
totalTime,
averageTime: totalTime / count,
entitiesPerSecond: count / (totalTime / 1000),
breakdown: {
entityCreation: entityCreationTime,
componentAddition: componentAdditionTime,
tagAssignment: tagAssignmentTime
}
};
}
/**
* 创建测试实体
*/
function createTestEntities(scene: Scene, count: number): Entity[] {
const entities: Entity[] = [];
for (let i = 0; i < count; i++) {
const entity = scene.createEntity(`Entity_${i}`);
// 所有实体都有位置组件
entity.addComponent(new PositionComponent(
Math.random() * 1000,
Math.random() * 1000
));
// 70%的实体有速度组件
if (Math.random() < 0.7) {
entity.addComponent(new VelocityComponent(
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10
));
}
// 50%的实体有生命值组件
if (Math.random() < 0.5) {
entity.addComponent(new HealthComponent(
Math.floor(Math.random() * 100) + 50
));
}
// 30%的实体有渲染组件
if (Math.random() < 0.3) {
entity.addComponent(new RenderComponent(`sprite_${i % 10}`));
}
// 20%的实体有AI组件
if (Math.random() < 0.2) {
entity.addComponent(new AIComponent(['idle', 'patrol', 'chase'][Math.floor(Math.random() * 3)]));
}
// 设置随机标签
entity.tag = Math.floor(Math.random() * 10);
entities.push(entity);
}
return entities;
}
/**
* 测试单组件查询性能
*/
function testSingleComponentQuery(scene: Scene, iterations: number): number {
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
scene.querySystem.queryAll(PositionComponent);
}
return performance.now() - startTime;
}
/**
* 测试多组件查询性能
*/
function testMultiComponentQuery(scene: Scene, iterations: number): number {
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
scene.querySystem.queryAll(PositionComponent, VelocityComponent);
}
return performance.now() - startTime;
}
/**
* 测试复杂查询性能
*/
function testComplexQuery(scene: Scene, iterations: number): number {
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
scene.querySystem.queryAll(PositionComponent, VelocityComponent, HealthComponent);
}
return performance.now() - startTime;
}
/**
* 测试标签查询性能
*/
function testTagQuery(scene: Scene, iterations: number): number {
const startTime = performance.now();
// 优化:预先获取所有标签查询结果,然后重复使用
// 这更符合实际游戏中的使用模式
const tags = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i = 0; i < iterations; i++) {
// 批量查询所有标签
for (const tag of tags) {
scene.querySystem.queryByTag(tag);
}
}
return performance.now() - startTime;
}
/**
* 测试单个标签查询性能
*/
function testSingleTagQuery(scene: Scene, iterations: number): number {
const startTime = performance.now();
// 只查询标签0测试单个标签的查询性能和缓存效果
for (let i = 0; i < iterations; i++) {
scene.querySystem.queryByTag(0);
}
return performance.now() - startTime;
}
/**
* 测试实体更新性能
*/
function testEntityUpdate(scene: Scene, iterations: number): number {
const entities = scene.querySystem.queryAll(PositionComponent, VelocityComponent).entities;
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
for (const entity of entities) {
const pos = entity.getComponent(PositionComponent);
const vel = entity.getComponent(VelocityComponent);
if (pos && vel) {
pos.x += vel.vx;
pos.y += vel.vy;
}
}
}
return performance.now() - startTime;
}
/**
* 获取内存使用情况
*/
function getMemoryUsage(): number {
if (typeof process !== 'undefined' && process.memoryUsage) {
return process.memoryUsage().heapUsed / 1024 / 1024; // MB
}
return 0;
}
/**
* 运行性能测试
*/
function runPerformanceTest(scene: Scene, entityCount: number, config: TestConfig): PerformanceResult {
console.log(`\n📊 测试 ${entityCount.toLocaleString()} 个实体...`);
// 测试实体创建性能
const startMemory = getMemoryUsage();
console.log(` 🔧 测试实体创建性能...`);
const creationStats = testEntityCreation(scene, entityCount);
const endMemory = getMemoryUsage();
console.log(` 📈 实体创建性能分析:`);
console.log(` 总时间: ${creationStats.totalTime.toFixed(2)}ms`);
console.log(` 平均时间: ${creationStats.averageTime.toFixed(4)}ms/实体`);
console.log(` 创建速度: ${creationStats.entitiesPerSecond.toFixed(0)} 实体/秒`);
console.log(` 时间分解:`);
console.log(` - 实体创建: ${creationStats.breakdown.entityCreation.toFixed(2)}ms (${(creationStats.breakdown.entityCreation / creationStats.totalTime * 100).toFixed(1)}%)`);
console.log(` - 组件添加: ${creationStats.breakdown.componentAddition.toFixed(2)}ms (${(creationStats.breakdown.componentAddition / creationStats.totalTime * 100).toFixed(1)}%)`);
console.log(` - 标签分配: ${creationStats.breakdown.tagAssignment.toFixed(2)}ms (${(creationStats.breakdown.tagAssignment / creationStats.totalTime * 100).toFixed(1)}%)`);
console.log(` 内存使用: ${(endMemory - startMemory).toFixed(1)}MB`);
// 运行测试
console.log(` 🔍 执行查询测试...`);
const singleQuery = testSingleComponentQuery(scene, config.queryIterations);
const multiQuery = testMultiComponentQuery(scene, config.queryIterations);
const complexQuery = testComplexQuery(scene, config.queryIterations);
const tagQuery = testTagQuery(scene, config.queryIterations);
const singleTagQuery = testSingleTagQuery(scene, config.queryIterations);
console.log(` ⚡ 执行更新测试...`);
const entityUpdate = testEntityUpdate(scene, config.updateIterations);
console.log(` ✅ 测试完成`);
return {
entityCount,
singleQuery,
multiQuery,
complexQuery,
tagQuery,
singleTagQuery,
entityUpdate,
memoryUsage: endMemory - startMemory
};
}
/**
* 显示系统信息
*/
function displaySystemInfo(scene: Scene): void {
const status = scene.querySystem.getAccelerationStatus();
const stats = scene.querySystem.getStats();
console.log('\n🔍 系统信息:');
console.log(` 当前提供者: ${status.currentProvider}`);
console.log(` WebAssembly: ${status.wasmEnabled ? '已启用' : '未启用'}`);
console.log(` 可用提供者: ${status.availableProviders.join(', ')}`);
console.log(` 索引统计:`);
console.log(` 组件掩码索引: ${stats.indexStats.maskIndexSize}`);
console.log(` 组件类型索引: ${stats.indexStats.componentIndexSize}`);
console.log(` 标签索引: ${stats.indexStats.tagIndexSize}`);
console.log(` 名称索引: ${stats.indexStats.nameIndexSize}`);
if (status.performanceInfo?.cacheStats) {
console.log(` 查询缓存:`);
console.log(` 缓存大小: ${status.performanceInfo.cacheStats.size}`);
console.log(` 命中率: ${status.performanceInfo.cacheStats.hitRate}`);
}
}
/**
* 显示性能结果
*/
function displayResults(results: PerformanceResult[], scene: Scene): void {
console.log('\n📈 ECS框架性能测试结果');
console.log('='.repeat(130));
console.log('| 实体数量 | 单组件查询 | 双组件查询 | 三组件查询 | 多标签查询 | 单标签查询 | 实体更新 | 内存使用 |');
console.log('|' + '-'.repeat(128) + '|');
for (const result of results) {
const entityCount = result.entityCount.toLocaleString().padStart(9);
const singleQuery = `${result.singleQuery.toFixed(2)}ms`.padStart(10);
const multiQuery = `${result.multiQuery.toFixed(2)}ms`.padStart(10);
const complexQuery = `${result.complexQuery.toFixed(2)}ms`.padStart(10);
const tagQuery = `${result.tagQuery.toFixed(2)}ms`.padStart(10);
const singleTagQuery = `${result.singleTagQuery.toFixed(2)}ms`.padStart(10);
const entityUpdate = `${result.entityUpdate.toFixed(2)}ms`.padStart(9);
const memoryUsage = `${result.memoryUsage.toFixed(1)}MB`.padStart(9);
console.log(`| ${entityCount} | ${singleQuery} | ${multiQuery} | ${complexQuery} | ${tagQuery} | ${singleTagQuery} | ${entityUpdate} | ${memoryUsage} |`);
}
console.log('|' + '-'.repeat(128) + '|');
// 计算性能指标
const maxEntities = Math.max(...results.map(r => r.entityCount));
const maxResult = results.find(r => r.entityCount === maxEntities)!;
console.log(`\n🎯 性能峰值 (${maxEntities.toLocaleString()} 个实体):`);
console.log(` 单组件查询: ${(TEST_CONFIG.queryIterations / maxResult.singleQuery * 1000).toFixed(0)} 次/秒`);
console.log(` 双组件查询: ${(TEST_CONFIG.queryIterations / maxResult.multiQuery * 1000).toFixed(0)} 次/秒`);
console.log(` 三组件查询: ${(TEST_CONFIG.queryIterations / maxResult.complexQuery * 1000).toFixed(0)} 次/秒`);
console.log(` 多标签查询: ${(TEST_CONFIG.queryIterations * 10 / maxResult.tagQuery * 1000).toFixed(0)} 次/秒`);
console.log(` 单标签查询: ${(TEST_CONFIG.queryIterations / maxResult.singleTagQuery * 1000).toFixed(0)} 次/秒`);
console.log(` 实体更新: ${(maxResult.entityCount * TEST_CONFIG.updateIterations / maxResult.entityUpdate * 1000).toFixed(0)} 个/秒`);
console.log(` 内存效率: ${(maxResult.entityCount / (maxResult.memoryUsage || 1)).toFixed(0)} 实体/MB`);
// 性能评级
const avgQueryTime = (maxResult.singleQuery + maxResult.multiQuery + maxResult.complexQuery + maxResult.singleTagQuery) / 4;
let rating = '';
if (avgQueryTime < 50) rating = '🚀 优秀';
else if (avgQueryTime < 100) rating = '✅ 良好';
else if (avgQueryTime < 200) rating = '⚠️ 一般';
else rating = '❌ 需要优化';
console.log(`\n📊 性能评级: ${rating}`);
console.log(` 平均查询时间: ${avgQueryTime.toFixed(2)}ms`);
// 显示查询统计信息
const queryStats = scene.querySystem.getStats().queryStats;
console.log(`\n🔍 查询统计:`);
console.log(` 总查询次数: ${queryStats.totalQueries.toLocaleString()}`);
console.log(` 缓存命中: ${queryStats.cacheHits.toLocaleString()}`);
console.log(` 索引命中: ${queryStats.indexHits.toLocaleString()}`);
console.log(` 线性扫描: ${queryStats.linearScans.toLocaleString()}`);
console.log(` 缓存命中率: ${queryStats.cacheHitRate}`);
// 标签查询性能分析
console.log(`\n🏷 标签查询分析:`);
const tagQueryRatio = maxResult.tagQuery / maxResult.singleTagQuery;
console.log(` 多标签查询 vs 单标签查询: ${tagQueryRatio.toFixed(2)}x (预期约10x)`);
if (tagQueryRatio > 15) {
console.log(` ⚠️ 多标签查询性能异常,可能存在缓存问题`);
} else if (tagQueryRatio < 5) {
console.log(` ✅ 标签查询缓存效果良好`);
} else {
console.log(` 📊 标签查询性能正常`);
}
// 性能改进分析
const improvement = calculatePerformanceImprovement(results);
if (improvement) {
console.log(`\n📈 性能改进分析:`);
console.log(` 双组件查询改进: ${improvement.multiQuery}x`);
console.log(` 三组件查询改进: ${improvement.complexQuery}x`);
console.log(` 整体查询改进: ${improvement.overall}x`);
}
// 扩展性分析
console.log(`\n📊 扩展性分析:`);
analyzeScalability(results);
}
/**
* 计算性能改进(与优化前对比)
*/
function calculatePerformanceImprovement(results: PerformanceResult[]): {
multiQuery: string;
complexQuery: string;
overall: string;
} | null {
// 基于50,000实体的结果进行分析
const maxResult = results.find(r => r.entityCount === 50000);
if (!maxResult) return null;
// 优化前的基准时间(基于之前的测试结果)
const baselineMultiQuery = 1270.54; // ms
const baselineComplexQuery = 981.76; // ms
const multiImprovement = (baselineMultiQuery / maxResult.multiQuery).toFixed(2);
const complexImprovement = (baselineComplexQuery / maxResult.complexQuery).toFixed(2);
const overallImprovement = ((baselineMultiQuery + baselineComplexQuery) /
(maxResult.multiQuery + maxResult.complexQuery)).toFixed(2);
return {
multiQuery: multiImprovement,
complexQuery: complexImprovement,
overall: overallImprovement
};
}
/**
* 分析系统扩展性
*/
function analyzeScalability(results: PerformanceResult[]): void {
if (results.length < 2) return;
// 分析查询时间随实体数量的变化趋势
const first = results[0];
const last = results[results.length - 1];
const entityRatio = last.entityCount / first.entityCount;
const singleQueryRatio = last.singleQuery / first.singleQuery;
const multiQueryRatio = last.multiQuery / first.multiQuery;
const complexQueryRatio = last.complexQuery / first.complexQuery;
console.log(` 实体数量增长: ${entityRatio.toFixed(1)}x (${first.entityCount.toLocaleString()}${last.entityCount.toLocaleString()})`);
console.log(` 单组件查询时间增长: ${singleQueryRatio.toFixed(2)}x`);
console.log(` 双组件查询时间增长: ${multiQueryRatio.toFixed(2)}x`);
console.log(` 三组件查询时间增长: ${complexQueryRatio.toFixed(2)}x`);
// 计算复杂度
const avgComplexity = (singleQueryRatio + multiQueryRatio + complexQueryRatio) / 3;
let complexityRating = '';
if (avgComplexity < entityRatio * 0.1) complexityRating = '🚀 近似O(1) - 优秀';
else if (avgComplexity < entityRatio * 0.5) complexityRating = '✅ 亚线性 - 良好';
else if (avgComplexity < entityRatio) complexityRating = '⚠️ 接近线性 - 一般';
else complexityRating = '❌ 超线性 - 需要优化';
console.log(` 时间复杂度评估: ${complexityRating}`);
// 内存效率分析
const memoryEfficiencyFirst = first.entityCount / first.memoryUsage;
const memoryEfficiencyLast = last.entityCount / last.memoryUsage;
const memoryEfficiencyRatio = memoryEfficiencyLast / memoryEfficiencyFirst;
console.log(` 内存效率变化: ${memoryEfficiencyRatio.toFixed(2)}x (${memoryEfficiencyFirst.toFixed(0)}${memoryEfficiencyLast.toFixed(0)} 实体/MB)`);
}
/**
* 专门测试实体创建性能
*/
async function runEntityCreationBenchmark(): Promise<void> {
console.log('\n🚀 实体创建性能基准测试');
console.log('='.repeat(60));
const testCounts = [1000, 5000, 10000, 50000, 100000];
for (const count of testCounts) {
console.log(`\n📊 测试创建 ${count.toLocaleString()} 个实体:`);
// 创建新场景
const scene = new Scene();
// 测试创建性能
const stats = testEntityCreation(scene, count);
console.log(` 总时间: ${stats.totalTime.toFixed(2)}ms`);
console.log(` 平均时间: ${stats.averageTime.toFixed(4)}ms/实体`);
console.log(` 创建速度: ${stats.entitiesPerSecond.toFixed(0)} 实体/秒`);
console.log(` 时间分解:`);
console.log(` - 实体创建: ${stats.breakdown.entityCreation.toFixed(2)}ms (${(stats.breakdown.entityCreation / stats.totalTime * 100).toFixed(1)}%)`);
console.log(` - 组件添加: ${stats.breakdown.componentAddition.toFixed(2)}ms (${(stats.breakdown.componentAddition / stats.totalTime * 100).toFixed(1)}%)`);
console.log(` - 标签分配: ${stats.breakdown.tagAssignment.toFixed(2)}ms (${(stats.breakdown.tagAssignment / stats.totalTime * 100).toFixed(1)}%)`);
// 分析性能瓶颈
const { entityCreation, componentAddition, tagAssignment } = stats.breakdown;
const total = stats.totalTime;
console.log(` 性能瓶颈分析:`);
if (componentAddition / total > 0.5) {
console.log(` ⚠️ 组件添加是主要瓶颈 (${(componentAddition / total * 100).toFixed(1)}%)`);
}
if (entityCreation / total > 0.3) {
console.log(` ⚠️ 实体创建开销较高 (${(entityCreation / total * 100).toFixed(1)}%)`);
}
if (tagAssignment / total > 0.1) {
console.log(` ⚠️ 标签分配开销异常 (${(tagAssignment / total * 100).toFixed(1)}%)`);
}
// 分析组件添加性能(仅对较小的测试集)
if (count <= 10000) {
analyzeComponentAdditionPerformance(new Scene(), Math.min(count, 5000));
}
// 清理场景
scene.end();
}
console.log('\n📈 实体创建性能总结:');
console.log(' 主要性能瓶颈通常在组件添加阶段');
console.log(' 建议优化方向:');
console.log(' 1. 减少组件注册开销');
console.log(' 2. 优化位掩码计算');
console.log(' 3. 减少内存分配次数');
console.log(' 4. 使用对象池复用组件实例');
}
/**
* 测试组件添加性能的详细分析
*/
function analyzeComponentAdditionPerformance(scene: Scene, count: number): void {
console.log(`\n🔬 组件添加性能详细分析 (${count.toLocaleString()} 个实体):`);
// 创建实体但不添加组件
const entities = scene.createEntities(count, "TestEntity");
// 分别测试每种组件的添加性能
const componentTests = [
{
name: "PositionComponent",
create: () => new PositionComponent(Math.random() * 1000, Math.random() * 1000),
probability: 1.0
},
{
name: "VelocityComponent",
create: () => new VelocityComponent((Math.random() - 0.5) * 10, (Math.random() - 0.5) * 10),
probability: 0.7
},
{
name: "HealthComponent",
create: () => new HealthComponent(Math.floor(Math.random() * 100) + 50),
probability: 0.5
},
{
name: "RenderComponent",
create: () => new RenderComponent(`sprite_${Math.floor(Math.random() * 10)}`),
probability: 0.3
},
{
name: "AIComponent",
create: () => new AIComponent(['idle', 'patrol', 'chase'][Math.floor(Math.random() * 3)]),
probability: 0.2
}
];
for (const test of componentTests) {
const startTime = performance.now();
let addedCount = 0;
for (const entity of entities) {
if (Math.random() < test.probability) {
entity.addComponent(test.create());
addedCount++;
}
}
const endTime = performance.now();
const totalTime = endTime - startTime;
console.log(` ${test.name}:`);
console.log(` 添加数量: ${addedCount.toLocaleString()}`);
console.log(` 总时间: ${totalTime.toFixed(2)}ms`);
console.log(` 平均时间: ${(totalTime / addedCount).toFixed(4)}ms/组件`);
console.log(` 添加速度: ${(addedCount / (totalTime / 1000)).toFixed(0)} 组件/秒`);
}
}
/**
* 主测试函数
*/
async function runBenchmarks(): Promise<void> {
console.log('🎯 ECS框架性能基准测试');
console.log('='.repeat(60));
// 先运行实体创建性能测试
await runEntityCreationBenchmark();
// 然后运行完整的框架测试
console.log('\n🚀 完整框架性能测试');
console.log('='.repeat(60));
console.log(`\n⚙ 测试配置:`);
console.log(` 实体数量: ${TEST_CONFIG.entityCounts.map(n => n.toLocaleString()).join(', ')}`);
console.log(` 查询迭代: ${TEST_CONFIG.queryIterations.toLocaleString()}`);
console.log(` 更新迭代: ${TEST_CONFIG.updateIterations.toLocaleString()}`);
console.log(` 预计测试时间: ${(TEST_CONFIG.entityCounts.length * 2).toFixed(0)}-${(TEST_CONFIG.entityCounts.length * 5).toFixed(0)} 分钟`);
console.log('\n🔧 初始化ECS框架...');
// 初始化WebAssembly模块
try {
const { ecsCore } = await import('../../Utils/WasmCore');
await ecsCore.initialize();
console.log(`✅ WebAssembly模块: ${ecsCore.isUsingWasm() ? '已加载' : '未加载'}`);
} catch (error) {
console.log('⚠️ WebAssembly模块加载失败使用JavaScript实现');
}
const scene = new Scene();
// 等待初始化完成
await new Promise(resolve => setTimeout(resolve, 1000));
displaySystemInfo(scene);
const results: PerformanceResult[] = [];
const totalTests = TEST_CONFIG.entityCounts.length;
// 运行不同规模的测试
for (let i = 0; i < TEST_CONFIG.entityCounts.length; i++) {
const entityCount = TEST_CONFIG.entityCounts[i];
console.log(`\n🔄 进度: ${i + 1}/${totalTests} (${((i + 1) / totalTests * 100).toFixed(1)}%)`);
const result = runPerformanceTest(scene, entityCount, TEST_CONFIG);
results.push(result);
// 清理场景,准备下一轮测试
console.log(` 🧹 清理内存...`);
scene.end();
scene.begin();
// 强制垃圾回收
if (typeof global !== 'undefined' && global.gc) {
global.gc();
}
// 大规模测试间隔稍作休息
if (entityCount >= 100000) {
console.log(` ⏱️ 等待系统稳定...`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
displayResults(results, scene);
scene.end();
console.log('\n✅ 性能测试完成!');
console.log(`📊 总测试时间: ${((Date.now() - startTime) / 1000 / 60).toFixed(1)} 分钟`);
}
// 记录开始时间
const startTime = Date.now();
// 运行测试
runBenchmarks().catch(error => {
console.error('❌ 测试失败:', error);
});

View File

@@ -0,0 +1,53 @@
const { Scene } = require('./bin/ECS/Scene.js');
const { Component } = require('./bin/ECS/Component.js');
// 简单的组件类
class TestComponent extends Component {
constructor(value) {
super();
this.value = value;
}
}
console.log('🔬 组件添加性能分析');
// 创建场景和实体
const scene = new Scene();
console.log('✅ 创建场景完成');
const startCreate = performance.now();
const entities = scene.createEntities(5000, 'TestEntity');
const endCreate = performance.now();
console.log(`✅ 创建了 ${entities.length} 个实体,耗时: ${(endCreate - startCreate).toFixed(2)}ms`);
// 测试单个组件添加性能
console.log('\n📊 测试组件添加性能:');
const startAdd = performance.now();
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
entity.addComponent(new TestComponent(i));
}
const endAdd = performance.now();
const addTime = endAdd - startAdd;
console.log(`添加 ${entities.length} 个组件耗时: ${addTime.toFixed(2)}ms`);
console.log(`平均每个组件: ${(addTime / entities.length).toFixed(4)}ms`);
console.log(`添加速度: ${(entities.length / (addTime / 1000)).toFixed(0)} 组件/秒`);
// 测试组件获取性能
console.log('\n📊 测试组件获取性能:');
const startGet = performance.now();
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
const component = entity.getComponent(TestComponent);
}
const endGet = performance.now();
const getTime = endGet - startGet;
console.log(`获取 ${entities.length} 个组件耗时: ${getTime.toFixed(2)}ms`);
console.log(`平均每个组件: ${(getTime / entities.length).toFixed(4)}ms`);
console.log(`获取速度: ${(entities.length / (getTime / 1000)).toFixed(0)} 组件/秒`);

View File

@@ -0,0 +1,199 @@
import { BitMaskOptimizer } from '../../ECS/Core/BitMaskOptimizer';
/**
* 位掩码优化器测试
*/
function testBitMaskOptimizer(): void {
console.log('🧪 测试位掩码优化器');
const optimizer = BitMaskOptimizer.getInstance();
optimizer.reset();
// 测试组件类型注册
console.log(' 📝 测试组件类型注册...');
const positionId = optimizer.registerComponentType('Position');
const velocityId = optimizer.registerComponentType('Velocity');
const healthId = optimizer.registerComponentType('Health');
console.log(` Position ID: ${positionId}`);
console.log(` Velocity ID: ${velocityId}`);
console.log(` Health ID: ${healthId}`);
// 测试单个组件掩码
console.log(' 🎯 测试单个组件掩码...');
const positionMask = optimizer.createSingleComponentMask('Position');
const velocityMask = optimizer.createSingleComponentMask('Velocity');
console.log(` Position掩码: ${positionMask.toString(2)}`);
console.log(` Velocity掩码: ${velocityMask.toString(2)}`);
// 测试组合掩码
console.log(' 🔗 测试组合掩码...');
const combinedMask = optimizer.createCombinedMask(['Position', 'Velocity']);
console.log(` Position+Velocity掩码: ${combinedMask.toString(2)}`);
// 测试掩码包含检查
console.log(' ✅ 测试掩码包含检查...');
const hasPosition = optimizer.maskContainsComponent(combinedMask, 'Position');
const hasVelocity = optimizer.maskContainsComponent(combinedMask, 'Velocity');
const hasHealth = optimizer.maskContainsComponent(combinedMask, 'Health');
console.log(` 包含Position: ${hasPosition}`);
console.log(` 包含Velocity: ${hasVelocity}`);
console.log(` 包含Health: ${hasHealth}`);
// 测试掩码操作
console.log(' 🔧 测试掩码操作...');
let entityMask = 0n;
entityMask = optimizer.addComponentToMask(entityMask, 'Position');
entityMask = optimizer.addComponentToMask(entityMask, 'Health');
console.log(` 添加Position和Health后: ${entityMask.toString(2)}`);
const hasAll = optimizer.maskContainsAllComponents(entityMask, ['Position', 'Health']);
const hasAny = optimizer.maskContainsAnyComponent(entityMask, ['Position', 'Velocity']);
console.log(` 包含Position和Health: ${hasAll}`);
console.log(` 包含Position或Velocity: ${hasAny}`);
// 测试掩码分析
console.log(' 📊 测试掩码分析...');
const componentNames = optimizer.maskToComponentNames(entityMask);
const componentCount = optimizer.getComponentCount(entityMask);
console.log(` 掩码包含的组件: ${componentNames.join(', ')}`);
console.log(` 组件数量: ${componentCount}`);
// 测试缓存统计
console.log(' 📈 测试缓存统计...');
const stats = optimizer.getCacheStats();
console.log(` 缓存大小: ${stats.size}`);
console.log(` 组件类型数量: ${stats.componentTypes}`);
// 测试预计算常用掩码
console.log(' ⚡ 测试预计算常用掩码...');
const commonCombinations = [
['Position', 'Velocity'],
['Position', 'Health'],
['Position', 'Velocity', 'Health']
];
optimizer.precomputeCommonMasks(commonCombinations);
const statsAfterPrecompute = optimizer.getCacheStats();
console.log(` 预计算后缓存大小: ${statsAfterPrecompute.size}`);
console.log('✅ 位掩码优化器测试完成');
}
/**
* 性能测试
*/
function testBitMaskPerformance(): void {
console.log('\n🚀 位掩码优化器性能测试');
const optimizer = BitMaskOptimizer.getInstance();
optimizer.reset();
// 注册组件类型
const componentTypes = ['Position', 'Velocity', 'Health', 'Render', 'AI', 'Physics', 'Audio', 'Network'];
for (const type of componentTypes) {
optimizer.registerComponentType(type);
}
const iterations = 100000;
// 测试单个掩码创建性能
console.log(' 🔥 测试单个掩码创建性能...');
let start = performance.now();
for (let i = 0; i < iterations; i++) {
optimizer.createSingleComponentMask('Position');
}
let end = performance.now();
console.log(` ${iterations}次单个掩码创建: ${(end - start).toFixed(2)}ms`);
// 测试组合掩码创建性能
console.log(' 🔥 测试组合掩码创建性能...');
start = performance.now();
for (let i = 0; i < iterations; i++) {
optimizer.createCombinedMask(['Position', 'Velocity', 'Health']);
}
end = performance.now();
console.log(` ${iterations}次组合掩码创建: ${(end - start).toFixed(2)}ms`);
// 测试掩码检查性能
console.log(' 🔥 测试掩码检查性能...');
const testMask = optimizer.createCombinedMask(['Position', 'Velocity', 'Health']);
start = performance.now();
for (let i = 0; i < iterations; i++) {
optimizer.maskContainsComponent(testMask, 'Position');
optimizer.maskContainsComponent(testMask, 'AI');
}
end = performance.now();
console.log(` ${iterations * 2}次掩码检查: ${(end - start).toFixed(2)}ms`);
// 对比原生位操作性能
console.log(' ⚖️ 对比原生位操作性能...');
const positionBit = 1n << 0n;
const velocityBit = 1n << 1n;
const healthBit = 1n << 2n;
const nativeMask = positionBit | velocityBit | healthBit;
start = performance.now();
for (let i = 0; i < iterations; i++) {
(nativeMask & positionBit) !== 0n;
(nativeMask & (1n << 7n)) !== 0n; // AI位
}
end = performance.now();
console.log(` ${iterations * 2}次原生位操作: ${(end - start).toFixed(2)}ms`);
console.log('✅ 性能测试完成');
}
/**
* 内存使用测试
*/
function testBitMaskMemoryUsage(): void {
console.log('\n💾 位掩码优化器内存使用测试');
const optimizer = BitMaskOptimizer.getInstance();
optimizer.reset();
// 注册大量组件类型
console.log(' 📝 注册组件类型...');
for (let i = 0; i < 100; i++) {
optimizer.registerComponentType(`Component${i}`);
}
// 创建大量掩码组合
console.log(' 🔗 创建掩码组合...');
const maskCount = 1000;
for (let i = 0; i < maskCount; i++) {
const componentCount = Math.floor(Math.random() * 5) + 1;
const components: string[] = [];
for (let j = 0; j < componentCount; j++) {
components.push(`Component${Math.floor(Math.random() * 100)}`);
}
optimizer.createCombinedMask(components);
}
const stats = optimizer.getCacheStats();
console.log(` 📊 最终统计:`);
console.log(` 组件类型数量: ${stats.componentTypes}`);
console.log(` 缓存掩码数量: ${stats.size}`);
console.log(` 平均每个掩码占用: ~${(stats.size * 64 / 1024).toFixed(2)} KB`);
console.log('✅ 内存使用测试完成');
}
// 运行所有测试
export function runBitMaskOptimizerTests(): void {
console.log('🧪 位掩码优化器测试套件');
console.log('='.repeat(50));
testBitMaskOptimizer();
testBitMaskPerformance();
testBitMaskMemoryUsage();
console.log('\n✅ 所有测试完成');
}

View File

@@ -0,0 +1,189 @@
import { ComponentPool, ComponentPoolManager } from '../../ECS/Core/ComponentPool';
import { Component } from '../../ECS/Component';
/**
* 测试用组件
*/
class TestComponent extends Component {
public value: number = 0;
constructor(value: number = 0) {
super();
this.value = value;
}
reset(): void {
this.value = 0;
}
}
/**
* 运行组件对象池测试
*/
export function runComponentPoolTests(): void {
console.log('🧪 组件对象池测试');
console.log('='.repeat(50));
testBasicFunctionality();
testPoolManager();
testPerformance();
console.log('✅ 组件对象池测试完成');
}
/**
* 基础功能测试
*/
function testBasicFunctionality(): void {
console.log('\n📝 基础功能测试...');
const pool = new ComponentPool(
() => new TestComponent(),
(component) => component.reset(),
10
);
// 测试获取新组件实例
console.log(' 测试获取新组件实例...');
const component = pool.acquire();
console.assert(component instanceof TestComponent, '应该返回TestComponent实例');
console.assert(component.value === 0, '新组件的值应该为0');
// 测试释放和复用
console.log(' 测试组件释放和复用...');
component.value = 42;
pool.release(component);
console.assert(pool.getAvailableCount() === 1, '池中应该有1个可用组件');
const reusedComponent = pool.acquire();
console.assert(reusedComponent === component, '应该复用同一个组件实例');
console.assert(reusedComponent.value === 0, '复用的组件应该被重置');
// 测试预填充
console.log(' 测试对象池预填充...');
pool.prewarm(5);
console.assert(pool.getAvailableCount() === 5, '预填充后应该有5个可用组件');
const components: TestComponent[] = [];
for (let i = 0; i < 5; i++) {
components.push(pool.acquire());
}
console.assert(pool.getAvailableCount() === 0, '获取5个组件后池应该为空');
// 测试最大容量限制
console.log(' 测试最大容量限制...');
pool.prewarm(10);
const extraComponent = new TestComponent();
pool.release(extraComponent);
console.assert(pool.getAvailableCount() === 10, '不应该超过最大容量');
// 测试清空池
console.log(' 测试清空对象池...');
pool.clear();
console.assert(pool.getAvailableCount() === 0, '清空后池应该为空');
console.log(' ✅ 基础功能测试通过');
}
/**
* 池管理器测试
*/
function testPoolManager(): void {
console.log('\n📝 池管理器测试...');
const manager = ComponentPoolManager.getInstance();
manager.clearAll();
// 测试单例模式
console.log(' 测试单例模式...');
const manager1 = ComponentPoolManager.getInstance();
const manager2 = ComponentPoolManager.getInstance();
console.assert(manager1 === manager2, '应该返回同一个实例');
// 测试注册组件池
console.log(' 测试注册组件池...');
manager.registerPool(
'TestComponent',
() => new TestComponent(),
(component) => component.reset(),
5
);
const stats = manager.getPoolStats();
console.assert(stats.has('TestComponent'), '应该包含已注册的组件类型');
console.assert(stats.get('TestComponent')?.maxSize === 5, '最大容量应该为5');
// 测试获取和释放组件
console.log(' 测试获取和释放组件...');
const component = manager.acquireComponent<TestComponent>('TestComponent');
console.assert(component instanceof TestComponent, '应该返回TestComponent实例');
if (component) {
component.value = 42;
manager.releaseComponent('TestComponent', component);
const reusedComponent = manager.acquireComponent<TestComponent>('TestComponent');
console.assert(reusedComponent === component, '应该复用同一个组件');
console.assert(reusedComponent?.value === 0, '复用的组件应该被重置');
}
// 测试预热所有池
console.log(' 测试预热所有池...');
manager.registerPool('TestComponent1', () => new TestComponent());
manager.registerPool('TestComponent2', () => new TestComponent());
manager.prewarmAll(3);
const finalStats = manager.getPoolStats();
console.assert(finalStats.get('TestComponent1')?.available === 3, 'TestComponent1应该有3个可用组件');
console.assert(finalStats.get('TestComponent2')?.available === 3, 'TestComponent2应该有3个可用组件');
// 测试未注册的组件类型
console.log(' 测试未注册的组件类型...');
const nullComponent = manager.acquireComponent('NonExistentComponent');
console.assert(nullComponent === null, '未注册的组件类型应该返回null');
manager.clearAll();
console.log(' ✅ 池管理器测试通过');
}
/**
* 性能测试
*/
function testPerformance(): void {
console.log('\n📝 性能测试...');
const pool = new ComponentPool(() => new TestComponent());
const iterations = 10000;
// 预热池
pool.prewarm(100);
// 测试对象池性能
const poolStart = performance.now();
for (let i = 0; i < iterations; i++) {
const component = pool.acquire();
pool.release(component);
}
const poolEnd = performance.now();
const poolTime = poolEnd - poolStart;
// 测试直接创建性能
const directStart = performance.now();
for (let i = 0; i < iterations; i++) {
new TestComponent();
}
const directEnd = performance.now();
const directTime = directEnd - directStart;
console.log(` 对象池时间: ${poolTime.toFixed(2)}ms`);
console.log(` 直接创建时间: ${directTime.toFixed(2)}ms`);
const improvement = ((directTime - poolTime) / directTime * 100);
console.log(` 性能提升: ${improvement.toFixed(1)}%`);
if (poolTime < directTime) {
console.log(' ✅ 对象池性能测试通过 - 比直接创建更快');
} else {
console.log(' ⚠️ 对象池在小规模测试中可能不如直接创建快');
}
}

View File

@@ -1,420 +0,0 @@
#!/usr/bin/env node
/**
* ECS框架基准测试 - 简化版本
* 专门测试框架本身的性能不依赖复杂的ECS实现
*/
console.log('🚀 ECS框架性能基准测试');
console.log('='.repeat(60));
console.log('测试目标: 框架本身的性能极限,不包含复杂游戏逻辑');
console.log('='.repeat(60));
// 模拟简单的实体和组件
class MockEntity {
public id: number;
public components = new Map<string, any>();
public tags = new Set<string>();
public enabled: boolean = true;
constructor(id: number) {
this.id = id;
}
addComponent(type: string, data: any): void {
this.components.set(type, data);
}
getComponent(type: string): any {
return this.components.get(type);
}
hasComponent(type: string): boolean {
return this.components.has(type);
}
removeComponent(type: string): void {
this.components.delete(type);
}
addTag(tag: string): void {
this.tags.add(tag);
}
hasTag(tag: string): boolean {
return this.tags.has(tag);
}
removeTag(tag: string): void {
this.tags.delete(tag);
}
}
// 模拟查询系统
class MockQuery {
private entities: MockEntity[] = [];
constructor(entities: MockEntity[]) {
this.entities = entities;
}
// 查询包含指定组件的实体
withComponents(...componentTypes: string[]): MockEntity[] {
return this.entities.filter(entity =>
componentTypes.every(type => entity.hasComponent(type))
);
}
// 查询包含指定标签的实体
withTags(...tags: string[]): MockEntity[] {
return this.entities.filter(entity =>
tags.every(tag => entity.hasTag(tag))
);
}
// 查询启用的实体
enabled(): MockEntity[] {
return this.entities.filter(entity => entity.enabled);
}
// 查询禁用的实体
disabled(): MockEntity[] {
return this.entities.filter(entity => !entity.enabled);
}
// 复合查询:组件 + 标签
withComponentsAndTags(componentTypes: string[], tags: string[]): MockEntity[] {
return this.entities.filter(entity =>
componentTypes.every(type => entity.hasComponent(type)) &&
tags.every(tag => entity.hasTag(tag)) &&
entity.enabled
);
}
// 排除查询:不包含指定组件
withoutComponents(...componentTypes: string[]): MockEntity[] {
return this.entities.filter(entity =>
!componentTypes.some(type => entity.hasComponent(type))
);
}
}
// 测试函数
function testEntityCreation(count: number): number {
const startTime = performance.now();
const entities: MockEntity[] = [];
for (let i = 0; i < count; i++) {
const entity = new MockEntity(i);
entity.addComponent('position', { x: i * 0.1, y: i * 0.2 });
entity.addComponent('velocity', { vx: 1, vy: 1 });
// 添加一些标签和状态
if (i % 2 === 0) entity.addTag('even');
if (i % 3 === 0) entity.addTag('player');
if (i % 5 === 0) entity.addTag('enemy');
if (i % 10 === 0) entity.enabled = false;
entities.push(entity);
}
return performance.now() - startTime;
}
function testComponentAccess(entities: MockEntity[], iterations: number): number {
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
for (const entity of entities) {
const pos = entity.getComponent('position');
const vel = entity.getComponent('velocity');
if (pos && vel) {
pos.x += vel.vx * 0.016;
pos.y += vel.vy * 0.016;
}
}
}
return performance.now() - startTime;
}
function testComponentAddRemove(entities: MockEntity[], iterations: number): number {
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
for (const entity of entities) {
entity.addComponent('temp', { value: i });
entity.removeComponent('temp');
}
}
return performance.now() - startTime;
}
function testSingleComponentQuery(entities: MockEntity[], iterations: number): number {
const query = new MockQuery(entities);
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
const result = query.withComponents('position');
}
return performance.now() - startTime;
}
function testMultiComponentQuery(entities: MockEntity[], iterations: number): number {
const query = new MockQuery(entities);
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
const result = query.withComponents('position', 'velocity');
}
return performance.now() - startTime;
}
function testTagQuery(entities: MockEntity[], iterations: number): number {
const query = new MockQuery(entities);
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
const players = query.withTags('player');
const enemies = query.withTags('enemy');
}
return performance.now() - startTime;
}
function testComplexQuery(entities: MockEntity[], iterations: number): number {
const query = new MockQuery(entities);
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
const result = query.withComponentsAndTags(['position', 'velocity'], ['player']);
}
return performance.now() - startTime;
}
function testExclusionQuery(entities: MockEntity[], iterations: number): number {
const query = new MockQuery(entities);
const startTime = performance.now();
for (let i = 0; i < iterations; i++) {
const result = query.withoutComponents('temp', 'disabled');
}
return performance.now() - startTime;
}
function testComponentExistence(entities: MockEntity[], iterations: number): number {
const startTime = performance.now();
let count = 0;
for (let i = 0; i < iterations; i++) {
for (const entity of entities) {
if (entity.hasComponent('position') && entity.hasComponent('velocity')) {
count++;
}
}
}
return performance.now() - startTime;
}
// 运行基准测试
async function runBenchmarks(): Promise<void> {
console.log('\n📊 1. 实体创建性能测试');
console.log('-'.repeat(50));
const entityCounts = [1000, 5000, 10000, 20000, 50000];
for (const count of entityCounts) {
const createTime = testEntityCreation(count);
const entitiesPerSecond = count / (createTime / 1000);
const timePerEntity = createTime / count;
console.log(`${count.toString().padStart(6)} 个实体: ${createTime.toFixed(2)}ms (${entitiesPerSecond.toFixed(0)}个/秒, ${timePerEntity.toFixed(4)}ms/个)`);
}
console.log('\n🔍 2. 组件访问性能测试');
console.log('-'.repeat(50));
const testEntities: MockEntity[] = [];
for (let i = 0; i < 5000; i++) {
const entity = new MockEntity(i);
entity.addComponent('position', { x: i * 0.1, y: i * 0.2 });
entity.addComponent('velocity', { vx: 1, vy: 1 });
// 添加标签和状态
if (i % 2 === 0) entity.addTag('even');
if (i % 3 === 0) entity.addTag('player');
if (i % 5 === 0) entity.addTag('enemy');
if (i % 10 === 0) entity.enabled = false;
testEntities.push(entity);
}
const accessIterations = [100, 500, 1000, 2000];
for (const iterations of accessIterations) {
const accessTime = testComponentAccess(testEntities, iterations);
const accessesPerSecond = (testEntities.length * iterations) / (accessTime / 1000);
const timePerAccess = accessTime / (testEntities.length * iterations);
console.log(`${iterations.toString().padStart(4)} 次迭代: ${accessTime.toFixed(2)}ms (${accessesPerSecond.toFixed(0)}次访问/秒, ${(timePerAccess * 1000).toFixed(3)}μs/次)`);
}
console.log('\n🧪 3. 组件添加/删除性能测试');
console.log('-'.repeat(50));
const addRemoveIterations = [100, 500, 1000];
for (const iterations of addRemoveIterations) {
const addRemoveTime = testComponentAddRemove(testEntities, iterations);
const operationsPerSecond = (testEntities.length * iterations * 2) / (addRemoveTime / 1000); // *2 for add+remove
const timePerOperation = addRemoveTime / (testEntities.length * iterations * 2);
console.log(`${iterations.toString().padStart(4)} 次迭代: ${addRemoveTime.toFixed(2)}ms (${operationsPerSecond.toFixed(0)}次操作/秒, ${(timePerOperation * 1000).toFixed(3)}μs/次)`);
}
console.log('\n🔎 4. 查询系统性能测试');
console.log('-'.repeat(50));
const queryIterations = [100, 500, 1000];
console.log('4.1 单组件查询:');
for (const iterations of queryIterations) {
const queryTime = testSingleComponentQuery(testEntities, iterations);
const queriesPerSecond = iterations / (queryTime / 1000);
const timePerQuery = queryTime / iterations;
console.log(` ${iterations.toString().padStart(4)} 次查询: ${queryTime.toFixed(2)}ms (${queriesPerSecond.toFixed(0)}次/秒, ${timePerQuery.toFixed(3)}ms/次)`);
}
console.log('4.2 多组件查询:');
for (const iterations of queryIterations) {
const queryTime = testMultiComponentQuery(testEntities, iterations);
const queriesPerSecond = iterations / (queryTime / 1000);
const timePerQuery = queryTime / iterations;
console.log(` ${iterations.toString().padStart(4)} 次查询: ${queryTime.toFixed(2)}ms (${queriesPerSecond.toFixed(0)}次/秒, ${timePerQuery.toFixed(3)}ms/次)`);
}
console.log('4.3 标签查询:');
for (const iterations of queryIterations) {
const queryTime = testTagQuery(testEntities, iterations);
const queriesPerSecond = (iterations * 2) / (queryTime / 1000); // *2 for player+enemy queries
const timePerQuery = queryTime / (iterations * 2);
console.log(` ${iterations.toString().padStart(4)} 次查询: ${queryTime.toFixed(2)}ms (${queriesPerSecond.toFixed(0)}次/秒, ${timePerQuery.toFixed(3)}ms/次)`);
}
console.log('4.4 复合查询 (组件+标签):');
for (const iterations of queryIterations) {
const queryTime = testComplexQuery(testEntities, iterations);
const queriesPerSecond = iterations / (queryTime / 1000);
const timePerQuery = queryTime / iterations;
console.log(` ${iterations.toString().padStart(4)} 次查询: ${queryTime.toFixed(2)}ms (${queriesPerSecond.toFixed(0)}次/秒, ${timePerQuery.toFixed(3)}ms/次)`);
}
console.log('4.5 排除查询:');
for (const iterations of queryIterations) {
const queryTime = testExclusionQuery(testEntities, iterations);
const queriesPerSecond = iterations / (queryTime / 1000);
const timePerQuery = queryTime / iterations;
console.log(` ${iterations.toString().padStart(4)} 次查询: ${queryTime.toFixed(2)}ms (${queriesPerSecond.toFixed(0)}次/秒, ${timePerQuery.toFixed(3)}ms/次)`);
}
console.log('4.6 组件存在性检查:');
for (const iterations of queryIterations) {
const checkTime = testComponentExistence(testEntities, iterations);
const checksPerSecond = (testEntities.length * iterations) / (checkTime / 1000);
const timePerCheck = checkTime / (testEntities.length * iterations);
console.log(` ${iterations.toString().padStart(4)} 次迭代: ${checkTime.toFixed(2)}ms (${checksPerSecond.toFixed(0)}次检查/秒, ${(timePerCheck * 1000).toFixed(3)}μs/次)`);
}
console.log('\n🎯 5. 寻找性能极限');
console.log('-'.repeat(50));
const limitTestSizes = [10000, 25000, 50000, 100000, 200000];
const targetFrameTime = 16.67; // 60FPS
for (const size of limitTestSizes) {
// 强制垃圾回收以获得更一致的测试结果
try {
if (typeof globalThis !== 'undefined' && (globalThis as any).gc) {
(globalThis as any).gc();
}
} catch (e) {
// 忽略垃圾回收错误
}
const entities: MockEntity[] = [];
// 创建实体 - 简化结构,只测试核心性能
const createStart = performance.now();
for (let i = 0; i < size; i++) {
const entity = new MockEntity(i);
entity.addComponent('position', { x: i * 0.1, y: i * 0.2 });
entity.addComponent('velocity', { vx: 1, vy: 1 });
entities.push(entity);
}
const createTime = performance.now() - createStart;
// 预热测试让JavaScript引擎优化代码
for (let warmup = 0; warmup < 10; warmup++) {
for (const entity of entities) {
const pos = entity.getComponent('position');
const vel = entity.getComponent('velocity');
if (pos && vel) {
pos.x += vel.vx * 0.016;
pos.y += vel.vy * 0.016;
}
}
}
// 使用固定时间测试而不是固定次数,这样更能反映真实性能
const testTimeMs = 1000; // 测试1秒钟
let frameCount = 0;
let totalFrameTime = 0;
const startTime = performance.now();
while (performance.now() - startTime < testTimeMs) {
const frameStart = performance.now();
for (const entity of entities) {
const pos = entity.getComponent('position');
const vel = entity.getComponent('velocity');
if (pos && vel) {
pos.x += vel.vx * 0.016;
pos.y += vel.vy * 0.016;
}
}
const frameTime = performance.now() - frameStart;
totalFrameTime += frameTime;
frameCount++;
}
const avgFrameTime = totalFrameTime / frameCount;
const fps = 1000 / avgFrameTime;
const actualFps = frameCount / ((performance.now() - startTime) / 1000);
const status = avgFrameTime <= targetFrameTime ? '✅' : avgFrameTime <= targetFrameTime * 2 ? '⚠️' : '❌';
console.log(`${size.toString().padStart(6)} 个实体: 创建${createTime.toFixed(2)}ms, 处理${avgFrameTime.toFixed(3)}ms/帧, 理论${fps.toFixed(1)}FPS, 实际${actualFps.toFixed(1)}FPS ${status}`);
console.log(`${' '.repeat(14)} 测试${frameCount}帧, 总时间${(performance.now() - startTime).toFixed(0)}ms`);
if (avgFrameTime > targetFrameTime * 3) {
console.log(`💥 性能极限: 约 ${size} 个实体时框架开始出现严重性能问题`);
break;
}
}
console.log('\n' + '='.repeat(60));
console.log('✅ ECS框架基准测试完成');
}
// 运行测试
runBenchmarks().catch(console.error);

View File

@@ -0,0 +1,182 @@
import { runBitMaskOptimizerTests } from './Unit/bitmask-optimizer.test';
import { runComponentPoolTests } from './Unit/component-pool.test';
/**
* 测试运行器 - 统一运行所有测试
*/
export class TestRunner {
private results: Map<string, { passed: number; failed: number; duration: number }> = new Map();
/**
* 运行所有单元测试
*/
async runUnitTests(): Promise<void> {
console.log('🧪 运行单元测试');
console.log('='.repeat(50));
await this.runTest('组件对象池', runComponentPoolTests);
await this.runTest('位掩码优化器', runBitMaskOptimizerTests);
console.log('\n📊 单元测试总结:');
this.printSummary();
}
/**
* 运行性能测试
*/
async runPerformanceTests(): Promise<void> {
console.log('\n🚀 运行性能测试');
console.log('='.repeat(50));
// 性能测试需要从benchmark.ts文件中导入
console.log('⚠️ 性能测试需要单独运行 - 请使用: node benchmark.ts');
console.log('\n📊 性能测试总结:');
this.printSummary();
}
/**
* 运行集成测试
*/
async runIntegrationTests(): Promise<void> {
console.log('\n🔗 运行集成测试');
console.log('='.repeat(50));
// 集成测试待实现
console.log('⚠️ 集成测试尚未实现');
}
/**
* 运行所有测试
*/
async runAllTests(): Promise<void> {
console.log('🎯 ECS框架完整测试套件');
console.log('='.repeat(60));
const startTime = performance.now();
await this.runUnitTests();
await this.runPerformanceTests();
await this.runIntegrationTests();
const endTime = performance.now();
const totalDuration = endTime - startTime;
console.log('\n✅ 所有测试完成');
console.log(`🕐 总测试时间: ${(totalDuration / 1000).toFixed(2)}`);
this.printFinalSummary();
}
/**
* 运行单个测试
*/
private async runTest(testName: string, testFunction: () => void | Promise<void>): Promise<void> {
console.log(`\n▶ 开始测试: ${testName}`);
const startTime = performance.now();
let passed = 0;
let failed = 0;
try {
await testFunction();
passed = 1;
console.log(`${testName} 测试通过`);
} catch (error) {
failed = 1;
console.error(`${testName} 测试失败:`, error);
}
const endTime = performance.now();
const duration = endTime - startTime;
this.results.set(testName, { passed, failed, duration });
console.log(`⏱️ 耗时: ${duration.toFixed(2)}ms`);
}
/**
* 打印测试摘要
*/
private printSummary(): void {
let totalPassed = 0;
let totalFailed = 0;
let totalDuration = 0;
for (const [name, result] of this.results) {
totalPassed += result.passed;
totalFailed += result.failed;
totalDuration += result.duration;
const status = result.failed > 0 ? '❌' : '✅';
console.log(` ${status} ${name}: ${result.duration.toFixed(2)}ms`);
}
console.log(`\n📈 测试统计:`);
console.log(` 通过: ${totalPassed}`);
console.log(` 失败: ${totalFailed}`);
console.log(` 总时间: ${totalDuration.toFixed(2)}ms`);
console.log(` 成功率: ${totalPassed + totalFailed > 0 ? (totalPassed / (totalPassed + totalFailed) * 100).toFixed(1) : 0}%`);
}
/**
* 打印最终测试摘要
*/
private printFinalSummary(): void {
console.log('\n📋 最终测试报告');
console.log('='.repeat(60));
let totalPassed = 0;
let totalFailed = 0;
for (const [, result] of this.results) {
totalPassed += result.passed;
totalFailed += result.failed;
}
if (totalFailed === 0) {
console.log('🎉 所有测试都通过了!');
} else {
console.log(`⚠️ 有 ${totalFailed} 个测试失败`);
}
console.log(`📊 测试覆盖率: ${this.results.size} 个测试模块`);
console.log(`✅ 通过率: ${totalPassed + totalFailed > 0 ? (totalPassed / (totalPassed + totalFailed) * 100).toFixed(1) : 0}%`);
}
/**
* 清除测试结果
*/
clearResults(): void {
this.results.clear();
}
}
/**
* 便捷函数:运行所有测试
*/
export async function runAllTests(): Promise<void> {
const runner = new TestRunner();
await runner.runAllTests();
}
/**
* 便捷函数:仅运行单元测试
*/
export async function runUnitTests(): Promise<void> {
const runner = new TestRunner();
await runner.runUnitTests();
}
/**
* 便捷函数:仅运行性能测试
*/
export async function runPerformanceTests(): Promise<void> {
const runner = new TestRunner();
await runner.runPerformanceTests();
}
// 如果直接运行此文件,执行所有测试
if (require.main === module) {
runAllTests().catch(console.error);
}

View File

@@ -1,409 +0,0 @@
/**
* ECS框架加速提供者接口
*
* 提供可替换的性能加速实现专注于ECS实体查询功能
* 支持JavaScript、WebAssembly等不同后端实现
*/
// ================================
// 核心接口定义
// ================================
/**
* 实体查询结果
*/
export interface QueryResult {
/** 查询到的实体ID数组 */
entities: Uint32Array;
/** 查询到的实体数量 */
count: number;
}
/**
* 实体查询接口
*
* 提供高性能的ECS实体查询功能
*/
export interface QueryProvider {
/**
* 根据单个组件掩码查询实体
* @param componentMask 组件掩码
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryByComponent(componentMask: bigint, maxResults: number): QueryResult;
/**
* 根据多个组件掩码查询实体AND操作
* @param componentMasks 组件掩码数组
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryByComponents(componentMasks: bigint[], maxResults: number): QueryResult;
/**
* 查询包含指定组件但排除其他组件的实体
* @param includeMask 必须包含的组件掩码
* @param excludeMask 必须排除的组件掩码
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryExcluding(includeMask: bigint, excludeMask: bigint, maxResults: number): QueryResult;
/**
* 更新实体的组件掩码
* @param entityId 实体ID
* @param componentMask 新的组件掩码
*/
updateEntityMask(entityId: number, componentMask: bigint): void;
/**
* 批量更新实体掩码
* @param entityIds 实体ID数组
* @param masks 掩码数组
*/
batchUpdateMasks(entityIds: Uint32Array, masks: BigUint64Array): void;
}
/**
* 加速提供者接口
*
* 定义了ECS框架加速提供者的基本契约
*/
export interface AccelerationProvider {
/** 提供者名称 */
readonly name: string;
/** 提供者版本 */
readonly version: string;
/** 是否为WebAssembly实现 */
readonly isWasm: boolean;
/** 实体查询功能模块 */
query: QueryProvider;
/**
* 初始化提供者
* @throws {Error} 初始化失败时抛出错误
*/
initialize(): Promise<void>;
/**
* 检查是否支持指定功能
* @param feature 功能名称
* @returns 是否支持该功能
*/
supports(feature: string): boolean;
/**
* 获取性能信息
* @returns 性能统计信息
*/
getPerformanceInfo(): {
/** 每秒操作数 */
operationsPerSecond: number;
/** 内存使用量(字节) */
memoryUsage: number;
/** 支持的功能列表 */
features: string[];
};
/**
* 清理资源
*/
dispose(): void;
}
// ================================
// JavaScript实现
// ================================
/**
* JavaScript实现的基础加速提供者
*
* 提供纯JavaScript的ECS查询实现作为默认后端
*/
export class JavaScriptProvider implements AccelerationProvider {
readonly name = 'JavaScript';
readonly version = '1.0.0';
readonly isWasm = false;
/** 实体查询功能模块 */
query: QueryProvider;
/**
* 构造函数
*/
constructor() {
this.query = new JSQueryProvider();
}
/**
* 初始化提供者
*/
async initialize(): Promise<void> {
// JavaScript版本无需初始化
}
/**
* 检查是否支持指定功能
* @param feature 功能名称
* @returns 是否支持该功能
*/
supports(feature: string): boolean {
const supportedFeatures = [
'entity-query', 'batch-operations', 'component-masks'
];
return supportedFeatures.includes(feature);
}
/**
* 获取性能信息
* @returns 性能统计信息
*/
getPerformanceInfo() {
return {
operationsPerSecond: 1000000, // 100万次/秒
memoryUsage: 0,
features: ['entity-query', 'batch-operations', 'component-masks']
};
}
/**
* 清理资源
*/
dispose(): void {
// JavaScript版本无需清理
}
}
/**
* JavaScript查询实现
*
* 使用Map存储实体掩码提供基础的查询功能
*/
class JSQueryProvider implements QueryProvider {
/** 实体掩码存储 */
private entityMasks = new Map<number, bigint>();
/**
* 根据单个组件掩码查询实体
* @param componentMask 组件掩码
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryByComponent(componentMask: bigint, maxResults: number): QueryResult {
const results = new Uint32Array(maxResults);
let count = 0;
for (const [entityId, mask] of this.entityMasks) {
if ((mask & componentMask) === componentMask && count < maxResults) {
results[count++] = entityId;
}
}
return { entities: results.slice(0, count), count };
}
/**
* 根据多个组件掩码查询实体AND操作
* @param componentMasks 组件掩码数组
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryByComponents(componentMasks: bigint[], maxResults: number): QueryResult {
const results = new Uint32Array(maxResults);
let count = 0;
for (const [entityId, mask] of this.entityMasks) {
let matches = true;
for (const componentMask of componentMasks) {
if ((mask & componentMask) !== componentMask) {
matches = false;
break;
}
}
if (matches && count < maxResults) {
results[count++] = entityId;
}
}
return { entities: results.slice(0, count), count };
}
/**
* 查询包含指定组件但排除其他组件的实体
* @param includeMask 必须包含的组件掩码
* @param excludeMask 必须排除的组件掩码
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryExcluding(includeMask: bigint, excludeMask: bigint, maxResults: number): QueryResult {
const results = new Uint32Array(maxResults);
let count = 0;
for (const [entityId, mask] of this.entityMasks) {
if ((mask & includeMask) === includeMask && (mask & excludeMask) === 0n && count < maxResults) {
results[count++] = entityId;
}
}
return { entities: results.slice(0, count), count };
}
/**
* 更新实体的组件掩码
* @param entityId 实体ID
* @param componentMask 新的组件掩码
*/
updateEntityMask(entityId: number, componentMask: bigint): void {
this.entityMasks.set(entityId, componentMask);
}
/**
* 批量更新实体掩码
* @param entityIds 实体ID数组
* @param masks 掩码数组
*/
batchUpdateMasks(entityIds: Uint32Array, masks: BigUint64Array): void {
for (let i = 0; i < entityIds.length; i++) {
this.entityMasks.set(entityIds[i], masks[i]);
}
}
}
// ================================
// 管理器类
// ================================
/**
* 加速提供者管理器
*
* 管理不同的加速提供者实现,支持动态切换和性能测试
*/
export class AccelerationManager {
/** 单例实例 */
private static instance: AccelerationManager;
/** 当前使用的提供者 */
private currentProvider: AccelerationProvider;
/** 可用的提供者映射 */
private availableProviders = new Map<string, AccelerationProvider>();
/**
* 私有构造函数
*/
private constructor() {
// 默认使用JavaScript提供者
this.currentProvider = new JavaScriptProvider();
this.availableProviders.set('javascript', this.currentProvider);
}
/**
* 获取单例实例
* @returns 管理器实例
*/
public static getInstance(): AccelerationManager {
if (!AccelerationManager.instance) {
AccelerationManager.instance = new AccelerationManager();
}
return AccelerationManager.instance;
}
/**
* 注册新的加速提供者
* @param name 提供者名称
* @param provider 提供者实例
*/
public registerProvider(name: string, provider: AccelerationProvider): void {
this.availableProviders.set(name, provider);
}
/**
* 切换加速提供者
* @param name 提供者名称
* @returns 是否切换成功
*/
public async setProvider(name: string): Promise<boolean> {
const provider = this.availableProviders.get(name);
if (!provider) {
console.warn(`Acceleration provider '${name}' not found`);
return false;
}
try {
await provider.initialize();
this.currentProvider = provider;
console.log(`Switched to acceleration provider: ${provider.name} v${provider.version}`);
return true;
} catch (error) {
console.error(`Failed to initialize provider '${name}':`, error);
return false;
}
}
/**
* 获取当前提供者
* @returns 当前提供者实例
*/
public getProvider(): AccelerationProvider {
return this.currentProvider;
}
/**
* 获取所有可用提供者名称
* @returns 提供者名称数组
*/
public getAvailableProviders(): string[] {
return Array.from(this.availableProviders.keys());
}
/**
* 自动选择最佳提供者
* 优先选择WebAssembly提供者回退到JavaScript提供者
*/
public async selectBestProvider(): Promise<void> {
const providers = Array.from(this.availableProviders.values());
// 优先选择WebAssembly提供者
const wasmProvider = providers.find(p => p.isWasm);
if (wasmProvider) {
const success = await this.setProvider(wasmProvider.name);
if (success) return;
}
// 回退到JavaScript提供者
await this.setProvider('javascript');
}
/**
* 性能基准测试
* @returns 各提供者的性能测试结果(操作/秒)
*/
public async benchmarkProviders(): Promise<Map<string, number>> {
const results = new Map<string, number>();
for (const [name, provider] of this.availableProviders) {
try {
await provider.initialize();
// 简单的查询性能测试
const start = performance.now();
const testMask = 0b1111n; // 测试掩码
for (let i = 0; i < 10000; i++) {
provider.query.queryByComponent(testMask, 100);
}
const end = performance.now();
results.set(name, 10000 / (end - start) * 1000); // 操作/秒
provider.dispose();
} catch (error) {
console.warn(`Benchmark failed for provider '${name}':`, error);
results.set(name, 0);
}
}
return results;
}
}

View File

@@ -1,439 +0,0 @@
/**
* WebAssembly桥接工具
*
* 提供WebAssembly模块的加载、初始化和内存管理功能
* 为ECS框架提供高性能的底层支持
*/
import {
AccelerationProvider,
QueryProvider,
QueryResult
} from './AccelerationProvider';
// ================================
// 类型定义和接口
// ================================
/**
* WebAssembly模块接口
* 定义了ECS相关的WASM函数签名
*/
interface WasmModule {
/** WebAssembly内存对象 */
memory: WebAssembly.Memory;
// 内存管理函数
/** 分配指定大小的内存,返回指针 */
malloc(size: number): number;
/** 释放指定指针的内存 */
free(ptr: number): void;
// 实体查询函数
/** 根据组件掩码查询实体 */
query_by_component(maskPtr: number, resultPtr: number, maxResults: number): number;
/** 根据多个组件掩码查询实体 */
query_by_components(masksPtr: number, maskCount: number, resultPtr: number, maxResults: number): number;
/** 查询包含指定组件但排除其他组件的实体 */
query_excluding(includeMaskPtr: number, excludeMaskPtr: number, resultPtr: number, maxResults: number): number;
/** 更新实体的组件掩码 */
update_entity_mask(entityId: number, mask: number): void;
/** 批量更新实体掩码 */
batch_update_masks(entityIdsPtr: number, masksPtr: number, count: number): void;
}
/**
* WebAssembly配置选项
*/
export interface WasmConfig {
/** WASM文件路径 */
wasmPath: string;
/** 内存页数默认256页 */
memoryPages?: number;
/** 是否启用SIMD默认true */
enableSIMD?: boolean;
/** 是否启用多线程默认false */
enableThreads?: boolean;
}
// ================================
// 主要提供者类
// ================================
/**
* WebAssembly加速提供者
*
* 提供WebAssembly后端实现主要用于高性能的实体查询操作
*/
export class WebAssemblyProvider implements AccelerationProvider {
readonly name = 'WebAssembly';
readonly version = '1.0.0';
readonly isWasm = true;
/** WASM模块实例 */
private wasmModule?: WasmModule;
/** 配置选项 */
private config: WasmConfig;
/** 初始化状态 */
private initialized = false;
/** 实体查询提供者 */
query: QueryProvider;
/**
* 构造函数
* @param config WebAssembly配置选项
*/
constructor(config: WasmConfig) {
this.config = {
memoryPages: 256,
enableSIMD: true,
enableThreads: false,
...config
};
// 创建查询功能模块的WebAssembly实现
this.query = new WasmQueryProvider(this);
}
/**
* 初始化WebAssembly模块
* @throws {Error} 初始化失败时抛出错误
*/
async initialize(): Promise<void> {
if (this.initialized) return;
try {
const wasmBytes = await this.loadWasmBytes();
const wasmModule = await this.instantiateWasm(wasmBytes);
this.wasmModule = wasmModule;
this.initialized = true;
console.log(`✅ WebAssembly provider initialized successfully`);
} catch (error) {
console.error('Failed to initialize WebAssembly provider:', error);
throw error;
}
}
/**
* 加载WASM字节码
* @returns WASM字节码的ArrayBuffer
* @private
*/
private async loadWasmBytes(): Promise<ArrayBuffer> {
if (typeof fetch !== 'undefined') {
// 浏览器环境
const response = await fetch(this.config.wasmPath);
if (!response.ok) {
throw new Error(`Failed to load WASM file: ${response.statusText}`);
}
return response.arrayBuffer();
} else {
// Node.js环境 - 需要在运行时动态导入
throw new Error('Node.js environment not supported in browser build. Please use fetch() or provide ArrayBuffer directly.');
}
}
/**
* 实例化WASM模块
* @param wasmBytes WASM字节码
* @returns 实例化的WASM模块
* @private
*/
private async instantiateWasm(wasmBytes: ArrayBuffer): Promise<WasmModule> {
const memory = new WebAssembly.Memory({
initial: this.config.memoryPages!,
maximum: this.config.memoryPages! * 2
});
const imports = {
env: {
memory,
// 提供给WASM的JavaScript函数
console_log: (ptr: number, len: number) => {
const bytes = new Uint8Array(memory.buffer, ptr, len);
const str = new TextDecoder().decode(bytes);
console.log('[WASM]', str);
},
performance_now: () => performance.now()
}
};
const wasmModule = await WebAssembly.instantiate(wasmBytes, imports);
return wasmModule.instance.exports as unknown as WasmModule;
}
/**
* 检查是否支持指定功能
* @param feature 功能名称
* @returns 是否支持该功能
*/
supports(feature: string): boolean {
const supportedFeatures = [
'fast-query', 'batch-operations', 'memory-optimization'
];
return supportedFeatures.includes(feature);
}
/**
* 获取性能信息
* @returns 性能统计信息
*/
getPerformanceInfo() {
return {
operationsPerSecond: 5000000, // 500万次/秒
memoryUsage: this.wasmModule?.memory.buffer.byteLength || 0,
features: [
'fast-query', 'batch-operations', 'memory-optimization'
]
};
}
/**
* 释放资源
*/
dispose(): void {
this.wasmModule = undefined;
this.initialized = false;
}
// ================================
// 内存管理方法
// ================================
/**
* 获取WASM模块内部使用
* @returns WASM模块实例
* @throws {Error} 模块未初始化时抛出错误
*/
getWasmModule(): WasmModule {
if (!this.wasmModule) {
throw new Error('WebAssembly module not initialized');
}
return this.wasmModule;
}
/**
* 分配WASM内存
* @param size 要分配的字节数
* @returns 内存指针
*/
malloc(size: number): number {
return this.getWasmModule().malloc(size);
}
/**
* 释放WASM内存
* @param ptr 内存指针
*/
free(ptr: number): void {
this.getWasmModule().free(ptr);
}
/**
* 将JavaScript数组复制到WASM内存
* @param data 要复制的数据
* @returns WASM内存指针
*/
copyToWasm(data: Float32Array | Uint32Array): number {
const wasm = this.getWasmModule();
const ptr = wasm.malloc(data.byteLength);
const wasmArray = new (data.constructor as any)(wasm.memory.buffer, ptr, data.length);
wasmArray.set(data);
return ptr;
}
/**
* 从WASM内存复制到JavaScript数组
* @param ptr WASM内存指针
* @param length 元素数量
* @param ArrayType 数组类型构造函数
* @returns 复制的JavaScript数组
*/
copyFromWasm(ptr: number, length: number, ArrayType: typeof Float32Array | typeof Uint32Array): Float32Array | Uint32Array {
const wasm = this.getWasmModule();
if (ArrayType === Float32Array) {
const wasmArray = new Float32Array(wasm.memory.buffer, ptr, length);
return wasmArray.slice();
} else {
const wasmArray = new Uint32Array(wasm.memory.buffer, ptr, length);
return wasmArray.slice();
}
}
}
// ================================
// 查询功能实现类
// ================================
/**
* WebAssembly查询实现
*
* 提供高性能的实体查询功能
*/
class WasmQueryProvider implements QueryProvider {
/**
* 构造函数
* @param provider WebAssembly提供者实例
*/
constructor(private provider: WebAssemblyProvider) {}
/**
* 根据组件掩码查询实体
* @param componentMask 组件掩码
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryByComponent(componentMask: bigint, maxResults: number): QueryResult {
const wasm = this.provider.getWasmModule();
// 注意这里简化了bigint的处理实际实现需要更复杂的转换
const maskPtr = this.provider.malloc(8);
const resultPtr = this.provider.malloc(maxResults * 4);
const count = wasm.query_by_component(maskPtr, resultPtr, maxResults);
const entities = this.provider.copyFromWasm(resultPtr, count, Uint32Array) as Uint32Array;
this.provider.free(maskPtr);
this.provider.free(resultPtr);
return { entities, count };
}
/**
* 根据多个组件掩码查询实体
* @param componentMasks 组件掩码数组
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryByComponents(componentMasks: bigint[], maxResults: number): QueryResult {
const wasm = this.provider.getWasmModule();
// 分配掩码数组内存
const masksPtr = this.provider.malloc(componentMasks.length * 8);
const resultPtr = this.provider.malloc(maxResults * 4);
// 复制掩码数据到WASM内存
const maskView = new BigUint64Array(wasm.memory.buffer, masksPtr, componentMasks.length);
maskView.set(componentMasks);
const count = wasm.query_by_components(masksPtr, componentMasks.length, resultPtr, maxResults);
const entities = this.provider.copyFromWasm(resultPtr, count, Uint32Array) as Uint32Array;
this.provider.free(masksPtr);
this.provider.free(resultPtr);
return { entities, count };
}
/**
* 查询包含指定组件但排除其他组件的实体
* @param includeMask 包含的组件掩码
* @param excludeMask 排除的组件掩码
* @param maxResults 最大结果数量
* @returns 查询结果
*/
queryExcluding(includeMask: bigint, excludeMask: bigint, maxResults: number): QueryResult {
const wasm = this.provider.getWasmModule();
const includeMaskPtr = this.provider.malloc(8);
const excludeMaskPtr = this.provider.malloc(8);
const resultPtr = this.provider.malloc(maxResults * 4);
// 写入掩码数据
const includeView = new BigUint64Array(wasm.memory.buffer, includeMaskPtr, 1);
const excludeView = new BigUint64Array(wasm.memory.buffer, excludeMaskPtr, 1);
includeView[0] = includeMask;
excludeView[0] = excludeMask;
const count = wasm.query_excluding(includeMaskPtr, excludeMaskPtr, resultPtr, maxResults);
const entities = this.provider.copyFromWasm(resultPtr, count, Uint32Array) as Uint32Array;
this.provider.free(includeMaskPtr);
this.provider.free(excludeMaskPtr);
this.provider.free(resultPtr);
return { entities, count };
}
/**
* 更新实体的组件掩码
* @param entityId 实体ID
* @param componentMask 新的组件掩码
*/
updateEntityMask(entityId: number, componentMask: bigint): void {
const wasm = this.provider.getWasmModule();
// 简化的mask处理实际应该支持完整的bigint
wasm.update_entity_mask(entityId, Number(componentMask));
}
/**
* 批量更新实体掩码
* @param entityIds 实体ID数组
* @param masks 掩码数组
*/
batchUpdateMasks(entityIds: Uint32Array, masks: BigUint64Array): void {
const wasm = this.provider.getWasmModule();
const entityIdsPtr = this.provider.copyToWasm(entityIds);
const masksPtr = this.provider.malloc(masks.byteLength);
// 复制掩码数据
const maskView = new BigUint64Array(wasm.memory.buffer, masksPtr, masks.length);
maskView.set(masks);
wasm.batch_update_masks(entityIdsPtr, masksPtr, entityIds.length);
this.provider.free(entityIdsPtr);
this.provider.free(masksPtr);
}
}
// ================================
// 工厂函数和工具函数
// ================================
/**
* 创建WebAssembly提供者的工厂函数
* @param wasmPath WASM文件路径
* @param config 可选的配置参数
* @returns WebAssembly提供者实例
*/
export function createWebAssemblyProvider(wasmPath: string, config?: Partial<WasmConfig>): WebAssemblyProvider {
return new WebAssemblyProvider({
wasmPath,
...config
});
}
/**
* 检查WebAssembly支持
* @returns 是否支持WebAssembly
*/
export function isWebAssemblySupported(): boolean {
return typeof WebAssembly !== 'undefined' &&
typeof WebAssembly.instantiate === 'function';
}
/**
* 检查SIMD支持
* @returns 是否支持SIMD
*/
export async function isSIMDSupported(): Promise<boolean> {
if (!isWebAssemblySupported()) return false;
try {
// 简单的SIMD检测
const wasmBytes = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00
]);
await WebAssembly.instantiate(wasmBytes);
return true;
} catch {
return false;
}
}

View File

@@ -0,0 +1,702 @@
/**
* 统一的WASM ECS核心模块
*
* 为小游戏优化的高性能ECS引擎提供简洁的API和自动回退机制
* 适用于NPM包发布和多种部署环境
*
*/
/** 实体ID类型 */
export type EntityId = number;
/** 组件掩码类型 */
export type ComponentMask = bigint;
/** 查询结果接口 */
export interface QueryResult {
/** 查询到的实体ID数组 */
entities: Uint32Array;
/** 实体数量 */
count: number;
}
/** 性能统计接口 */
export interface PerformanceStats {
/** 实体总数 */
entityCount: number;
/** 索引数量 */
indexCount: number;
/** 查询次数 */
queryCount: number;
/** 更新次数 */
updateCount: number;
/** 是否使用WASM */
wasmEnabled: boolean;
}
/** WASM模块类型定义 */
interface WasmEcsCoreInstance {
create_entity(): number;
destroy_entity(entity_id: number): boolean;
update_entity_mask(entity_id: number, mask: bigint): void;
batch_update_masks(entity_ids: Uint32Array, masks: BigUint64Array): void;
query_entities(mask: bigint, max_results: number): number;
get_query_result_count(): number;
query_cached(mask: bigint): number;
get_cached_query_count(mask: bigint): number;
query_multiple_components(masks: BigUint64Array, max_results: number): number;
query_with_exclusion(include_mask: bigint, exclude_mask: bigint, max_results: number): number;
get_entity_mask(entity_id: number): bigint;
entity_exists(entity_id: number): boolean;
get_entity_count(): number;
get_performance_stats(): Array<any>;
clear(): void;
rebuild_query_cache(): void;
free?(): void;
}
interface WasmModule {
EcsCore: new () => WasmEcsCoreInstance;
create_component_mask: (componentIds: Uint32Array) => ComponentMask;
mask_contains_component: (mask: ComponentMask, componentId: number) => boolean;
default: (input?: any) => Promise<any>;
initSync?: (input: any) => any;
memory?: WebAssembly.Memory;
}
/**
* 统一的WASM ECS核心类
*
* 提供高性能的ECS操作自动选择WASM或JavaScript实现
* 针对小游戏场景优化,易于使用且性能卓越
* 支持NPM包发布和多种部署环境
*/
export class WasmEcsCore {
/** WASM核心实例 */
private wasmCore: WasmEcsCoreInstance | null = null;
/** WASM模块 */
private wasmModule: WasmModule | null = null;
/** 是否已初始化 */
private initialized = false;
/** 是否使用WASM */
private usingWasm = false;
private silent = false;
// JavaScript回退实现
private jsEntityMasks = new Map<EntityId, ComponentMask>();
private jsNextEntityId = 1;
private jsQueryCount = 0;
private jsUpdateCount = 0;
/**
* 设置静默模式
*/
public setSilent(silent: boolean): void {
this.silent = silent;
}
/**
* 初始化ECS核心
*
* 尝试加载WASM模块失败时自动回退到JavaScript实现
*
* @returns 初始化是否成功
*/
async initialize(): Promise<boolean> {
if (this.initialized) return true;
if (!this.silent) {
console.log('🔄 初始化ECS核心...');
}
try {
// 尝试从bin目录加载WASM模块
const wasmPath = '../../bin/wasm/ecs_wasm_core';
if (!this.silent) {
console.log(`🔍 尝试加载WASM模块: ${wasmPath}`);
console.log(`📁 当前文件位置: ${typeof __filename !== 'undefined' ? __filename : 'unknown'}`);
console.log(`📂 工作目录: ${typeof process !== 'undefined' ? process.cwd() : 'unknown'}`);
// 计算绝对路径
if (typeof __filename !== 'undefined' && typeof require !== 'undefined') {
const path = require('path');
const fs = require('fs');
const currentDir = path.dirname(__filename);
const absoluteWasmPath = path.resolve(currentDir, wasmPath);
console.log(`📍 计算的绝对路径: ${absoluteWasmPath}`);
// 检查文件是否存在
const jsFile = absoluteWasmPath + '.js';
const wasmFile = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core_bg.wasm');
console.log(`📄 检查JS文件: ${jsFile} - ${fs.existsSync(jsFile) ? '存在' : '不存在'}`);
console.log(`📄 检查WASM文件: ${wasmFile} - ${fs.existsSync(wasmFile) ? '存在' : '不存在'}`);
}
}
this.wasmModule = await import(wasmPath);
if (!this.silent) {
console.log('✅ WASM模块导入成功正在初始化...');
}
if (this.wasmModule) {
// 在初始化前,先检查.wasm文件的加载路径
if (!this.silent) {
console.log('🔍 WASM模块将尝试加载 .wasm 文件...');
// 模拟WASM模块内部的路径计算
if (typeof __filename !== 'undefined' && typeof require !== 'undefined') {
const path = require('path');
const { pathToFileURL } = require('url');
const currentDir = path.dirname(__filename);
const wasmJsFile = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core.js');
const wasmBgFile = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core_bg.wasm');
const wasmJsUrl = pathToFileURL(wasmJsFile).href;
const expectedWasmUrl = new URL('ecs_wasm_core_bg.wasm', wasmJsUrl).href;
console.log(`📍 WASM JS文件URL: ${wasmJsUrl}`);
console.log(`📍 预期的.wasm文件URL: ${expectedWasmUrl}`);
console.log(`📍 实际.wasm文件路径: ${wasmBgFile}`);
const fs = require('fs');
console.log(`📄 .wasm文件是否存在: ${fs.existsSync(wasmBgFile) ? '存在' : '不存在'}`);
}
}
// 在Node.js环境中需要手动读取WASM文件
if (typeof require !== 'undefined') {
const fs = require('fs');
const path = require('path');
const currentDir = path.dirname(__filename);
const wasmPath = path.resolve(currentDir, '../../bin/wasm/ecs_wasm_core_bg.wasm');
if (!this.silent) {
console.log(`🔧 在Node.js环境中手动加载WASM文件: ${wasmPath}`);
}
if (fs.existsSync(wasmPath)) {
const wasmBytes = fs.readFileSync(wasmPath);
// 使用initSync同步初始化WASM模块
if (this.wasmModule.initSync) {
this.wasmModule.initSync(wasmBytes);
} else {
await this.wasmModule.default({ module_or_path: wasmBytes });
}
} else {
throw new Error(`WASM文件不存在: ${wasmPath}`);
}
} else {
await this.wasmModule.default();
}
this.wasmCore = new this.wasmModule.EcsCore();
}
this.usingWasm = true;
if (!this.silent) {
console.log('✅ WASM模块加载成功');
}
} catch (error) {
if (!this.silent) {
console.warn('⚠️ WASM加载失败使用JavaScript实现');
console.warn(`❌ 错误详情: ${error}`);
}
this.usingWasm = false;
}
this.initialized = true;
if (!this.silent) {
console.log(`🎮 ECS核心初始化完成 (${this.usingWasm ? 'WASM' : 'JavaScript'})`);
}
return true;
}
/**
* 创建新实体
*
* @returns 新实体的ID
*/
createEntity(): EntityId {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
return this.wasmCore.create_entity();
} else {
const entityId = this.jsNextEntityId++;
this.jsEntityMasks.set(entityId, BigInt(0));
return entityId;
}
}
/**
* 删除实体
*
* @param entityId 实体ID
* @returns 是否删除成功
*/
destroyEntity(entityId: EntityId): boolean {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
return this.wasmCore.destroy_entity(entityId);
} else {
return this.jsEntityMasks.delete(entityId);
}
}
/**
* 更新实体的组件掩码
*
* @param entityId 实体ID
* @param mask 组件掩码
*/
updateEntityMask(entityId: EntityId, mask: ComponentMask): void {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
this.wasmCore.update_entity_mask(entityId, mask);
} else {
this.jsEntityMasks.set(entityId, mask);
this.jsUpdateCount++;
}
}
/**
* 批量更新实体掩码(高性能)
*
* @param entityIds 实体ID数组
* @param masks 组件掩码数组
*/
batchUpdateMasks(entityIds: EntityId[], masks: ComponentMask[]): void {
this.ensureInitialized();
if (entityIds.length !== masks.length) {
throw new Error('实体ID和掩码数组长度必须相同');
}
if (this.usingWasm && this.wasmCore) {
const entityIdsArray = new Uint32Array(entityIds);
const masksArray = new BigUint64Array(masks);
this.wasmCore.batch_update_masks(entityIdsArray, masksArray);
} else {
for (let i = 0; i < entityIds.length; i++) {
this.jsEntityMasks.set(entityIds[i], masks[i]);
}
this.jsUpdateCount += entityIds.length;
}
}
/**
* 查询包含指定组件的实体
*
* @param mask 组件掩码
* @param maxResults 最大结果数
* @returns 查询结果
*/
queryEntities(mask: ComponentMask, maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
try {
const ptr = this.wasmCore.query_entities(mask, maxResults);
const count = this.wasmCore.get_query_result_count();
if (ptr && count > 0 && this.wasmModule?.memory) {
const entities = new Uint32Array(this.wasmModule.memory.buffer, ptr, count);
return {
entities: new Uint32Array(entities), // 创建副本以确保数据安全
count
};
} else {
return { entities: new Uint32Array(0), count: 0 };
}
} catch (error) {
if (!this.silent) {
console.warn('WASM查询失败回退到JavaScript实现:', error);
}
// 回退到JavaScript实现
}
}
// JavaScript实现
this.jsQueryCount++;
const entities: EntityId[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
if ((entityMask & mask) === mask) {
entities.push(entityId);
if (entities.length >= maxResults) break;
}
}
return {
entities: new Uint32Array(entities),
count: entities.length
};
}
/**
* 查询指定掩码的实体(带缓存优化)
*
* @param mask 组件掩码
* @returns 查询结果
*/
queryCached(mask: ComponentMask): QueryResult {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
try {
const ptr = this.wasmCore.query_cached(mask);
const count = this.wasmCore.get_cached_query_count(mask);
if (ptr && count > 0 && this.wasmModule?.memory) {
const entities = new Uint32Array(this.wasmModule.memory.buffer, ptr, count);
return {
entities: new Uint32Array(entities), // 复制数据
count
};
}
return { entities: new Uint32Array(0), count: 0 };
} catch (error) {
if (!this.silent) {
console.warn('WASM缓存查询失败回退到通用查询:', error);
}
// 回退到通用查询
return this.queryEntities(mask);
}
}
// JavaScript实现 - 直接使用通用查询
return this.queryEntities(mask);
}
/**
* 查询包含多个组件的实体
*
* @param masks 组件掩码数组
* @param maxResults 最大结果数
* @returns 查询结果
*/
queryMultipleComponents(masks: ComponentMask[], maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
try {
const masksArray = new BigUint64Array(masks);
const ptr = this.wasmCore.query_multiple_components(masksArray, maxResults);
if (ptr && this.wasmModule?.memory) {
// 暂时返回空结果,需要实现内存访问
return { entities: new Uint32Array(0), count: 0 };
}
return { entities: new Uint32Array(0), count: 0 };
} catch (error) {
if (!this.silent) {
console.warn('WASM多组件查询失败回退到JavaScript实现:', error);
}
// 回退到JavaScript实现
}
}
// JavaScript实现
this.jsQueryCount++;
const entities: EntityId[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
let hasAll = true;
for (const mask of masks) {
if ((entityMask & mask) !== mask) {
hasAll = false;
break;
}
}
if (hasAll) {
entities.push(entityId);
if (entities.length >= maxResults) break;
}
}
return {
entities: new Uint32Array(entities),
count: entities.length
};
}
/**
* 排除查询:包含某些组件但不包含其他组件
*
* @param includeMask 必须包含的组件掩码
* @param excludeMask 必须排除的组件掩码
* @param maxResults 最大结果数
* @returns 查询结果
*/
queryWithExclusion(includeMask: ComponentMask, excludeMask: ComponentMask, maxResults: number = 10000): QueryResult {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
try {
const ptr = this.wasmCore.query_with_exclusion(includeMask, excludeMask, maxResults);
if (ptr && this.wasmModule?.memory) {
// 暂时返回空结果,需要实现内存访问
return { entities: new Uint32Array(0), count: 0 };
}
return { entities: new Uint32Array(0), count: 0 };
} catch (error) {
if (!this.silent) {
console.warn('WASM排除查询失败回退到JavaScript实现:', error);
}
// 回退到JavaScript实现
}
}
// JavaScript实现
this.jsQueryCount++;
const entities: EntityId[] = [];
for (const [entityId, entityMask] of this.jsEntityMasks) {
if ((entityMask & includeMask) === includeMask && (entityMask & excludeMask) === BigInt(0)) {
entities.push(entityId);
if (entities.length >= maxResults) break;
}
}
return {
entities: new Uint32Array(entities),
count: entities.length
};
}
/**
* 获取实体的组件掩码
*
* @param entityId 实体ID
* @returns 组件掩码如果实体不存在则返回null
*/
getEntityMask(entityId: EntityId): ComponentMask | null {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
return this.wasmCore.get_entity_mask(entityId) || null;
} else {
return this.jsEntityMasks.get(entityId) || null;
}
}
/**
* 检查实体是否存在
*
* @param entityId 实体ID
* @returns 是否存在
*/
entityExists(entityId: EntityId): boolean {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
return this.wasmCore.entity_exists(entityId);
} else {
return this.jsEntityMasks.has(entityId);
}
}
/**
* 创建组件掩码
*
* @param componentIds 组件ID数组
* @returns 组件掩码
*/
createComponentMask(componentIds: number[]): ComponentMask {
if (this.usingWasm && this.wasmModule) {
return this.wasmModule.create_component_mask(new Uint32Array(componentIds));
} else {
let mask = BigInt(0);
for (const id of componentIds) {
if (id < 64) {
mask |= BigInt(1) << BigInt(id);
}
}
return mask;
}
}
/**
* 检查掩码是否包含组件
*
* @param mask 组件掩码
* @param componentId 组件ID
* @returns 是否包含
*/
maskContainsComponent(mask: ComponentMask, componentId: number): boolean {
if (this.usingWasm && this.wasmModule) {
return this.wasmModule.mask_contains_component(mask, componentId);
} else {
if (componentId >= 64) return false;
return (mask & (BigInt(1) << BigInt(componentId))) !== BigInt(0);
}
}
/**
* 获取性能统计信息
*
* @returns 性能统计
*/
getPerformanceStats(): PerformanceStats {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
const stats = Array.from(this.wasmCore.get_performance_stats());
return {
entityCount: stats[0] as number,
indexCount: stats[1] as number,
queryCount: stats[2] as number,
updateCount: stats[3] as number,
wasmEnabled: true
};
} else {
return {
entityCount: this.jsEntityMasks.size,
indexCount: 0,
queryCount: this.jsQueryCount,
updateCount: this.jsUpdateCount,
wasmEnabled: false
};
}
}
/**
* 清空所有数据
*/
clear(): void {
this.ensureInitialized();
if (this.usingWasm && this.wasmCore) {
this.wasmCore.clear();
} else {
this.jsEntityMasks.clear();
this.jsNextEntityId = 1;
this.jsQueryCount = 0;
this.jsUpdateCount = 0;
}
}
/**
* 是否使用WASM实现
*
* @returns 是否使用WASM
*/
isUsingWasm(): boolean {
return this.usingWasm;
}
/**
* 是否已初始化
*
* @returns 是否已初始化
*/
isInitialized(): boolean {
return this.initialized;
}
/**
* 确保已初始化
*/
private ensureInitialized(): void {
if (!this.initialized) {
throw new Error('ECS核心未初始化请先调用 initialize()');
}
}
/**
* 清理资源
*/
cleanup(): void {
if (this.usingWasm && this.wasmCore) {
try {
this.wasmCore.free?.();
} catch (error) {
if (!this.silent) {
console.warn('⚠️ 清理WASM资源时出错:', error);
}
}
}
this.wasmCore = null;
this.wasmModule = null;
this.jsEntityMasks.clear();
this.initialized = false;
this.usingWasm = false;
}
}
/**
* 全局ECS核心实例
*
* 提供单例模式的ECS核心确保整个应用使用同一个实例
*/
export const ecsCore = new WasmEcsCore();
/**
* 初始化ECS引擎
*
* 便捷的初始化函数,推荐在应用启动时调用
*
* @param silent 是否静默模式
* @returns 初始化是否成功
*
* @example
* ```typescript
* import { initializeEcs } from 'ecs-framework';
*
* async function main() {
* // 使用默认配置JavaScript实现
* await initializeEcs();
*
* // 或者自定义配置
* await initializeEcs({
* enabled: false, // 禁用WASM
* silent: true // 静默模式
* });
* }
* ```
*/
export async function initializeEcs(silent: boolean = false): Promise<boolean> {
ecsCore.setSilent(silent);
return ecsCore.initialize();
}
/**
* 快速查询工具函数
*
* 为常见查询操作提供便捷的API
*/
export const Query = {
/**
* 查询拥有指定组件的所有实体
*/
withComponent: (componentId: number, maxResults?: number): QueryResult => {
const mask = ecsCore.createComponentMask([componentId]);
return ecsCore.queryEntities(mask, maxResults);
},
/**
* 查询拥有多个组件的实体
*/
withComponents: (componentIds: number[], maxResults?: number): QueryResult => {
const masks = componentIds.map(id => ecsCore.createComponentMask([id]));
return ecsCore.queryMultipleComponents(masks, maxResults);
},
/**
* 查询拥有某些组件但不拥有其他组件的实体
*/
withExclusion: (includeIds: number[], excludeIds: number[], maxResults?: number): QueryResult => {
const includeMask = ecsCore.createComponentMask(includeIds);
const excludeMask = ecsCore.createComponentMask(excludeIds);
return ecsCore.queryWithExclusion(includeMask, excludeMask, maxResults);
}
};

View File

@@ -1,5 +1,24 @@
// 工具类导出
/**
* 工具模块导出
*/
export * from './Extensions';
export * from './Pool';
export * from './Emitter';
export * from './GlobalManager';
export * from './PerformanceMonitor';
export { Time } from './Time';
export { Time } from './Time';
/**
* WebAssembly核心模块
* 提供高性能的ECS查询和计算功能
*/
export {
WasmEcsCore,
ecsCore,
initializeEcs,
Query,
EntityId,
ComponentMask,
QueryResult,
PerformanceStats
} from './WasmCore';

View File

@@ -1,8 +1,6 @@
/**
* ECS Framework - 轻量级实体组件系统框架
* 适用于Laya、Cocos等游戏引擎的小游戏开发
* @version 2.0.0
* @author ECS Framework Team
*/
// 核心模块
@@ -16,7 +14,7 @@ export { TimerManager } from './Utils/Timers/TimerManager';
export { ITimer } from './Utils/Timers/ITimer';
export { Timer } from './Utils/Timers/Timer';
// ECS核心
// ECS核心组件
export * from './ECS';
// 工具类
@@ -24,5 +22,16 @@ export * from './Utils/Pool';
export * from './Utils/PerformanceMonitor';
export * from './Utils/Extensions';
// WebAssembly核心模块
export {
WasmEcsCore,
ecsCore,
initializeEcs,
Query,
EntityId,
ComponentMask,
QueryResult
} from './Utils/WasmCore';
// 类型定义
export * from './Types';

View File

@@ -0,0 +1,224 @@
# Rust WebAssembly 编译指南
本指南将帮助您从零开始安装Rust环境并编译WASM模块。
## 📋 前置要求
- Windows 10/11 或 macOS/Linux
- 稳定的网络连接
- 管理员权限(用于安装软件)
## 🚀 第一步:安装 Rust
### Windows 用户
1. **下载 Rust 安装器**
- 访问 https://rustup.rs/
- 点击 "DOWNLOAD RUSTUP-INIT.EXE (64-BIT)"
- 或者直接下载https://win.rustup.rs/x86_64
2. **运行安装器**
```cmd
# 下载后运行
rustup-init.exe
```
3. **选择安装选项**
- 出现提示时,选择 "1) Proceed with installation (default)"
- 等待安装完成
4. **重启命令行**
- 关闭当前命令行窗口
- 重新打开 cmd 或 PowerShell
### macOS/Linux 用户
```bash
# 使用官方安装脚本
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 重新加载环境变量
source ~/.cargo/env
```
## 🔧 第二步:安装 wasm-pack
wasm-pack 是编译 Rust 到 WebAssembly 的官方工具。
### Windows 用户
```cmd
# 方法1使用 cargo 安装(推荐)
cargo install wasm-pack
# 方法2下载预编译版本
# 访问 https://github.com/rustwasm/wasm-pack/releases
# 下载最新的 Windows 版本
```
### macOS/Linux 用户
```bash
# 方法1使用官方安装脚本
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# 方法2使用 cargo 安装
cargo install wasm-pack
```
## ✅ 第三步:验证安装
打开新的命令行窗口,运行以下命令验证安装:
```cmd
# 检查 Rust 版本
rustc --version
# 检查 Cargo 版本
cargo --version
# 检查 wasm-pack 版本
wasm-pack --version
```
如果所有命令都能正常显示版本号,说明安装成功!
## 🏗️ 第四步:编译 WASM 模块
现在可以编译我们的 Rust WASM 模块了:
### 使用批处理文件Windows 推荐)
```cmd
# 进入项目目录
cd D:\project\ecs-framework\source\src\wasm\rust-ecs-core
# 运行批处理文件
build.bat
```
### 使用命令行(跨平台)
```bash
# 进入项目目录
cd source/src/wasm/rust-ecs-core
# 编译 WASM 模块
wasm-pack build --target web --out-dir pkg --release
```
### 编译选项说明
- `--target web`: 生成适用于浏览器的模块
- `--out-dir pkg`: 输出到 pkg 目录
- `--release`: 发布模式,启用优化
## 📦 第五步:验证编译结果
编译成功后,`pkg` 目录应该包含以下文件:
```
pkg/
├── ecs_wasm_core.js # JavaScript 绑定
├── ecs_wasm_core_bg.wasm # WebAssembly 二进制文件
├── ecs_wasm_core.d.ts # TypeScript 类型定义
├── package.json # NPM 包配置
└── README.md # 包说明
```
## 🧪 第六步:测试 WASM 模块
创建一个简单的测试文件来验证模块是否正常工作:
```html
<!DOCTYPE html>
<html>
<head>
<title>WASM ECS 测试</title>
</head>
<body>
<h1>Rust WASM ECS 测试</h1>
<div id="output"></div>
<script type="module">
import init, { EcsCore } from './pkg/ecs_wasm_core.js';
async function run() {
try {
// 初始化 WASM 模块
await init();
// 创建 ECS 核心实例
const ecs = new EcsCore();
// 创建实体
const entity = ecs.create_entity();
console.log('创建实体:', entity);
// 显示结果
document.getElementById('output').innerHTML =
`✅ WASM 模块加载成功!<br>创建的实体ID: ${entity}`;
} catch (error) {
console.error('错误:', error);
document.getElementById('output').innerHTML =
`❌ 错误: ${error.message}`;
}
}
run();
</script>
</body>
</html>
```
## 🔧 故障排除
### 常见问题
1. **"rustc 不是内部或外部命令"**
- 重启命令行窗口
- 检查环境变量是否正确设置
- 重新安装 Rust
2. **"wasm-pack 不是内部或外部命令"**
- 确保 wasm-pack 安装成功
- 重启命令行窗口
- 尝试使用 `cargo install wasm-pack` 重新安装
3. **编译错误**
- 检查 Rust 版本是否为最新稳定版
- 运行 `rustup update` 更新 Rust
- 检查网络连接,确保能下载依赖
4. **WASM 模块加载失败**
- 确保使用 HTTP 服务器而不是直接打开文件
- 检查浏览器是否支持 WebAssembly
- 查看浏览器控制台的错误信息
### 更新工具
```bash
# 更新 Rust
rustup update
# 更新 wasm-pack
cargo install wasm-pack --force
```
## 🎯 下一步
编译成功后,您可以:
1. 在项目中使用 `WasmLoader` 加载模块
2. 运行性能基准测试
3. 集成到您的游戏或应用中
## 📞 获取帮助
如果遇到问题,可以:
1. 查看 Rust 官方文档https://doc.rust-lang.org/
2. 查看 wasm-pack 文档https://rustwasm.github.io/wasm-pack/
3. 检查项目的 GitHub Issues
4. 在 Rust 社区寻求帮助https://users.rust-lang.org/

View File

@@ -0,0 +1,2 @@
[target.wasm32-unknown-unknown]
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']

286
source/src/wasm/rust-ecs-core/Cargo.lock generated Normal file
View File

@@ -0,0 +1,286 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bumpalo"
version = "3.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ecs-wasm-core"
version = "0.1.0"
dependencies = [
"ahash",
"getrandom",
"js-sys",
"serde",
"serde-wasm-bindgen",
"smallvec",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
"wasi",
"wasm-bindgen",
]
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rustversion"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]]
name = "zerocopy"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -0,0 +1,50 @@
[package]
name = "ecs-wasm-core"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "*"
js-sys = "*"
serde = { version = "*", features = ["derive"] }
serde-wasm-bindgen = "*"
# 高性能哈希表避免getrandom版本冲突
ahash = "*"
# 使用标准库的HashMap避免getrandom版本冲突
smallvec = "*" # 栈分配的小向量
# 为 WASM 环境配置 getrandom
getrandom = { version = "*", features = ["wasm_js"] }
[dependencies.web-sys]
version = "0.3"
features = [
"console",
"Performance",
"Window",
]
[profile.release]
# 优化WASM二进制大小和性能
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
[profile.release.package."*"]
opt-level = 3
# 配置 wasm-pack 行为
[package.metadata.wasm-pack.profile.release]
# 如果下载失败,可以暂时禁用 wasm-opt 优化
wasm-opt = false
# 或者指定本地 wasm-opt 路径(如果已安装)
# wasm-opt = ["-O4", "--enable-simd"]
# 配置网络超时和重试
[package.metadata.wasm-pack]
# 增加下载超时时间
timeout = 300

View File

@@ -0,0 +1,56 @@
@echo off
echo 正在构建 WASM 模块...
REM 方法1尝试正常构建
echo 尝试正常构建...
wasm-pack build --target web --out-dir pkg --release
if %ERRORLEVEL% == 0 (
echo ✅ 构建成功!
goto :success
)
echo ❌ 正常构建失败,尝试其他方法...
REM 方法2设置代理如果有的话
REM set HTTPS_PROXY=http://127.0.0.1:7890
REM set HTTP_PROXY=http://127.0.0.1:7890
REM 方法3禁用 wasm-opt 优化
echo 尝试禁用 wasm-opt 优化...
wasm-pack build --target web --out-dir pkg --release -- --no-default-features
if %ERRORLEVEL% == 0 (
echo ✅ 构建成功(已禁用优化)!
goto :success
)
REM 方法4手动下载 binaryen
echo 尝试手动处理 binaryen...
if not exist "tools\binaryen" (
echo 请手动下载 binaryen 到 tools 目录
echo 下载地址: https://github.com/WebAssembly/binaryen/releases/download/version_117/binaryen-version_117-x86_64-windows.tar.gz
echo 或者使用国内镜像源
)
REM 方法5使用环境变量跳过下载
echo 尝试跳过 binaryen 下载...
set WASM_PACK_CACHE_DISABLE=1
wasm-pack build --target web --out-dir pkg --release --mode no-install
if %ERRORLEVEL% == 0 (
echo ✅ 构建成功(跳过下载)!
goto :success
)
echo ❌ 所有方法都失败了
echo 建议:
echo 1. 检查网络连接
echo 2. 使用 VPN 或代理
echo 3. 手动下载 binaryen 工具
echo 4. 临时禁用 wasm-opt 优化
goto :end
:success
echo 🎉 WASM 模块构建完成!
echo 输出目录: pkg/
:end
pause

View File

@@ -0,0 +1,65 @@
@echo off
chcp 65001 >nul
REM Rust WASM构建脚本 (Windows版本)
echo 开始构建Rust ECS WASM模块...
REM 检查是否安装了必要的工具
where wasm-pack >nul 2>&1
if %errorlevel% neq 0 (
echo 错误未找到wasm-pack请先安装
echo curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf ^| sh
echo 或者访问: https://rustwasm.github.io/wasm-pack/installer/
pause
exit /b 1
)
REM 检查是否安装了Rust
where rustc >nul 2>&1
if %errorlevel% neq 0 (
echo 错误未找到Rust请先安装
echo 访问: https://rustup.rs/
pause
exit /b 1
)
REM 清理之前的构建缓存
echo 清理之前的构建缓存...
if exist Cargo.lock del Cargo.lock
if exist target rmdir /s /q target
if exist pkg rmdir /s /q pkg
cargo clean
echo 更新依赖...
cargo update
REM 设置环境变量解决getrandom问题
set RUSTFLAGS=--cfg getrandom_backend="wasm_js"
REM 构建WASM模块
echo 正在编译WASM模块...
wasm-pack build --target web --out-dir pkg --release
REM 检查构建是否成功
if %errorlevel% equ 0 (
echo ✅ WASM模块构建成功
echo 生成的文件位于 pkg/ 目录:
dir pkg
echo.
echo 📦 生成的文件说明:
echo - ecs_wasm_core.js: JavaScript绑定
echo - ecs_wasm_core_bg.wasm: WebAssembly二进制文件
echo - ecs_wasm_core.d.ts: TypeScript类型定义
echo.
echo 🚀 使用方法:
echo import init, { EcsCore } from './pkg/ecs_wasm_core.js';
echo await init^(^);
echo const ecs = new EcsCore^(^);
) else (
echo ❌ 构建失败!
pause
exit /b 1
)
pause

View File

@@ -0,0 +1,37 @@
#!/bin/bash
# Rust WASM构建脚本
echo "开始构建Rust ECS WASM模块..."
# 检查是否安装了必要的工具
if ! command -v wasm-pack &> /dev/null; then
echo "错误未找到wasm-pack请先安装"
echo "curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh"
exit 1
fi
# 构建WASM模块
echo "正在编译WASM模块..."
wasm-pack build --target web --out-dir pkg --release
# 检查构建是否成功
if [ $? -eq 0 ]; then
echo "✅ WASM模块构建成功"
echo "生成的文件位于 pkg/ 目录:"
ls -la pkg/
echo ""
echo "📦 生成的文件说明:"
echo " - ecs_wasm_core.js: JavaScript绑定"
echo " - ecs_wasm_core_bg.wasm: WebAssembly二进制文件"
echo " - ecs_wasm_core.d.ts: TypeScript类型定义"
echo ""
echo "🚀 使用方法:"
echo "import init, { EcsCore } from './pkg/ecs_wasm_core.js';"
echo "await init();"
echo "const ecs = new EcsCore();"
else
echo "❌ 构建失败!"
exit 1
fi

View File

@@ -0,0 +1,185 @@
use wasm_bindgen::prelude::*;
use js_sys::Array;
mod query;
use query::QueryEngine;
// 当wasm-bindgen功能启用时提供console.log绑定
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
// 定义一个宏来简化日志记录
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
/// 实体ID类型
pub type EntityId = u32;
/// 组件掩码类型
pub type ComponentMask = u64;
/// 高性能ECS核心专注于实体查询和掩码管理
#[wasm_bindgen]
pub struct EcsCore {
/// 查询引擎
query_engine: QueryEngine,
/// 下一个可用的实体ID
next_entity_id: EntityId,
/// 更新计数
update_count: u32,
}
#[wasm_bindgen]
impl EcsCore {
/// 创建新的ECS核心
#[wasm_bindgen(constructor)]
pub fn new() -> EcsCore {
EcsCore {
query_engine: QueryEngine::new(),
next_entity_id: 1,
update_count: 0,
}
}
/// 创建新实体
#[wasm_bindgen]
pub fn create_entity(&mut self) -> EntityId {
let entity_id = self.next_entity_id;
self.next_entity_id += 1;
self.query_engine.add_entity(entity_id, 0);
entity_id
}
/// 删除实体
#[wasm_bindgen]
pub fn destroy_entity(&mut self, entity_id: EntityId) -> bool {
self.query_engine.remove_entity(entity_id)
}
/// 更新实体的组件掩码
#[wasm_bindgen]
pub fn update_entity_mask(&mut self, entity_id: EntityId, mask: ComponentMask) {
self.query_engine.update_entity_mask(entity_id, mask);
self.update_count += 1;
}
/// 批量更新实体掩码
#[wasm_bindgen]
pub fn batch_update_masks(&mut self, entity_ids: &[u32], masks: &[u64]) {
self.query_engine.batch_update_masks(entity_ids, masks);
self.update_count += entity_ids.len() as u32;
}
/// 查询实体
#[wasm_bindgen]
pub fn query_entities(&mut self, mask: ComponentMask, max_results: u32) -> *const u32 {
let results = self.query_engine.query_entities(mask, max_results as usize);
results.as_ptr()
}
/// 获取查询结果数量
#[wasm_bindgen]
pub fn get_query_result_count(&self) -> usize {
self.query_engine.get_last_query_result_count()
}
/// 缓存查询实体
#[wasm_bindgen]
pub fn query_cached(&mut self, mask: ComponentMask) -> *const u32 {
let results = self.query_engine.query_cached(mask);
results.as_ptr()
}
/// 获取缓存查询结果数量
#[wasm_bindgen]
pub fn get_cached_query_count(&mut self, mask: ComponentMask) -> usize {
self.query_engine.query_cached(mask).len()
}
/// 多组件查询
#[wasm_bindgen]
pub fn query_multiple_components(&mut self, masks: &[u64], max_results: u32) -> *const u32 {
let results = self.query_engine.query_multiple_components(masks, max_results as usize);
results.as_ptr()
}
/// 排除查询
#[wasm_bindgen]
pub fn query_with_exclusion(&mut self, include_mask: ComponentMask, exclude_mask: ComponentMask, max_results: u32) -> *const u32 {
let results = self.query_engine.query_with_exclusion(include_mask, exclude_mask, max_results as usize);
results.as_ptr()
}
/// 获取实体的组件掩码
#[wasm_bindgen]
pub fn get_entity_mask(&self, entity_id: EntityId) -> ComponentMask {
self.query_engine.get_entity_mask(entity_id)
}
/// 检查实体是否存在
#[wasm_bindgen]
pub fn entity_exists(&self, entity_id: EntityId) -> bool {
self.query_engine.entity_exists(entity_id)
}
/// 获取实体数量
#[wasm_bindgen]
pub fn get_entity_count(&self) -> u32 {
self.query_engine.get_entity_count()
}
/// 获取性能统计信息
#[wasm_bindgen]
pub fn get_performance_stats(&self) -> Array {
let stats = Array::new();
stats.push(&JsValue::from(self.query_engine.get_entity_count())); // 实体数量
stats.push(&JsValue::from(self.query_engine.get_query_count())); // 查询次数
stats.push(&JsValue::from(self.update_count)); // 更新次数
stats
}
/// 清理所有数据
#[wasm_bindgen]
pub fn clear(&mut self) {
self.query_engine.clear();
self.next_entity_id = 1;
self.update_count = 0;
}
/// 重建查询缓存
#[wasm_bindgen]
pub fn rebuild_query_cache(&mut self) {
self.query_engine.force_rebuild_cache();
}
}
/// 创建组件掩码的辅助函数
#[wasm_bindgen]
pub fn create_component_mask(component_ids: &[u32]) -> ComponentMask {
let mut mask = 0u64;
for &id in component_ids {
if id < 64 {
mask |= 1u64 << id;
}
}
mask
}
/// 检查掩码是否包含指定组件
#[wasm_bindgen]
pub fn mask_contains_component(mask: ComponentMask, component_id: u32) -> bool {
if component_id >= 64 {
return false;
}
(mask & (1u64 << component_id)) != 0
}
/// 初始化函数
#[wasm_bindgen(start)]
pub fn main() {
console_log!("Rust ECS WASM模块已加载");
}

View File

@@ -0,0 +1,218 @@
use crate::{EntityId, ComponentMask};
use ahash::AHashMap;
/// 查询引擎,负责高性能的实体查询
pub struct QueryEngine {
/// 实体到组件掩码的映射
entity_masks: AHashMap<EntityId, ComponentMask>,
/// 常用查询掩码的缓存
cached_queries: AHashMap<ComponentMask, Vec<EntityId>>,
/// 查询结果缓冲区
query_buffer: Vec<EntityId>,
/// 缓存有效性标志
cache_dirty: bool,
/// 性能统计
query_count: u32,
/// 最后查询结果数量
last_query_result_count: usize,
}
impl QueryEngine {
pub fn new() -> Self {
QueryEngine {
entity_masks: AHashMap::with_capacity(50000),
cached_queries: AHashMap::with_capacity(32),
query_buffer: Vec::with_capacity(50000),
cache_dirty: true,
query_count: 0,
last_query_result_count: 0,
}
}
/// 添加实体掩码
pub fn add_entity(&mut self, entity_id: EntityId, mask: ComponentMask) {
self.entity_masks.insert(entity_id, mask);
self.cache_dirty = true;
}
/// 移除实体
pub fn remove_entity(&mut self, entity_id: EntityId) -> bool {
if self.entity_masks.remove(&entity_id).is_some() {
self.cache_dirty = true;
true
} else {
false
}
}
/// 更新实体掩码
pub fn update_entity_mask(&mut self, entity_id: EntityId, mask: ComponentMask) {
if self.entity_masks.contains_key(&entity_id) {
self.entity_masks.insert(entity_id, mask);
self.cache_dirty = true;
}
}
/// 批量更新实体掩码
pub fn batch_update_masks(&mut self, entity_ids: &[EntityId], masks: &[ComponentMask]) {
if entity_ids.len() != masks.len() {
return;
}
for (i, &entity_id) in entity_ids.iter().enumerate() {
if self.entity_masks.contains_key(&entity_id) {
self.entity_masks.insert(entity_id, masks[i]);
}
}
self.cache_dirty = true;
}
/// 重建查询缓存
pub fn rebuild_cache(&mut self) {
if !self.cache_dirty {
return;
}
// 清空所有缓存
for cached_entities in self.cached_queries.values_mut() {
cached_entities.clear();
}
// 重建所有缓存的查询
for (&query_mask, cached_entities) in &mut self.cached_queries {
for (&entity_id, &entity_mask) in &self.entity_masks {
if (entity_mask & query_mask) == query_mask {
cached_entities.push(entity_id);
}
}
}
self.cache_dirty = false;
}
/// 通用查询方法
pub fn query_entities(&mut self, mask: ComponentMask, max_results: usize) -> &[EntityId] {
self.query_buffer.clear();
self.query_count += 1;
for (&entity_id, &entity_mask) in &self.entity_masks {
if (entity_mask & mask) == mask {
self.query_buffer.push(entity_id);
if self.query_buffer.len() >= max_results {
break;
}
}
}
self.last_query_result_count = self.query_buffer.len();
&self.query_buffer
}
/// 查询指定掩码的实体(带缓存)
pub fn query_cached(&mut self, mask: ComponentMask) -> &[EntityId] {
// 如果缓存中没有这个查询,添加它
if !self.cached_queries.contains_key(&mask) {
self.cached_queries.insert(mask, Vec::new());
self.cache_dirty = true;
}
self.rebuild_cache();
self.query_count += 1;
self.cached_queries.get(&mask).unwrap()
}
/// 多组件查询
pub fn query_multiple_components(&mut self, masks: &[ComponentMask], max_results: usize) -> &[EntityId] {
self.query_buffer.clear();
self.query_count += 1;
if masks.is_empty() {
return &self.query_buffer;
}
for (&entity_id, &entity_mask) in &self.entity_masks {
let mut matches_all = true;
for &mask in masks {
if (entity_mask & mask) != mask {
matches_all = false;
break;
}
}
if matches_all {
self.query_buffer.push(entity_id);
if self.query_buffer.len() >= max_results {
break;
}
}
}
&self.query_buffer
}
/// 带排除条件的查询
pub fn query_with_exclusion(&mut self, include_mask: ComponentMask, exclude_mask: ComponentMask, max_results: usize) -> &[EntityId] {
self.query_buffer.clear();
self.query_count += 1;
for (&entity_id, &entity_mask) in &self.entity_masks {
if (entity_mask & include_mask) == include_mask && (entity_mask & exclude_mask) == 0 {
self.query_buffer.push(entity_id);
if self.query_buffer.len() >= max_results {
break;
}
}
}
&self.query_buffer
}
/// 获取实体的组件掩码
pub fn get_entity_mask(&self, entity_id: EntityId) -> ComponentMask {
self.entity_masks.get(&entity_id).copied().unwrap_or(0)
}
/// 检查实体是否存在
pub fn entity_exists(&self, entity_id: EntityId) -> bool {
self.entity_masks.contains_key(&entity_id)
}
/// 获取实体数量
pub fn get_entity_count(&self) -> u32 {
self.entity_masks.len() as u32
}
/// 获取查询统计
pub fn get_query_count(&self) -> u32 {
self.query_count
}
/// 获取最后查询结果数量
pub fn get_last_query_result_count(&self) -> usize {
self.last_query_result_count
}
/// 清理所有数据
pub fn clear(&mut self) {
self.entity_masks.clear();
self.cached_queries.clear();
self.query_buffer.clear();
self.cache_dirty = true;
self.query_count = 0;
self.last_query_result_count = 0;
}
/// 强制重建查询缓存
pub fn force_rebuild_cache(&mut self) {
self.cache_dirty = true;
self.rebuild_cache();
}
}

View File

@@ -1,17 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dev-bin",
"sourceMap": true,
"declaration": false,
"declarationMap": false
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"bin",
"dev-bin"
]
}

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"module": "CommonJS",
"moduleResolution": "node",
"lib": ["ES2020", "DOM"],
"outDir": "./bin",
@@ -35,7 +35,7 @@
"exclude": [
"node_modules",
"bin",
"src/Testing/**/*",
"src/wasm/**/*",
"**/*.test.ts",
"**/*.spec.ts"
]