新增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

356
README.md
View File

@@ -8,12 +8,13 @@
## ✨ 特性
- 🚀 **轻量级 ECS 架构** - 基于实体组件系统,提供清晰的代码结构
-**高性能** - 可处理20万个实体@165.8FPS组件访问7200万次/秒
-**高性能** - 实体创建速度可达64万实体/秒,支持大规模实体管理
- 🎯 **智能优化** - 组件对象池、位掩码优化器、延迟索引更新等性能优化技术
- 📡 **事件系统** - 内置 Emitter 事件发射器,支持类型安全的事件管理
-**定时器系统** - 完整的定时器管理,支持延迟和重复任务
- 🔍 **查询系统** - 基于位掩码的高性能实体查询
- 🔍 **查询系统** - 基于位掩码的高性能实体查询,支持批量操作
- 🛠️ **性能监控** - 内置性能监控工具,帮助优化游戏性能
- 🎯 **对象池** - 内存管理优化,减少垃圾回收压力
- 🔧 **批量操作** - 支持批量实体创建、组件添加等高效操作
## 📦 安装
@@ -24,37 +25,35 @@ npm install @esengine/ecs-framework
## 📊 性能基准
```bash
# 运行性能基准测试
node benchmark.js
# 运行快速性能基准测试
npm run benchmark
# 运行完整性能测试
npm run test:performance
```
**框架性能数据**:
- 🚀 **实体创建**: 220万+个/秒 (50000个实体/22.73ms)
-**组件访问**: 7200万+次/秒 (5000个实体×2000次迭代)
- 🔧 **组件操作**: 3450万+次/秒 (添加/删除组件)
- 🔍 **查询速度**: 12000+次/秒 (单组件查询)
- 🎯 **处理能力**: 20万个实体@165.8FPS
**详细性能测试结果**:
### 🚀 实体创建性能
- **小规模**: 640,697 实体/秒 (1,000个实体/1.56ms)
- **中规模**: 250,345 实体/秒 (10,000个实体/39.94ms)
- **大规模**: 161,990 实体/秒 (500,000个实体/3.09秒)
### 🎯 核心操作性能
```
📊 实体创建性能
50000 个实体: 22.73ms (2199659个/秒)
📊 核心操作性能
实体创建: 640,697个/秒
组件添加: 596,929组件/秒
位掩码操作: 5,000,000次/秒
查询缓存: 零延迟访问
批量操作: 高效处理
🔍 组件访问性能
2000 次迭代: 139.67ms (71598669次访问/秒)
🧪 组件添加/删除性能
1000 次迭代: 289.66ms (34522936次操作/秒)
🔎 查询系统性能
单组件查询: 82.11ms/1000次 (12178次/秒)
多组件查询: 105.94ms/1000次 (9439次/秒)
复合查询: 135.01ms/1000次 (7407次/秒)
🎯 性能极限测试 (1秒钟固定时间测试)
5万个实体: 1.219ms/帧 (820.0FPS)
10万个实体: 2.976ms/帧 (335.9FPS)
20万个实体: 6.031ms/帧 (165.8FPS)
🔧 优化技术效果
组件对象池: 减少30-50%内存分配
位掩码优化器: 提升20-40%掩码性能
批量操作: 大幅减少创建时间
索引优化: 避免O(n)重复检查
缓存策略: 延迟清理机制
```
## 🚀 快速开始
@@ -74,104 +73,111 @@ function gameLoop() {
}
```
### 2. 创建场景
### 2. 高性能批量创建实体
```typescript
import { Scene, EntitySystem } from '@esengine/ecs-framework';
class GameScene extends Scene {
public initialize() {
// 创建玩家实体
const player = this.createEntity("Player");
// 批量创建实体
const entities = this.createEntities(1000, "Enemy");
// 添加自定义组件
const position = player.addComponent(new PositionComponent(100, 100));
const movement = player.addComponent(new MovementComponent());
// 批量添加组件
entities.forEach((entity, index) => {
entity.addComponent(new PositionComponent(
Math.random() * 1000,
Math.random() * 1000
));
entity.addComponent(new VelocityComponent());
});
// 添加系统
this.addEntityProcessor(new MovementSystem());
}
public onStart() {
console.log("游戏场景已启动");
}
}
// 设置当前场景
Core.scene = new GameScene();
```
### 3. 创建组件
```typescript
import { Component, Time } from '@esengine/ecs-framework';
class MovementComponent extends Component {
public speed: number = 100;
public direction = { x: 0, y: 0 };
public update() {
const position = this.entity.getComponent(PositionComponent);
if (position && (this.direction.x !== 0 || this.direction.y !== 0)) {
position.x += this.direction.x * this.speed * Time.deltaTime;
position.y += this.direction.y * this.speed * Time.deltaTime;
}
}
}
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
```
### 4. 创建系统
### 3. 使用组件对象池优化内存
```typescript
import { EntitySystem, Entity } from '@esengine/ecs-framework';
import { Component, ComponentPoolManager } from '@esengine/ecs-framework';
class MovementSystem extends EntitySystem {
protected process(entities: Entity[]) {
for (const entity of entities) {
const movement = entity.getComponent(MovementComponent);
if (movement) {
movement.update();
}
}
class BulletComponent extends Component {
public damage: number = 10;
public speed: number = 300;
// 重置方法用于对象池
public reset() {
this.damage = 10;
this.speed = 300;
}
}
// 注册组件池
ComponentPoolManager.getInstance().registerPool(BulletComponent, 1000);
// 使用对象池获取组件
const bullet = ComponentPoolManager.getInstance().getComponent(BulletComponent);
entity.addComponent(bullet);
// 释放回对象池
ComponentPoolManager.getInstance().releaseComponent(bullet);
```
### 4. 位掩码优化器加速查询
```typescript
import { BitMaskOptimizer } from '@esengine/ecs-framework';
// 注册常用组件类型
const optimizer = BitMaskOptimizer.getInstance();
optimizer.registerComponentType(PositionComponent);
optimizer.registerComponentType(VelocityComponent);
optimizer.registerComponentType(RenderComponent);
// 预计算常用掩码组合
optimizer.precomputeCommonMasks();
// 高效的掩码操作
const positionMask = optimizer.getComponentMask(PositionComponent);
const movementMask = optimizer.getCombinedMask([PositionComponent, VelocityComponent]);
```
## 📚 核心概念
### Entity实体
实体是游戏世界中的基本对象,作为组件的容器。实体本身不包含游戏逻辑,所有功能都通过组件来实现
实体是游戏世界中的基本对象,支持批量操作和高性能创建
```typescript
// 通过场景创建实体
// 单个实体创建
const entity = scene.createEntity("MyEntity");
// 设置实体属性
entity.tag = 1; // 设置标签用于分类
entity.updateOrder = 0; // 设置更新顺序
entity.enabled = true; // 设置启用状态
// 批量实体创建
const entities = scene.createEntities(1000, "Bullets");
// 添加组件来扩展功能
const positionComponent = entity.addComponent(new PositionComponent(100, 200));
const healthComponent = entity.addComponent(new HealthComponent(100));
// 实体属性设置
entity.tag = 1;
entity.updateOrder = 0;
entity.enabled = true;
```
### Component组件
组件包含数据和行为,定义了实体的特性
组件包含数据和行为,支持对象池优化
```typescript
import { Component } from '@esengine/ecs-framework';
import { Component, ComponentPoolManager } from '@esengine/ecs-framework';
class HealthComponent extends Component {
public maxHealth: number = 100;
public currentHealth: number = 100;
// 对象池重置方法
public reset() {
this.maxHealth = 100;
this.currentHealth = 100;
}
public takeDamage(damage: number) {
this.currentHealth = Math.max(0, this.currentHealth - damage);
if (this.currentHealth <= 0) {
@@ -179,16 +185,28 @@ class HealthComponent extends Component {
}
}
}
// 注册到对象池
ComponentPoolManager.getInstance().registerPool(HealthComponent, 500);
```
### System系统
系统处理实体集合,实现游戏逻辑
系统处理实体集合,支持批量处理优化
```typescript
import { EntitySystem, Entity } from '@esengine/ecs-framework';
class HealthSystem extends EntitySystem {
protected process(entities: Entity[]) {
// 批量处理实体
const batchSize = 1000;
for (let i = 0; i < entities.length; i += batchSize) {
const batch = entities.slice(i, i + batchSize);
this.processBatch(batch);
}
}
private processBatch(entities: Entity[]) {
for (const entity of entities) {
const health = entity.getComponent(HealthComponent);
if (health && health.currentHealth <= 0) {
@@ -201,94 +219,124 @@ class HealthSystem extends EntitySystem {
## 🎮 高级功能
### 事件系统
### 批量操作API
```typescript
import { Core, CoreEvents } from '@esengine/ecs-framework';
// 批量创建实体
const entities = scene.createEntities(5000, "Enemies");
// 监听事件
Core.emitter.addObserver(CoreEvents.frameUpdated, this.onFrameUpdate, this);
// 批量查询
const movingEntities = scene.getEntitiesWithComponents([PositionComponent, VelocityComponent]);
// 发射自定义事件
Core.emitter.emit("playerDied", { player: entity, score: 1000 });
// 移除监听
Core.emitter.removeObserver(CoreEvents.frameUpdated, this.onFrameUpdate);
```
### 定时器系统
```typescript
import { Core } from '@esengine/ecs-framework';
// 延迟执行
Core.schedule(2.0, false, this, (timer) => {
console.log("2秒后执行");
});
// 重复执行
Core.schedule(1.0, true, this, (timer) => {
console.log("每秒执行一次");
});
```
### 实体查询
```typescript
// 按名称查找
const player = scene.findEntity("Player");
// 按标签查找
const enemies = scene.findEntitiesByTag(1);
// 按ID查找
const entity = scene.findEntityById(123);
// 延迟缓存清理
scene.addEntity(entity, false); // 延迟缓存清理
// ... 添加更多实体
scene.querySystem.clearCache(); // 手动清理缓存
```
### 性能监控
```typescript
import { PerformanceMonitor } from '@esengine/ecs-framework';
import { Core } from '@esengine/ecs-framework';
// 获取性能数据
const monitor = PerformanceMonitor.instance;
console.log("平均FPS:", monitor.averageFPS);
console.log("内存使用:", monitor.memoryUsage);
// 获取性能统计
const stats = scene.getPerformanceStats();
console.log(`实体数量: ${stats.entityCount}`);
console.log(`查询缓存大小: ${stats.queryCacheSize}`);
console.log(`组件池统计:`, stats.componentPoolStats);
```
## 🛠️ 开发工具
### 对象池
### 内存优化
```typescript
// 创建对象
class BulletPool extends es.Pool<Bullet> {
protected createObject(): Bullet {
return new Bullet();
}
}
// 预热组件
ComponentPoolManager.getInstance().preWarmPools({
BulletComponent: 1000,
EffectComponent: 500,
PickupComponent: 200
});
const bulletPool = new BulletPool();
// 获取对象
const bullet = bulletPool.obtain();
// 释放对象
bulletPool.free(bullet);
// 清理未使用的组件
ComponentPoolManager.getInstance().clearUnusedComponents();
```
### 实体调试
## 🧪 测试和基准
### 运行测试套件
```bash
# 运行所有测试
npm run test
# 单元测试
npm run test:unit
# 性能测试
npm run test:performance
# 快速基准测试
npm run benchmark
```
### 自定义性能测试
```typescript
// 获取实体调试信息
const debugInfo = entity.getDebugInfo();
console.log("实体信息:", debugInfo);
import { runEntityCreationBenchmark } from './Testing/Performance/benchmark';
// 获取场景统计
const stats = scene.getStats();
console.log("场景统计:", stats);
// 运行自定义基准测试
await runEntityCreationBenchmark();
```
## 🔧 优化建议
### 大规模实体处理
1. **使用批量API**
```typescript
// ✅ 推荐:批量创建
const entities = scene.createEntities(10000, "Units");
// ❌ 避免:循环单个创建
for (let i = 0; i < 10000; i++) {
scene.createEntity("Unit" + i);
}
```
2. **启用对象池**
```typescript
// 预先注册常用组件池
ComponentPoolManager.getInstance().registerPool(BulletComponent, 2000);
ComponentPoolManager.getInstance().registerPool(EffectComponent, 1000);
```
3. **优化查询频率**
```typescript
// 缓存查询结果
if (frameCount % 5 === 0) {
this.cachedEnemies = scene.getEntitiesWithComponent(EnemyComponent);
}
```
### 移动端优化
- 实体数量建议 ≤ 20,000
- 启用组件对象池
- 减少查询频率
- 使用批量操作
## 📈 版本更新
### v2.0.6 (最新)
- 🚀 **高性能实体创建**: 支持64万实体/秒的创建速度
- 🎯 **组件对象池**: 减少内存分配开销
- ⚡ **位掩码优化器**: 加速组件查询和操作
- 🔧 **批量操作API**: 支持高效的批量实体创建
- 📊 **性能监控**: 完整的性能分析工具
- 🧪 **测试套件**: 单元测试、性能测试、集成测试
### 历史版本
- v1.x.x: 基础ECS架构实现
## 📖 文档
- [快速入门](docs/getting-started.md) - 从零开始学习框架使用

View File

@@ -1,21 +1,53 @@
# Security Policy
# 安全政策
## Supported Versions
## 支持的版本
Use this section to tell people about which versions of your project are
currently being supported with security updates.
我们为以下版本提供安全更新:
| Version | Supported |
| 版本 | 支持状态 |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
| 2.0.x | :white_check_mark: |
| 1.0.x | :x: |
## Reporting a Vulnerability
## 报告漏洞
Use this section to tell people how to report a vulnerability.
如果您发现了安全漏洞,请通过以下方式报告:
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
### 报告渠道
- **邮箱**: [安全邮箱将在实际部署时提供]
- **GitHub**: 创建私有安全报告(推荐)
### 报告流程
1. **不要**在公开的 issue 中报告安全漏洞
2. 提供详细的漏洞描述,包括:
- 受影响的版本
- 复现步骤
- 潜在的影响范围
- 如果可能,提供修复建议
### 响应时间
- **确认收到**: 72小时内
- **初步评估**: 1周内
- **修复发布**: 根据严重程度通常在2-4周内
### 处理流程
1. 我们会确认漏洞的存在和严重程度
2. 开发修复方案并进行测试
3. 发布安全更新
4. 在修复发布后,会在相关渠道公布漏洞详情
### 安全最佳实践
使用 ECS Framework 时,请遵循以下安全建议:
- 始终使用最新的稳定版本
- 定期更新依赖项
- 在生产环境中禁用调试模式
- 验证所有外部输入数据
- 不要在客户端存储敏感信息
感谢您帮助保持 ECS Framework 的安全性!

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env node
/**
* ECS框架性能基准测试入口
*
* 使用方法:
* node benchmark.js
*/
const { execSync } = require('child_process');
const path = require('path');
console.log('🚀 启动ECS框架性能基准测试...\n');
try {
// 运行性能测试
execSync('npm run test:framework:benchmark', {
stdio: 'inherit',
cwd: path.join(__dirname, 'source')
});
} catch (error) {
console.error('❌ 性能测试失败:', error.message);
process.exit(1);
}

View File

@@ -17,7 +17,7 @@ Core 是框架的核心管理类,负责游戏的生命周期管理。
### 创建和配置
```typescript
import { Core } from './Core';
import { Core } from '@esengine/ecs-framework';
// 创建核心实例(调试模式)
const core = Core.create(true);
@@ -29,7 +29,7 @@ const core = Core.create(false);
### 事件系统
```typescript
import { CoreEvents } from './ECS/CoreEvents';
import { CoreEvents } from '@esengine/ecs-framework';
// 监听核心事件
Core.emitter.addObserver(CoreEvents.frameUpdated, this.onUpdate, this);
@@ -62,7 +62,7 @@ Core.schedule(1.0, true, this, (timer) => {
### 创建和使用场景
```typescript
import { Scene } from './ECS/Scene';
import { Scene } from '@esengine/ecs-framework';
// 创建场景
const scene = new Scene();
@@ -77,6 +77,23 @@ scene.update(); // 更新场景
scene.end(); // 结束场景
```
### 批量实体管理
```typescript
// 批量创建实体 - 高性能
const entities = scene.createEntities(1000, "Enemy");
// 批量添加实体(延迟缓存清理)
entities.forEach(entity => {
scene.addEntity(entity, false); // 延迟清理
});
scene.querySystem.clearCache(); // 手动清理缓存
// 获取性能统计
const stats = scene.getPerformanceStats();
console.log(`实体数量: ${stats.entityCount}`);
```
## Entity实体
实体是游戏世界中的基本对象,包含位置、旋转、缩放等基本属性。
@@ -84,7 +101,7 @@ scene.end(); // 结束场景
### 实体的基本属性
```typescript
import { Vector2 } from './Math/Vector2';
import { Vector2 } from '@esengine/ecs-framework';
const entity = scene.createEntity("MyEntity");
@@ -161,7 +178,7 @@ console.log(debugInfo);
### 创建组件
```typescript
import { Component } from './ECS/Component';
import { Component } from '@esengine/ecs-framework';
class HealthComponent extends Component {
public maxHealth: number = 100;
@@ -244,6 +261,43 @@ entity.removeComponentByType(HealthComponent);
entity.removeAllComponents();
```
### 组件对象池优化
```typescript
import { Component, ComponentPoolManager } from '@esengine/ecs-framework';
class BulletComponent extends Component {
public damage: number = 10;
public speed: number = 300;
// 对象池重置方法
public reset() {
this.damage = 10;
this.speed = 300;
}
}
// 注册组件池
ComponentPoolManager.getInstance().registerPool(BulletComponent, 1000);
// 使用对象池获取组件
const bullet = ComponentPoolManager.getInstance().getComponent(BulletComponent);
entity.addComponent(bullet);
// 释放组件回对象池
ComponentPoolManager.getInstance().releaseComponent(bullet);
// 预热组件池
ComponentPoolManager.getInstance().preWarmPools({
BulletComponent: 1000,
EffectComponent: 500
});
// 获取池统计
const stats = ComponentPoolManager.getInstance().getPoolStats();
console.log('组件池统计:', stats);
```
## Scene场景
场景是实体和系统的容器,管理游戏世界的状态。
@@ -483,14 +537,86 @@ bulletPool.clear();
- 监控性能数据
- 合理使用时间缩放
## 高级性能优化功能
### 位掩码优化器
位掩码优化器可以预计算和缓存常用的组件掩码,提升查询性能。
```typescript
import { BitMaskOptimizer } from '@esengine/ecs-framework';
const optimizer = BitMaskOptimizer.getInstance();
// 注册组件类型
optimizer.registerComponentType(PositionComponent);
optimizer.registerComponentType(VelocityComponent);
optimizer.registerComponentType(RenderComponent);
// 预计算常用掩码组合
optimizer.precomputeCommonMasks();
// 获取优化的掩码
const positionMask = optimizer.getComponentMask(PositionComponent);
const movementMask = optimizer.getCombinedMask([PositionComponent, VelocityComponent]);
// 掩码操作
const hasBothComponents = optimizer.hasAllComponents(entityMask, movementMask);
const hasAnyComponent = optimizer.hasAnyComponent(entityMask, movementMask);
// 获取掩码分析
const analysis = optimizer.analyzeMask(entityMask);
console.log('掩码包含的组件类型:', analysis.componentTypes);
```
### 延迟索引更新器
批量更新索引可以显著提升大规模实体操作的性能。
```typescript
import { IndexUpdateBatcher } from '@esengine/ecs-framework';
const batcher = new IndexUpdateBatcher((updates) => {
// 处理批量更新
console.log(`批量处理 ${updates.length} 个索引更新`);
});
// 配置批量大小和延迟
batcher.configure(100, 16); // 批量大小100延迟16ms
// 添加更新任务
batcher.addUpdate("add", entity, componentMask);
batcher.addUpdate("remove", entity, componentMask);
// 强制刷新
batcher.flush();
```
### 批量操作API
```typescript
// 批量创建实体 - 最高性能
const entities = scene.createEntities(10000, "Bullets");
// 延迟缓存清理
entities.forEach(entity => {
scene.addEntity(entity, false); // 延迟清理
});
scene.querySystem.clearCache(); // 手动清理
// 批量查询优化
const movingEntities = scene.getEntitiesWithComponents([PositionComponent, VelocityComponent]);
```
## 总结
ECS Framework 提供了完整的实体组件系统架构:
- **Core** 管理游戏生命周期和全局功能
- **Entity** 作为游戏对象的基础容器
- **Component** 实现具体的功能模块
- **Component** 实现具体的功能模块,支持对象池优化
- **System** 处理游戏逻辑
- **Scene** 管理游戏世界状态
- **Scene** 管理游戏世界状态,支持批量操作
- **高级优化** 位掩码优化器、组件对象池、批量操作等
通过合理使用这些核心概念,可以构建出结构清晰、易于维护的游戏代码。
通过合理使用这些核心概念和优化功能,可以构建出高性能、结构清晰、易于维护的游戏代码。

View File

@@ -29,6 +29,23 @@ console.log(entity.name); // "Player"
console.log(entity.id); // 唯一的数字ID
```
### 批量创建实体(推荐)
```typescript
import { Scene } from '@esengine/ecs-framework';
const scene = new Scene();
// 批量创建1000个实体 - 高性能
const entities = scene.createEntities(1000, "Enemy");
// 批量配置
entities.forEach((entity, index) => {
entity.tag = 2; // 敌人标签
// 添加组件...
});
```
### 使用流式API创建
```typescript

View File

@@ -9,15 +9,21 @@ ecs-framework/
├── source/
│ ├── src/ # 源代码
│ │ ├── ECS/ # ECS核心系统
│ │ ├── Math/ # 数学运算
│ │ ├── Types/ # 类型定义
│ │ ── Utils/ # 工具类
│ │ ── Utils/ # 工具类
│ │ └── Testing/ # 测试文件
│ ├── scripts/ # 构建脚本
│ └── tsconfig.json # TypeScript配置
└── docs/ # 文档
```
## 安装和构建
## 安装和使用
### NPM 安装
```bash
npm install @esengine/ecs-framework
```
### 从源码构建
@@ -28,27 +34,28 @@ git clone https://github.com/esengine/ecs-framework.git
# 进入源码目录
cd ecs-framework/source
# 安装依赖
npm install
# 编译TypeScript
npx tsc
npm run build
```
### 直接使用
您可以直接将源码复制到项目中使用或者引用编译后的JavaScript文件。
## 基础设置
### 1. 导入框架
```typescript
// 导入核心类
import { Core } from './Core';
import { Entity } from './ECS/Entity';
import { Component } from './ECS/Component';
import { Scene } from './ECS/Scene';
import { QuerySystem } from './ECS/Core/QuerySystem';
import { Emitter } from './Utils/Emitter';
import { TimerManager } from './Utils/Timers/TimerManager';
import {
Core,
Entity,
Component,
Scene,
EntitySystem,
ComponentPoolManager,
BitMaskOptimizer
} from '@esengine/ecs-framework';
```
### 2. 创建基础管理器
@@ -57,9 +64,6 @@ import { TimerManager } from './Utils/Timers/TimerManager';
class GameManager {
private core: Core;
private scene: Scene;
private querySystem: QuerySystem;
private emitter: Emitter;
private timerManager: TimerManager;
constructor() {
// 创建核心实例
@@ -69,21 +73,30 @@ class GameManager {
this.scene = new Scene();
this.scene.name = "GameScene";
// 获取场景的查询系统
this.querySystem = this.scene.querySystem;
// 获取核心的事件系统和定时器
this.emitter = Core.emitter;
this.timerManager = this.core._timerManager;
// 设置当前场景
Core.scene = this.scene;
// 初始化优化功能
this.setupOptimizations();
}
private setupOptimizations() {
// 注册组件对象池
ComponentPoolManager.getInstance().preWarmPools({
PositionComponent: 1000,
VelocityComponent: 1000,
HealthComponent: 500
});
// 注册位掩码优化
const optimizer = BitMaskOptimizer.getInstance();
optimizer.registerComponentType(PositionComponent);
optimizer.registerComponentType(VelocityComponent);
optimizer.registerComponentType(HealthComponent);
optimizer.precomputeCommonMasks();
}
public update(deltaTime: number): void {
// 更新定时器
this.timerManager.update(deltaTime);
// 更新场景
this.scene.update();
@@ -122,6 +135,8 @@ gameLoop();
### 1. 定义组件
```typescript
import { Component, ComponentPoolManager } from '@esengine/ecs-framework';
// 位置组件
class PositionComponent extends Component {
public x: number = 0;
@@ -132,6 +147,12 @@ class PositionComponent extends Component {
this.x = x;
this.y = y;
}
// 对象池重置方法
public reset() {
this.x = 0;
this.y = 0;
}
}
// 速度组件
@@ -144,6 +165,11 @@ class VelocityComponent extends Component {
this.x = x;
this.y = y;
}
public reset() {
this.x = 0;
this.y = 0;
}
}
// 生命值组件
@@ -157,6 +183,11 @@ class HealthComponent extends Component {
this.currentHealth = maxHealth;
}
public reset() {
this.maxHealth = 100;
this.currentHealth = 100;
}
public takeDamage(damage: number): void {
this.currentHealth = Math.max(0, this.currentHealth - damage);
}
@@ -169,6 +200,11 @@ class HealthComponent extends Component {
return this.currentHealth <= 0;
}
}
// 注册组件到对象池
ComponentPoolManager.getInstance().registerPool(PositionComponent, 1000);
ComponentPoolManager.getInstance().registerPool(VelocityComponent, 1000);
ComponentPoolManager.getInstance().registerPool(HealthComponent, 500);
```
### 2. 创建实体
@@ -180,10 +216,19 @@ class GameManager {
public createPlayer(): Entity {
const player = this.scene.createEntity("Player");
// 添加组件
player.addComponent(new PositionComponent(400, 300));
player.addComponent(new VelocityComponent(0, 0));
player.addComponent(new HealthComponent(100));
// 使用对象池获取组件
const position = ComponentPoolManager.getInstance().getComponent(PositionComponent);
position.x = 400;
position.y = 300;
player.addComponent(position);
const velocity = ComponentPoolManager.getInstance().getComponent(VelocityComponent);
player.addComponent(velocity);
const health = ComponentPoolManager.getInstance().getComponent(HealthComponent);
health.maxHealth = 100;
health.currentHealth = 100;
player.addComponent(health);
// 设置标签和更新顺序
player.tag = 1; // 玩家标签
@@ -192,45 +237,64 @@ class GameManager {
return player;
}
public createEnemy(x: number, y: number): Entity {
const enemy = this.scene.createEntity("Enemy");
public createEnemies(count: number): Entity[] {
// 使用批量创建API - 高性能
const enemies = this.scene.createEntities(count, "Enemy");
enemy.addComponent(new PositionComponent(x, y));
enemy.addComponent(new VelocityComponent(50, 0));
enemy.addComponent(new HealthComponent(50));
// 批量配置敌人
enemies.forEach((enemy, index) => {
// 使用对象池获取组件
const position = ComponentPoolManager.getInstance().getComponent(PositionComponent);
position.x = Math.random() * 800;
position.y = Math.random() * 600;
enemy.addComponent(position);
const velocity = ComponentPoolManager.getInstance().getComponent(VelocityComponent);
velocity.x = (Math.random() - 0.5) * 100;
velocity.y = (Math.random() - 0.5) * 100;
enemy.addComponent(velocity);
const health = ComponentPoolManager.getInstance().getComponent(HealthComponent);
health.maxHealth = 50;
health.currentHealth = 50;
enemy.addComponent(health);
enemy.tag = 2; // 敌人标签
enemy.updateOrder = 1;
});
enemy.tag = 2; // 敌人标签
enemy.updateOrder = 1;
return enemies;
}
public destroyEntity(entity: Entity): void {
// 释放组件回对象池
entity.components.forEach(component => {
ComponentPoolManager.getInstance().releaseComponent(component);
});
return enemy;
// 销毁实体
entity.destroy();
}
}
```
## 使
### 3. 创建系统
```typescript
class GameManager {
// ... 之前的代码 ...
private updateSystems(deltaTime: number): void {
this.updateMovementSystem(deltaTime);
this.updateHealthSystem(deltaTime);
this.updateCollisionSystem();
}
private updateMovementSystem(deltaTime: number): void {
// 查询所有具有位置和速度组件的实体
const movableEntities = this.querySystem.queryTwoComponents(
import { EntitySystem, Entity } from '@esengine/ecs-framework';
class MovementSystem extends EntitySystem {
protected process(entities: Entity[]): void {
// 使用高性能查询获取移动实体
const movableEntities = this.scene.querySystem.queryTwoComponents(
PositionComponent,
VelocityComponent
);
movableEntities.forEach(({ entity, component1: position, component2: velocity }) => {
// 更新位置
position.x += velocity.x * deltaTime;
position.y += velocity.y * deltaTime;
position.x += velocity.x * Time.deltaTime;
position.y += velocity.y * Time.deltaTime;
// 边界检查
if (position.x < 0 || position.x > 800) {
@@ -241,177 +305,95 @@ class GameManager {
}
});
}
private updateHealthSystem(deltaTime: number): void {
// 查询所有具有生命值组件的实体
const healthEntities = this.querySystem.queryComponentTyped(HealthComponent);
}
class HealthSystem extends EntitySystem {
protected process(entities: Entity[]): void {
const healthEntities = this.scene.querySystem.queryComponentTyped(HealthComponent);
const deadEntities: Entity[] = [];
healthEntities.forEach(({ entity, component: health }) => {
// 检查死亡
if (health.isDead()) {
deadEntities.push(entity);
}
});
// 移除死亡实体
// 销毁死亡实体
deadEntities.forEach(entity => {
entity.destroy();
});
}
private updateCollisionSystem(): void {
// 获取玩家
const players = this.scene.findEntitiesByTag(1); // 玩家标签
const enemies = this.scene.findEntitiesByTag(2); // 敌人标签
players.forEach(player => {
const playerPos = player.getComponent(PositionComponent);
const playerHealth = player.getComponent(HealthComponent);
if (!playerPos || !playerHealth) return;
enemies.forEach(enemy => {
const enemyPos = enemy.getComponent(PositionComponent);
if (!enemyPos) return;
// 简单的距离检测
const distance = Math.sqrt(
Math.pow(playerPos.x - enemyPos.x, 2) +
Math.pow(playerPos.y - enemyPos.y, 2)
);
if (distance < 50) { // 碰撞距离
playerHealth.takeDamage(10);
console.log(`当前生命值: ${playerHealth.currentHealth}`);
}
});
this.scene.removeEntity(entity);
});
}
}
```
## 使用事件系统
## 高级功能
框架内置了事件系统,用于组件间通信:
### 1. 性能监控
```typescript
// 定义事件类型
enum GameEvents {
PLAYER_DIED = 'playerDied',
ENEMY_SPAWNED = 'enemySpawned',
SCORE_CHANGED = 'scoreChanged'
}
class GameManager {
// ... 之前的代码 ...
constructor() {
// ... 之前的代码 ...
public getPerformanceStats(): void {
const stats = this.scene.getPerformanceStats();
console.log(`实体数量: ${stats.entityCount}`);
console.log(`查询缓存大小: ${stats.queryCacheSize}`);
// 监听事件
this.emitter.on(GameEvents.PLAYER_DIED, this.onPlayerDied.bind(this));
this.emitter.on(GameEvents.ENEMY_SPAWNED, this.onEnemySpawned.bind(this));
}
private onPlayerDied(player: Entity): void {
console.log('游戏结束!');
// 重置游戏或显示游戏结束界面
}
private onEnemySpawned(enemy: Entity): void {
console.log('新敌人出现!');
}
private updateHealthSystem(deltaTime: number): void {
const healthEntities = this.querySystem.queryComponentTyped(HealthComponent);
healthEntities.forEach(({ entity, component: health }) => {
if (health.isDead()) {
// 发送死亡事件
if (entity.tag === 1) { // 玩家
this.emitter.emit(GameEvents.PLAYER_DIED, entity);
}
entity.destroy();
}
});
const poolStats = ComponentPoolManager.getInstance().getPoolStats();
console.log('组件池统计:', poolStats);
}
}
```
## 使用定时器
框架提供了强大的定时器系统:
### 2. 批量操作
```typescript
class GameManager {
// ... 之前的代码 ...
public startGame(): void {
// 创建玩家
this.createPlayer();
// 每2秒生成一个敌人
Core.schedule(2.0, true, this, (timer) => {
const x = Math.random() * 800;
const y = Math.random() * 600;
const enemy = this.createEnemy(x, y);
this.emitter.emit(GameEvents.ENEMY_SPAWNED, enemy);
});
// 5秒后增加敌人生成速度
Core.schedule(5.0, false, this, (timer) => {
console.log('游戏难度提升!');
// 可以在这里修改敌人生成间隔
});
}
}
// 批量创建大量实体
const bullets = this.scene.createEntities(1000, "Bullet");
// 批量查询
const enemies = this.scene.getEntitiesWithComponents([PositionComponent, HealthComponent]);
// 延迟缓存清理(高性能)
bullets.forEach(bullet => {
this.scene.addEntity(bullet, false); // 延迟清理
});
this.scene.querySystem.clearCache(); // 手动清理
```
### 3. 事件系统
```typescript
import { Core, CoreEvents } from '@esengine/ecs-framework';
// 监听事件
Core.emitter.addObserver(CoreEvents.frameUpdated, this.onFrameUpdate, this);
// 发射自定义事件
Core.emitter.emit("playerDied", { player: entity, score: 1000 });
// 移除监听
Core.emitter.removeObserver(CoreEvents.frameUpdated, this.onFrameUpdate);
```
## 完整示例
以下是一个完整的小游戏示例,展示了框架的主要功能:
```typescript
// 导入框架
import { Core } from './Core';
import { Entity } from './ECS/Entity';
import { Component } from './ECS/Component';
import { Scene } from './ECS/Scene';
import { QuerySystem } from './ECS/Core/QuerySystem';
import { Emitter } from './Utils/Emitter';
import {
Core,
Entity,
Component,
Scene,
EntitySystem,
ComponentPoolManager,
BitMaskOptimizer,
Time
} from '@esengine/ecs-framework';
// 定义组件
class PositionComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class VelocityComponent extends Component {
constructor(public x: number = 0, public y: number = 0) {
super();
}
}
class HealthComponent extends Component {
constructor(public maxHealth: number = 100) {
super();
this.currentHealth = maxHealth;
}
public currentHealth: number;
public takeDamage(damage: number): void {
this.currentHealth = Math.max(0, this.currentHealth - damage);
}
public isDead(): boolean {
return this.currentHealth <= 0;
}
}
// 定义组件(前面已定义)
// ... PositionComponent, VelocityComponent, HealthComponent ...
// 游戏事件
enum GameEvents {
@@ -423,141 +405,124 @@ enum GameEvents {
class SimpleGame {
private core: Core;
private scene: Scene;
private querySystem: QuerySystem;
private emitter: Emitter;
private isRunning: boolean = false;
constructor() {
this.core = Core.create(true);
this.scene = new Scene();
this.scene.name = "SimpleGame";
this.querySystem = this.scene.querySystem;
this.emitter = Core.emitter;
// 设置场景
this.scene.name = "GameScene";
Core.scene = this.scene;
// 监听事件
this.emitter.on(GameEvents.PLAYER_DIED, () => {
console.log('游戏结束!');
this.isRunning = false;
this.setupOptimizations();
this.setupSystems();
this.setupEvents();
}
private setupOptimizations(): void {
// 预热组件池
ComponentPoolManager.getInstance().preWarmPools({
PositionComponent: 2000,
VelocityComponent: 2000,
HealthComponent: 1000
});
// 注册位掩码优化
const optimizer = BitMaskOptimizer.getInstance();
optimizer.registerComponentType(PositionComponent);
optimizer.registerComponentType(VelocityComponent);
optimizer.registerComponentType(HealthComponent);
optimizer.precomputeCommonMasks();
}
private setupSystems(): void {
this.scene.addEntityProcessor(new MovementSystem());
this.scene.addEntityProcessor(new HealthSystem());
}
private setupEvents(): void {
Core.emitter.addObserver(GameEvents.PLAYER_DIED, this.onPlayerDied, this);
Core.emitter.addObserver(GameEvents.ENEMY_SPAWNED, this.onEnemySpawned, this);
}
public start(): void {
console.log('游戏开始!');
this.isRunning = true;
// 创建玩家
// 创建游戏实体
this.createPlayer();
// 定期生成敌人
Core.schedule(2.0, true, this, (timer) => {
if (this.isRunning) {
this.createEnemy();
}
});
this.createEnemies(100);
// 启动游戏循环
this.gameLoop();
}
public stop(): void {
this.isRunning = false;
// 清理组件池
ComponentPoolManager.getInstance().clearAllPools();
}
private createPlayer(): Entity {
const player = this.scene.createEntity("Player");
player.addComponent(new PositionComponent(400, 300));
player.addComponent(new VelocityComponent(100, 0));
player.addComponent(new HealthComponent(100));
player.tag = 1; // 玩家标签
const position = ComponentPoolManager.getInstance().getComponent(PositionComponent);
position.x = 400;
position.y = 300;
player.addComponent(position);
const velocity = ComponentPoolManager.getInstance().getComponent(VelocityComponent);
player.addComponent(velocity);
const health = ComponentPoolManager.getInstance().getComponent(HealthComponent);
health.maxHealth = 100;
health.currentHealth = 100;
player.addComponent(health);
player.tag = 1;
return player;
}
private createEnemy(): Entity {
const enemy = this.scene.createEntity("Enemy");
const x = Math.random() * 800;
const y = Math.random() * 600;
private createEnemies(count: number): Entity[] {
// 使用高性能批量创建
const enemies = this.scene.createEntities(count, "Enemy");
enemy.addComponent(new PositionComponent(x, y));
enemy.addComponent(new VelocityComponent(-50, 0));
enemy.addComponent(new HealthComponent(50));
enemy.tag = 2; // 敌人标签
enemies.forEach((enemy, index) => {
const position = ComponentPoolManager.getInstance().getComponent(PositionComponent);
position.x = Math.random() * 800;
position.y = Math.random() * 600;
enemy.addComponent(position);
const velocity = ComponentPoolManager.getInstance().getComponent(VelocityComponent);
velocity.x = (Math.random() - 0.5) * 100;
velocity.y = (Math.random() - 0.5) * 100;
enemy.addComponent(velocity);
const health = ComponentPoolManager.getInstance().getComponent(HealthComponent);
health.maxHealth = 50;
health.currentHealth = 50;
enemy.addComponent(health);
enemy.tag = 2;
});
this.emitter.emit(GameEvents.ENEMY_SPAWNED, enemy);
return enemy;
return enemies;
}
private onPlayerDied(event: any): void {
console.log("游戏结束!玩家死亡");
this.stop();
}
private onEnemySpawned(event: any): void {
console.log("敌人出现!");
}
private update(deltaTime: number): void {
// 更新定时器
this.core._timerManager.update(deltaTime);
// 更新场景
this.scene.update();
// 更新游戏系统
this.updateMovement(deltaTime);
this.updateCollision();
this.updateHealth();
}
private updateMovement(deltaTime: number): void {
const movableEntities = this.querySystem.queryTwoComponents(
PositionComponent,
VelocityComponent
);
movableEntities.forEach(({ entity, component1: pos, component2: vel }) => {
pos.x += vel.x * deltaTime;
pos.y += vel.y * deltaTime;
// 边界检查
if (pos.x < 0 || pos.x > 800) vel.x = -vel.x;
if (pos.y < 0 || pos.y > 600) vel.y = -vel.y;
});
}
private updateCollision(): void {
const players = this.scene.findEntitiesByTag(1);
const enemies = this.scene.findEntitiesByTag(2);
players.forEach(player => {
const playerPos = player.getComponent(PositionComponent);
const playerHealth = player.getComponent(HealthComponent);
if (!playerPos || !playerHealth) return;
enemies.forEach(enemy => {
const enemyPos = enemy.getComponent(PositionComponent);
if (!enemyPos) return;
const distance = Math.sqrt(
Math.pow(playerPos.x - enemyPos.x, 2) +
Math.pow(playerPos.y - enemyPos.y, 2)
);
if (distance < 50) {
playerHealth.takeDamage(10);
console.log(`玩家生命值: ${playerHealth.currentHealth}`);
}
});
});
}
private updateHealth(): void {
const healthEntities = this.querySystem.queryComponentTyped(HealthComponent);
const deadEntities: Entity[] = [];
healthEntities.forEach(({ entity, component: health }) => {
if (health.isDead()) {
deadEntities.push(entity);
if (entity.tag === 1) { // 玩家死亡
this.emitter.emit(GameEvents.PLAYER_DIED, entity);
}
}
});
// 移除死亡实体
deadEntities.forEach(entity => {
entity.destroy();
});
}
private gameLoop(): void {
@@ -584,6 +549,23 @@ const game = new SimpleGame();
game.start();
```
## 性能优化建议
### 1. 大规模实体处理
- 使用 `createEntities()` 批量创建实体
- 启用组件对象池减少内存分配
- 使用延迟缓存清理机制
### 2. 查询优化
- 缓存频繁查询的结果
- 使用 `BitMaskOptimizer` 优化掩码操作
- 减少不必要的查询频率
### 3. 内存管理
- 预热常用组件池
- 及时释放不用的组件回对象池
- 定期清理未使用的缓存
## 下一步
现在您已经掌握了 ECS Framework 的基础用法,可以继续学习:
@@ -591,13 +573,14 @@ game.start();
- [实体使用指南](entity-guide.md) - 详细了解实体的所有功能和用法
- [核心概念](core-concepts.md) - 深入了解 ECS 架构和设计原理
- [查询系统使用指南](query-system-usage.md) - 学习高性能查询系统的详细用法
- [性能基准](performance.md) - 了解框架的性能表现和优化建议
## 常见问题
### Q: 如何在不同游戏引擎中集成?
A: ECS Framework 是引擎无关的,您只需要:
1. 将框架源码复制到项目中
1. 通过npm安装框架 `npm install @esengine/ecs-framework`
2. 在游戏引擎的主循环中调用 `scene.update()`
3. 根据需要集成渲染、输入等引擎特定功能
@@ -605,22 +588,20 @@ A: ECS Framework 是引擎无关的,您只需要:
A: 框架本身不提供输入处理,建议:
1. 创建一个输入组件来存储输入状态
2. 在游戏循环中更新输入状态
3. 在相关组件中读取输入状态并处理
2. 在游戏引擎的输入回调中更新输入组件
3. 创建输入处理系统来响应输入状态
### Q: 如何调试
### Q: 如何优化大规模实体性能
A: 框架提供了多种调试功能
- 使用 `entity.getDebugInfo()` 查看实体信息
- 使用 `querySystem.getPerformanceReport()` 查看查询性能
- 使用 `querySystem.getStats()` 查看详细统计信息
A: 关键优化策略
1. 启用组件对象池:`ComponentPoolManager.getInstance().registerPool()`
2. 使用批量操作:`scene.createEntities()`
3. 缓存查询结果,减少查询频率
4. 使用位掩码优化器:`BitMaskOptimizer.getInstance()`
### Q: 性能如何优化
### Q: 组件对象池何时有效
A: 框架已经内置了多种性能优化
- 使用位掩码进行快速组件匹配
- 多级索引系统加速查询
- 智能缓存减少重复计算
- 批量操作减少开销
建议定期调用 `querySystem.optimizeIndexes()` 来自动优化配置。
A: 对象池在以下情况下最有效
- 频繁创建和销毁相同类型的组件
- 组件数量大于1000个
- 游戏运行时间较长,需要避免垃圾回收压力

View File

@@ -5,204 +5,302 @@
## 🚀 快速测试
```bash
# 在项目根目录运行
node benchmark.js
# 快速性能基准测试
npm run benchmark
# 完整性能测试
npm run test:performance
# 单元测试
npm run test:unit
```
## 📊 性能基准数据
> 测试环境: Node.js, 现代桌面CPU
> 测试时间: 2025年
> 测试环境: Node.js, Windows 10, 现代桌面CPU
### 1. 实体创建性能
| 实体数量 | 创建时间 | 创建速度 | 每个实体耗时 |
|---------|---------|---------|-------------|
| 1,000 | 1.11ms | 903,751个/秒 | 0.0011ms |
| 5,000 | 3.47ms | 1,441,462个/秒 | 0.0007ms |
| 10,000 | 6.91ms | 1,446,341个/秒 | 0.0007ms |
| 20,000 | 7.44ms | 2,686,764个/秒 | 0.0004ms |
| 50,000 | 22.73ms | 2,199,659个/秒 | 0.0005ms |
| 实体数量 | 创建时间 | 创建速度 | 每个实体耗时 | 性能等级 |
|---------|---------|---------|-------------|---------|
| 1,000 | 1.56ms | 640,697个/秒 | 0.0016ms | 🚀 极致 |
| 5,000 | 19.47ms | 256,805个/秒 | 0.0039ms | 🚀 极致 |
| 10,000 | 39.94ms | 250,345个/秒 | 0.0040ms | 🚀 极致 |
| 50,000 | 258.17ms | 193,673个/秒 | 0.0052ms | ✅ 优秀 |
| 100,000 | 463.04ms | 215,963个/秒 | 0.0046ms | ✅ 优秀 |
| 500,000 | 3,087ms | 161,990个/秒 | 0.0062ms | ✅ 优秀 |
**结论**: 实体创建性能优秀,平均每秒可创建 **220万+个实体**
**结论**: 🚀 实体创建性能达到极致水平大规模创建50万实体仅需3秒
### 2. 组件访问性能
### 2. 性能瓶颈分析 (500,000个实体)
| 迭代次数 | 总耗时 | 访问速度 | 每次访问耗时 |
|---------|--------|---------|-------------|
| 100次 | 13.27ms | 37,678,407次/秒 | 0.027μs |
| 500次 | 34.27ms | 72,957,553次/秒 | 0.014μs |
| 1000次 | 68.85ms | 72,624,911次/秒 | 0.014μs |
| 2000次 | 139.67ms | 71,598,669次/秒 | 0.014μs |
**当前瓶颈分布**:
```
实体创建: 46.3% (1,429ms)
组件添加: 53.5% (1,651ms) ← 主要瓶颈
标签分配: 0.2% (7ms)
```
**结论**: ✅ 组件访问性能优秀,平均每秒可访问 **7200万+次**
**特征**: 框架实现了均衡的性能分布,各部分开销相对合理
### 3. 组件操作性能
### 3. 组件添加性能详细分析
| 迭代次数 | 总耗时 | 操作速度 | 每次操作耗时 |
|---------|--------|---------|-------------|
| 100次 | 36.89ms | 27,105,193次/秒 | 0.037μs |
| 500次 | 147.42ms | 33,915,665次/秒 | 0.029μs |
| 1000次 | 289.66ms | 34,522,936次/秒 | 0.029μs |
| 组件类型 | 添加速度 | 平均耗时 | 性能等级 |
|---------|---------|---------|---------|
| PositionComponent | 596,929组件/秒 | 0.0017ms | 🚀 极致 |
| VelocityComponent | 1,186,770组件/秒 | 0.0008ms | 🚀 极致 |
| HealthComponent | 841,982组件/秒 | 0.0012ms | 🚀 极致 |
| RenderComponent | 763,351组件/秒 | 0.0013ms | 🚀 极致 |
| AIComponent | 185,964组件/秒 | 0.0054ms | ✅ 优秀 |
**结论**: ✅ 组件添加/删除性能优秀,平均每秒可操作 **3450万+次**
### 4. 优化技术性能影响
### 4. 查询系统性能
| 优化技术 | 性能提升 | 内存影响 | 适用场景 |
|---------|---------|---------|---------|
| 组件对象池 | 30-50% | 减少分配 | 频繁创建/销毁 |
| 位掩码优化器 | 20-40% | 缓存开销 | 大量查询操作 |
| 批量操作 | 显著提升 | 无明显影响 | 大规模实体创建 |
| 延迟索引更新 | 60-80% | 临时内存增加 | 批量实体操作 |
| 索引去重优化 | 避免O(n) | 轻微内存增加 | 防止重复实体 |
#### 4.1 单组件查询
| 查询次数 | 总耗时 | 查询速度 | 每次查询耗时 |
|---------|--------|---------|-------------|
| 100次 | 10.37ms | 9,639次/秒 | 0.104ms |
| 500次 | 41.17ms | 12,144次/秒 | 0.082ms |
| 1000次 | 82.11ms | 12,178次/秒 | 0.082ms |
### 5. 查询系统性能
#### 4.2 多组件查询
| 查询次数 | 总耗时 | 查询速度 | 每次查询耗时 |
|---------|--------|---------|-------------|
| 100次 | 11.22ms | 8,914次/秒 | 0.112ms |
| 500次 | 54.85ms | 9,116次/秒 | 0.110ms |
| 1000次 | 105.94ms | 9,439次/秒 | 0.106ms |
#### 5.1 基础查询性能
| 查询类型 | 查询速度 | 每次查询耗时 | 性能等级 |
|---------|---------|-------------|---------|
| 单组件查询 | 12,178次/秒 | 0.082ms | ✅ 优秀 |
| 多组件查询 | 9,439次/秒 | 0.106ms | ✅ 优秀 |
| 复合查询 | 7,407次/秒 | 0.135ms | ✅ 良好 |
#### 4.3 复合查询 (组件+标签)
| 查询次数 | 总耗时 | 查询速度 | 每次查询耗时 |
|---------|--------|---------|-------------|
| 100次 | 15.80ms | 6,327次/秒 | 0.158ms |
| 500次 | 65.77ms | 7,602次/秒 | 0.132ms |
| 1000次 | 135.01ms | 7,407次/秒 | 0.135ms |
#### 5.2 缓存查询性能
| 缓存状态 | 访问速度 | 性能特征 |
|---------|---------|---------|
| 缓存命中 | 零延迟 | 🚀 即时响应 |
| 缓存未命中 | 标准查询 | ✅ 自动构建 |
| 缓存清理 | 批量延迟 | 🔧 优化策略 |
**结论**: ⚠️ 查询性能正常,平均每秒可查询 **12000+次**
### 6. 新功能性能基准
### 5. 性能极限测试
#### 6.1 组件对象池性能
```
📊 对象池 vs 直接创建 (10,000次操作)
对象池获取: 1.65ms (6,060,606次/秒)
直接创建: 1.51ms (6,622,516次/秒)
⚠️ 小规模测试中对象池可能略慢,但在大规模应用中:
- 减少30-50%的内存分配
- 避免垃圾回收压力
- 提升长期运行稳定性
```
| 实体数量 | 创建时间 | 处理时间/帧 | FPS | 状态 |
|---------|---------|------------|-----|------|
| 10,000 | 1.55ms | 0.137ms | 7264.0 | ✅ |
| 25,000 | 3.91ms | 0.432ms | 2311.4 | ✅ |
| 50,000 | 12.40ms | 1.219ms | 820.0 | ✅ |
| 100,000 | 58.93ms | 2.976ms | 335.9 | ✅ |
| 200,000 | 51.43ms | 6.031ms | 165.8 | ✅ |
#### 6.2 位掩码优化器性能
```
🔥 位掩码操作性能 (100,000次操作)
单个掩码创建: 20.00ms (5,000,000次/秒)
组合掩码创建: 53.69ms (1,862,285次/秒)
缓存掩码访问: <1ms (近零延迟)
```
**结论**: 🚀 框架极限性能优秀,可处理 **20万个实体@165.8FPS** 仍维持高性能
## 🎯 性能扩展性分析
## 🎯 性能瓶颈分析
### 实体创建扩展性
```
📈 创建速度趋势分析
1K-10K实体: 250,000-640,000 实体/秒 (优秀)
10K-100K实体: 200,000-250,000 实体/秒 (良好)
100K-500K实体: 160,000-220,000 实体/秒 (稳定)
结论: 性能随规模稳定下降,无突然性能悬崖
```
### 主要瓶颈
1. **查询系统** (相对瓶颈)
- 单组件查询: ~12,000次/秒
- 多组件查询: ~9,400次/秒
- 复合查询: ~7,400次/秒
- **原因**: 需要遍历所有实体进行过滤
2. **大规模实体处理** (可接受)
- 10万个实体: 335.9 FPS
- 20万个实体: 165.8 FPS
- **原因**: 线性时间复杂度,符合预期
### 非瓶颈项
**实体创建**: 220万+个/秒,性能优秀
**组件访问**: 7200万+次/秒,性能优秀
**组件操作**: 3450万+次/秒,性能优秀
**系统处理**: 20万个实体@165.8FPS,性能优秀
## 📈 时间复杂度分析
| 操作类型 | 时间复杂度 | 性能等级 | 说明 |
|---------|-----------|---------|------|
| 实体创建 | O(1) | ✅ 优秀 | 常数时间创建 |
| 组件访问 | O(1) | ✅ 优秀 | 哈希表查找 |
| 组件操作 | O(1) | ✅ 优秀 | 常数时间添加/删除 |
| 单组件查询 | O(n) | ⚠️ 正常 | 线性遍历实体 |
| 多组件查询 | O(n×m) | ⚠️ 正常 | 遍历实体×组件数 |
| 系统处理 | O(n) | ✅ 优秀 | 线性处理实体 |
### 内存使用效率
| 实体数量 | 内存使用 | 每实体内存 | 内存效率 |
|---------|---------|-----------|---------|
| 1,000 | 3.5MB | 3.5KB | 🚀 极致 |
| 5,000 | 7.1MB | 1.4KB | 🚀 极致 |
| 10,000 | 20.8MB | 2.1KB | ✅ 优秀 |
| 50,000 | ~100MB | ~2KB | ✅ 优秀 |
## 💡 性能优化建议
### 对于查询密集型应用
### 1. 实体创建最佳实践
1. **缓存查询结果**
```typescript
// 缓存常用查询
const cachedPlayers = scene.getEntitiesWithComponents([Position, Player]);
```
**✅ 推荐做法**:
```typescript
// 使用批量创建API
const entities = scene.createEntities(10000, "Enemies");
2. **减少查询频率**
```typescript
// 每5帧查询一次而不是每帧
if (frameCount % 5 === 0) {
updateEnemyList();
}
```
// 延迟缓存清理
entities.forEach(entity => {
scene.addEntity(entity, false); // 延迟清理
});
scene.querySystem.clearCache(); // 手动清理
```
3. **使用更精确的查询**
```typescript
// 优先使用单组件查询
const entities = scene.getEntitiesWithComponent(Position);
```
**❌ 避免做法**:
```typescript
// 避免循环单个创建
for (let i = 0; i < 10000; i++) {
scene.createEntity("Enemy" + i); // 每次触发缓存清理
}
```
### 对于大规模实体应用
### 2. 组件池优化策略
1. **分批处理**
```typescript
// 分批处理大量实体
const batchSize = 1000;
for (let i = 0; i < entities.length; i += batchSize) {
processBatch(entities.slice(i, i + batchSize));
}
```
**预热策略**:
```typescript
// 预热常用组件池
ComponentPoolManager.getInstance().preWarmPools({
BulletComponent: 2000, // 子弹大量创建
EffectComponent: 1000, // 特效频繁使用
PickupComponent: 500 // 道具适量缓存
});
```
2. **LOD系统**
```typescript
// 根据距离调整处理频率
if (distance > 100) {
if (frameCount % 10 !== 0) continue; // 远距离实体降低更新频率
}
```
**使用模式**:
```typescript
// 高效的组件复用
const bullet = ComponentPoolManager.getInstance().getComponent(BulletComponent);
bullet.reset(); // 重置状态
entity.addComponent(bullet);
## 🌍 实际应用指南
// 销毁时释放到池
ComponentPoolManager.getInstance().releaseComponent(bullet);
```
### 不同平台的建议
### 3. 查询优化策略
| 平台 | 推荐实体数量 | 查询频率 | 备注 |
|------|-------------|---------|------|
| 桌面端 | ≤100,000 | 高频查询可接受 | 性能充足 |
| Web端 | ≤50,000 | 中等查询频率 | 考虑浏览器限制 |
| 移动端 | ≤20,000 | 低频查询 | 性能和电池优化 |
**缓存策略**:
```typescript
// 缓存频繁查询结果
class MovementSystem extends EntitySystem {
private cachedMovingEntities: Entity[];
private lastCacheFrame: number = 0;
protected process(entities: Entity[]) {
// 每5帧更新一次缓存
if (Time.frameCount - this.lastCacheFrame > 5) {
this.cachedMovingEntities = scene.getEntitiesWithComponents([Position, Velocity]);
this.lastCacheFrame = Time.frameCount;
}
// 使用缓存结果
this.processMovement(this.cachedMovingEntities);
}
}
```
### 游戏类型建议
### 4. 不同规模应用建议
| 游戏类型 | 典型实体数 | 主要瓶颈 | 优化重点 |
|---------|-----------|---------|---------|
| 2D平台游戏 | 1,000-5,000 | 无明显瓶颈 | 专注游戏逻辑 |
| 2D射击游戏 | 5,000-20,000 | 碰撞检测 | 空间分割算法 |
| RTS游戏 | 10,000-50,000 | 查询系统 | 缓存+分批处理 |
| MMO游戏 | 50,000+ | 网络+查询 | 分区+优化查询 |
#### 小型游戏 (< 5,000实体)
- ✅ 可以随意使用所有功能
- ✅ 不需要特殊优化
- ✅ 专注于游戏逻辑开发
## 🔬 测试方法
#### 中型游戏 (5,000-50,000实体)
- ✅ 使用批量操作API
- ✅ 启用组件对象池
- ⚠️ 注意查询频率
### 运行完整基准测试
#### 大型游戏 (50,000+实体)
- 🚀 必须使用批量操作
- 🚀 必须启用对象池
- 🚀 必须缓存查询结果
- 🚀 考虑分区处理
## 🌍 平台性能对比
### Windows 桌面端 (测试平台)
- **实体创建**: 640,697实体/秒
- **组件操作**: 596,929组件/秒
- **推荐实体数**: ≤ 200,000
### 预估其他平台性能
| 平台类型 | 预估性能比例 | 推荐实体数 | 特殊注意 |
|---------|-------------|-----------|---------|
| macOS桌面 | 90-100% | ≤ 180,000 | 内存管理优秀 |
| Linux桌面 | 95-105% | ≤ 200,000 | 性能最优 |
| Chrome浏览器 | 60-80% | ≤ 100,000 | V8引擎优化 |
| Firefox浏览器 | 50-70% | ≤ 80,000 | SpiderMonkey限制 |
| Safari浏览器 | 55-75% | ≤ 90,000 | JavaScriptCore |
| Node.js服务器 | 100-110% | ≤ 500,000 | 服务器级性能 |
| Android Chrome | 30-50% | ≤ 30,000 | 移动端限制 |
| iOS Safari | 40-60% | ≤ 40,000 | iOS优化较好 |
## 🔬 测试环境详情
### 硬件环境
- **操作系统**: Windows 10 (Build 26100)
- **处理器**: 现代桌面CPU
- **内存**: 充足RAM
- **存储**: SSD高速存储
### 软件环境
- **Node.js**: v16+
- **TypeScript**: v5.8.3
- **ECS框架版本**: v2.0.6
- **测试工具**: 内置基准测试套件
### 测试方法
- **实体配置**: 位置、速度、生命值、渲染、AI组件随机分配
- **测试迭代**: 多次测试取平均值
- **内存监控**: 实时内存使用情况
- **性能指标**: performance.now()高精度计时
## 📋 性能测试清单
### 运行完整性能测试
```bash
# 项目根目录
node benchmark.js
# 1. 快速基准测试 (2-3分钟)
npm run benchmark
# 2. 完整性能测试 (10-15分钟)
npm run test:performance
# 3. 单元测试验证 (30秒)
npm run test:unit
# 4. 所有测试 (15-20分钟)
npm run test
```
### 自定义测试
### 自定义性能测试
```typescript
// 在source目录下
npm run test:framework:benchmark
import { runEntityCreationBenchmark } from '@esengine/ecs-framework/Testing/Performance/benchmark';
// 自定义规模测试
await runEntityCreationBenchmark([1000, 5000, 10000]);
// 组件性能测试
await runComponentPerformanceTest();
// 查询性能测试
await runQueryPerformanceTest();
```
## 📝 测试环境
## 🏆 性能总结
- **Node.js版本**: 16+
- **TypeScript版本**: 5.8.3
- **测试实体数**: 5,000个 (带position、velocity组件)
- **测试迭代**: 多次取平均值
- **硬件**: 现代桌面CPU
### 🎯 核心能力
1. **实体创建速度**: 最高64万实体/秒
2. **大规模处理**: 50万实体仅需3秒创建
3. **均衡性能**: 各组件开销分布合理
4. **扩展性**: 性能随规模线性下降,无突然悬崖
### 🔧 技术特点
1. **批量操作架构** - 大幅减少单次操作开销
2. **智能缓存策略** - 延迟清理机制
3. **索引系统优化** - 避免O(n)操作
4. **内存管理优化** - 对象池和位掩码缓存
### 🌟 实际应用价值
- **小型游戏**: 性能过剩,专注玩法
- **中型游戏**: 性能充足,适度优化
- **大型游戏**: 需要优化策略,但完全可行
- **服务器端**: 可处理大规模实体管理
---
**结论**: ECS框架本身性能优秀,能够满足大多数应用需求。性能瓶颈主要来自于**业务逻辑的算法选择**而非框架架构
**结论**: ECS框架达到了产品级性能标准能够满足从休闲小游戏到复杂RTS游戏的各种需求。框架层面的性能已经充分优化为开发者提供了坚实的性能基础

View File

@@ -7,15 +7,14 @@ QuerySystem 是 ECS Framework 中的高性能实体查询系统,支持多级
### 1. 获取查询系统
```typescript
import { Scene } from './ECS/Scene';
import { Entity } from './ECS/Entity';
import { Scene, Entity } from '@esengine/ecs-framework';
// 创建场景,查询系统会自动创建
const scene = new Scene();
const querySystem = scene.querySystem;
// 或者从Core获取当前场景的查询系统
import { Core } from './Core';
import { Core } from '@esengine/ecs-framework';
const currentQuerySystem = Core.scene?.querySystem;
```
@@ -206,7 +205,7 @@ console.log(`新增: ${diff.added.length}, 移除: ${diff.removed.length}`);
### 移动系统示例
```typescript
import { EntitySystem } from './ECS/Systems/EntitySystem';
import { EntitySystem } from '@esengine/ecs-framework';
class MovementSystem extends EntitySystem {
public update(): void {

6
package-lock.json generated
View File

@@ -1,6 +0,0 @@
{
"name": "ecs-framework",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

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"
]