更新文档并预留wasm接口
This commit is contained in:
42
README.md
42
README.md
@@ -14,7 +14,6 @@
|
||||
- 🔍 **查询系统** - 基于位掩码的高性能实体查询
|
||||
- 🛠️ **性能监控** - 内置性能监控工具,帮助优化游戏性能
|
||||
- 🎯 **对象池** - 内存管理优化,减少垃圾回收压力
|
||||
- 🎯 **纯ECS架构** - 专注于实体组件系统核心逻辑
|
||||
|
||||
## 📦 安装
|
||||
|
||||
@@ -78,17 +77,15 @@ function gameLoop() {
|
||||
### 2. 创建场景
|
||||
|
||||
```typescript
|
||||
import { Scene, Vector2, EntitySystem } from '@esengine/ecs-framework';
|
||||
import { Scene, EntitySystem } from '@esengine/ecs-framework';
|
||||
|
||||
class GameScene extends Scene {
|
||||
public initialize() {
|
||||
// 创建玩家实体
|
||||
const player = this.createEntity("Player");
|
||||
|
||||
// 设置位置
|
||||
player.position = new Vector2(100, 100);
|
||||
|
||||
// 添加自定义组件
|
||||
const position = player.addComponent(new PositionComponent(100, 100));
|
||||
const movement = player.addComponent(new MovementComponent());
|
||||
|
||||
// 添加系统
|
||||
@@ -107,19 +104,26 @@ Core.scene = new GameScene();
|
||||
### 3. 创建组件
|
||||
|
||||
```typescript
|
||||
import { Component, Vector2, Time } from '@esengine/ecs-framework';
|
||||
import { Component, Time } from '@esengine/ecs-framework';
|
||||
|
||||
class MovementComponent extends Component {
|
||||
public speed: number = 100;
|
||||
public direction: Vector2 = Vector2.zero;
|
||||
public direction = { x: 0, y: 0 };
|
||||
|
||||
public update() {
|
||||
if (this.direction.length > 0) {
|
||||
const movement = this.direction.multiply(this.speed * Time.deltaTime);
|
||||
this.entity.position = this.entity.position.add(movement);
|
||||
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. 创建系统
|
||||
@@ -142,15 +146,20 @@ class MovementSystem extends EntitySystem {
|
||||
## 📚 核心概念
|
||||
|
||||
### Entity(实体)
|
||||
实体是游戏世界中的基本对象,包含位置、旋转、缩放等基本属性,可以添加组件来扩展功能。
|
||||
实体是游戏世界中的基本对象,作为组件的容器。实体本身不包含游戏逻辑,所有功能都通过组件来实现。
|
||||
|
||||
```typescript
|
||||
import { Vector2 } from '@esengine/ecs-framework';
|
||||
|
||||
// 通过场景创建实体
|
||||
const entity = scene.createEntity("MyEntity");
|
||||
entity.position = new Vector2(100, 200);
|
||||
entity.rotation = Math.PI / 4;
|
||||
entity.scale = new Vector2(2, 2);
|
||||
|
||||
// 设置实体属性
|
||||
entity.tag = 1; // 设置标签用于分类
|
||||
entity.updateOrder = 0; // 设置更新顺序
|
||||
entity.enabled = true; // 设置启用状态
|
||||
|
||||
// 添加组件来扩展功能
|
||||
const positionComponent = entity.addComponent(new PositionComponent(100, 200));
|
||||
const healthComponent = entity.addComponent(new HealthComponent(100));
|
||||
```
|
||||
|
||||
### Component(组件)
|
||||
@@ -283,6 +292,7 @@ console.log("场景统计:", stats);
|
||||
## 📖 文档
|
||||
|
||||
- [快速入门](docs/getting-started.md) - 从零开始学习框架使用
|
||||
- [实体使用指南](docs/entity-guide.md) - 详细了解实体的所有功能和用法
|
||||
- [核心概念](docs/core-concepts.md) - 深入了解 ECS 架构和设计原理
|
||||
- [查询系统使用指南](docs/query-system-usage.md) - 学习高性能查询系统的详细用法
|
||||
|
||||
|
||||
465
docs/entity-guide.md
Normal file
465
docs/entity-guide.md
Normal file
@@ -0,0 +1,465 @@
|
||||
# 实体使用指南
|
||||
|
||||
本指南详细介绍 ECS Framework 中实体(Entity)的所有功能和使用方法。
|
||||
|
||||
## 实体概述
|
||||
|
||||
实体(Entity)是 ECS 架构中的核心概念之一,它作为组件的容器存在。实体本身不包含游戏逻辑,所有功能都通过添加不同的组件来实现。
|
||||
|
||||
### 实体的特点
|
||||
|
||||
- **轻量级容器**:实体只是组件的载体,不包含具体的游戏逻辑
|
||||
- **唯一标识**:每个实体都有唯一的ID和名称
|
||||
- **层次结构**:支持父子关系,可以构建复杂的实体层次
|
||||
- **高性能查询**:基于位掩码的组件查询系统
|
||||
- **生命周期管理**:完整的创建、更新、销毁流程
|
||||
|
||||
## 创建实体
|
||||
|
||||
### 基本创建方式
|
||||
|
||||
```typescript
|
||||
import { Scene } from '@esengine/ecs-framework';
|
||||
|
||||
// 通过场景创建实体
|
||||
const scene = new Scene();
|
||||
const entity = scene.createEntity("Player");
|
||||
|
||||
console.log(entity.name); // "Player"
|
||||
console.log(entity.id); // 唯一的数字ID
|
||||
```
|
||||
|
||||
### 使用流式API创建
|
||||
|
||||
```typescript
|
||||
import { Core } from '@esengine/ecs-framework';
|
||||
|
||||
// 使用ECS流式API
|
||||
const entity = Core.ecsAPI
|
||||
?.entity("Enemy")
|
||||
.withComponent(new PositionComponent(100, 200))
|
||||
.withComponent(new HealthComponent(50))
|
||||
.withTag(2)
|
||||
.build();
|
||||
```
|
||||
|
||||
## 实体属性
|
||||
|
||||
### 基本属性
|
||||
|
||||
```typescript
|
||||
// 实体名称 - 用于调试和标识
|
||||
entity.name = "Player";
|
||||
|
||||
// 实体ID - 只读,场景内唯一
|
||||
console.log(entity.id); // 例如: 1
|
||||
|
||||
// 标签 - 用于分类和快速查询
|
||||
entity.tag = 1; // 玩家标签
|
||||
entity.tag = 2; // 敌人标签
|
||||
|
||||
// 更新顺序 - 控制实体在系统中的处理优先级
|
||||
entity.updateOrder = 0; // 数值越小优先级越高
|
||||
```
|
||||
|
||||
### 状态控制
|
||||
|
||||
```typescript
|
||||
// 启用状态 - 控制实体是否参与更新和处理
|
||||
entity.enabled = true; // 启用实体
|
||||
entity.enabled = false; // 禁用实体
|
||||
|
||||
// 激活状态 - 控制实体及其子实体的活跃状态
|
||||
entity.active = true; // 激活实体
|
||||
entity.active = false; // 停用实体
|
||||
|
||||
// 检查层次结构中的激活状态
|
||||
if (entity.activeInHierarchy) {
|
||||
// 实体在整个层次结构中都是激活的
|
||||
}
|
||||
|
||||
// 检查销毁状态
|
||||
if (entity.isDestroyed) {
|
||||
// 实体已被销毁
|
||||
}
|
||||
```
|
||||
|
||||
### 更新间隔
|
||||
|
||||
```typescript
|
||||
// 控制实体更新频率
|
||||
entity.updateInterval = 1; // 每帧更新
|
||||
entity.updateInterval = 2; // 每2帧更新一次
|
||||
entity.updateInterval = 5; // 每5帧更新一次
|
||||
```
|
||||
|
||||
## 组件管理
|
||||
|
||||
### 添加组件
|
||||
|
||||
```typescript
|
||||
// 创建并添加组件
|
||||
const healthComponent = entity.addComponent(new HealthComponent(100));
|
||||
|
||||
// 使用工厂方法创建组件
|
||||
const positionComponent = entity.createComponent(PositionComponent, 100, 200);
|
||||
|
||||
// 批量添加组件
|
||||
const components = entity.addComponents([
|
||||
new PositionComponent(0, 0),
|
||||
new VelocityComponent(50, 0),
|
||||
new HealthComponent(100)
|
||||
]);
|
||||
```
|
||||
|
||||
### 获取组件
|
||||
|
||||
```typescript
|
||||
// 获取单个组件
|
||||
const health = entity.getComponent(HealthComponent);
|
||||
if (health) {
|
||||
console.log(`当前生命值: ${health.currentHealth}`);
|
||||
}
|
||||
|
||||
// 获取或创建组件(如果不存在则创建)
|
||||
const position = entity.getOrCreateComponent(PositionComponent, 0, 0);
|
||||
|
||||
// 获取多个同类型组件(如果组件可以重复添加)
|
||||
const allHealthComponents = entity.getComponents(HealthComponent);
|
||||
```
|
||||
|
||||
### 检查组件
|
||||
|
||||
```typescript
|
||||
// 检查是否拥有指定组件
|
||||
if (entity.hasComponent(HealthComponent)) {
|
||||
// 实体拥有生命值组件
|
||||
}
|
||||
|
||||
// 检查组件掩码(高性能)
|
||||
const mask = entity.componentMask;
|
||||
console.log(`组件掩码: ${mask.toString(2)}`);
|
||||
```
|
||||
|
||||
### 移除组件
|
||||
|
||||
```typescript
|
||||
// 移除指定组件实例
|
||||
const healthComponent = entity.getComponent(HealthComponent);
|
||||
if (healthComponent) {
|
||||
entity.removeComponent(healthComponent);
|
||||
}
|
||||
|
||||
// 按类型移除组件
|
||||
const removedHealth = entity.removeComponentByType(HealthComponent);
|
||||
|
||||
// 批量移除组件
|
||||
const removedComponents = entity.removeComponentsByTypes([
|
||||
HealthComponent,
|
||||
VelocityComponent
|
||||
]);
|
||||
|
||||
// 移除所有组件
|
||||
entity.removeAllComponents();
|
||||
```
|
||||
|
||||
## 层次结构管理
|
||||
|
||||
### 父子关系
|
||||
|
||||
```typescript
|
||||
// 创建父子实体
|
||||
const player = scene.createEntity("Player");
|
||||
const weapon = scene.createEntity("Weapon");
|
||||
const shield = scene.createEntity("Shield");
|
||||
|
||||
// 添加子实体
|
||||
player.addChild(weapon);
|
||||
player.addChild(shield);
|
||||
|
||||
// 获取父实体
|
||||
console.log(weapon.parent === player); // true
|
||||
|
||||
// 获取所有子实体
|
||||
const children = player.children;
|
||||
console.log(children.length); // 2
|
||||
|
||||
// 获取子实体数量
|
||||
console.log(player.childCount); // 2
|
||||
```
|
||||
|
||||
### 查找子实体
|
||||
|
||||
```typescript
|
||||
// 按名称查找子实体
|
||||
const weapon = player.findChild("Weapon");
|
||||
|
||||
// 递归查找子实体
|
||||
const deepChild = player.findChild("DeepChild", true);
|
||||
|
||||
// 按标签查找子实体
|
||||
const enemies = player.findChildrenByTag(2); // 查找所有敌人标签的子实体
|
||||
|
||||
// 递归按标签查找
|
||||
const allEnemies = player.findChildrenByTag(2, true);
|
||||
```
|
||||
|
||||
### 层次结构操作
|
||||
|
||||
```typescript
|
||||
// 移除子实体
|
||||
const removed = player.removeChild(weapon);
|
||||
|
||||
// 移除所有子实体
|
||||
player.removeAllChildren();
|
||||
|
||||
// 获取根实体
|
||||
const root = weapon.getRoot();
|
||||
|
||||
// 检查祖先关系
|
||||
if (player.isAncestorOf(weapon)) {
|
||||
// player 是 weapon 的祖先
|
||||
}
|
||||
|
||||
// 检查后代关系
|
||||
if (weapon.isDescendantOf(player)) {
|
||||
// weapon 是 player 的后代
|
||||
}
|
||||
|
||||
// 获取实体在层次结构中的深度
|
||||
const depth = weapon.getDepth(); // 从根实体开始计算的深度
|
||||
```
|
||||
|
||||
### 遍历子实体
|
||||
|
||||
```typescript
|
||||
// 遍历直接子实体
|
||||
player.forEachChild((child, index) => {
|
||||
console.log(`子实体 ${index}: ${child.name}`);
|
||||
});
|
||||
|
||||
// 递归遍历所有子实体
|
||||
player.forEachChild((child, index) => {
|
||||
console.log(`子实体 ${index}: ${child.name} (深度: ${child.getDepth()})`);
|
||||
}, true);
|
||||
```
|
||||
|
||||
## 实体生命周期
|
||||
|
||||
### 更新循环
|
||||
|
||||
```typescript
|
||||
// 手动更新实体(通常由场景自动调用)
|
||||
entity.update();
|
||||
|
||||
// 实体会自动调用所有组件的update方法
|
||||
class MyComponent extends Component {
|
||||
public update(): void {
|
||||
// 组件更新逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 销毁实体
|
||||
|
||||
```typescript
|
||||
// 销毁实体
|
||||
entity.destroy();
|
||||
|
||||
// 检查是否已销毁
|
||||
if (entity.isDestroyed) {
|
||||
console.log("实体已被销毁");
|
||||
}
|
||||
|
||||
// 销毁实体时会自动:
|
||||
// 1. 移除所有组件
|
||||
// 2. 从父实体中移除
|
||||
// 3. 销毁所有子实体
|
||||
// 4. 从场景中移除
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 组件缓存
|
||||
|
||||
```typescript
|
||||
// 预热组件缓存(提高后续访问性能)
|
||||
entity.warmUpComponentCache();
|
||||
|
||||
// 清理组件缓存
|
||||
entity.cleanupComponentCache();
|
||||
|
||||
// 获取缓存统计信息
|
||||
const cacheStats = entity.getComponentCacheStats();
|
||||
console.log(`缓存命中率: ${cacheStats.cacheStats.hitRate}`);
|
||||
console.log(`组件访问统计:`, cacheStats.accessStats);
|
||||
```
|
||||
|
||||
### 批量操作
|
||||
|
||||
```typescript
|
||||
// 批量添加组件(比单个添加更高效)
|
||||
const components = entity.addComponents([
|
||||
new PositionComponent(0, 0),
|
||||
new VelocityComponent(50, 0),
|
||||
new HealthComponent(100)
|
||||
]);
|
||||
|
||||
// 批量移除组件
|
||||
const removed = entity.removeComponentsByTypes([
|
||||
HealthComponent,
|
||||
VelocityComponent
|
||||
]);
|
||||
```
|
||||
|
||||
## 调试和监控
|
||||
|
||||
### 调试信息
|
||||
|
||||
```typescript
|
||||
// 获取详细的调试信息
|
||||
const debugInfo = entity.getDebugInfo();
|
||||
console.log("实体调试信息:", debugInfo);
|
||||
|
||||
// 调试信息包含:
|
||||
// - 基本属性(名称、ID、状态等)
|
||||
// - 组件信息(数量、类型、掩码等)
|
||||
// - 层次结构信息(父子关系、深度等)
|
||||
// - 性能统计(缓存命中率、访问统计等)
|
||||
```
|
||||
|
||||
### 实体比较
|
||||
|
||||
```typescript
|
||||
// 比较两个实体的优先级
|
||||
const result = entity1.compareTo(entity2);
|
||||
if (result < 0) {
|
||||
// entity1 优先级更高
|
||||
} else if (result > 0) {
|
||||
// entity2 优先级更高
|
||||
} else {
|
||||
// 优先级相同
|
||||
}
|
||||
|
||||
// 实体的字符串表示
|
||||
console.log(entity.toString()); // "Entity[Player:1]"
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 合理使用标签
|
||||
|
||||
```typescript
|
||||
// 定义标签常量
|
||||
const Tags = {
|
||||
PLAYER: 1,
|
||||
ENEMY: 2,
|
||||
PROJECTILE: 3,
|
||||
PICKUP: 4
|
||||
} as const;
|
||||
|
||||
// 使用标签进行分类
|
||||
player.tag = Tags.PLAYER;
|
||||
enemy.tag = Tags.ENEMY;
|
||||
```
|
||||
|
||||
### 2. 优化更新顺序
|
||||
|
||||
```typescript
|
||||
// 设置合理的更新顺序
|
||||
player.updateOrder = 0; // 玩家最先更新
|
||||
enemy.updateOrder = 1; // 敌人其次
|
||||
projectile.updateOrder = 2; // 投射物最后
|
||||
```
|
||||
|
||||
### 3. 合理使用层次结构
|
||||
|
||||
```typescript
|
||||
// 创建复合实体
|
||||
const tank = scene.createEntity("Tank");
|
||||
const turret = scene.createEntity("Turret");
|
||||
const barrel = scene.createEntity("Barrel");
|
||||
|
||||
// 建立层次关系
|
||||
tank.addChild(turret);
|
||||
turret.addChild(barrel);
|
||||
|
||||
// 这样可以通过控制父实体来影响整个层次结构
|
||||
tank.active = false; // 整个坦克都会被停用
|
||||
```
|
||||
|
||||
### 4. 组件缓存优化
|
||||
|
||||
```typescript
|
||||
// 对于频繁访问的组件,预热缓存
|
||||
entity.warmUpComponentCache();
|
||||
|
||||
// 定期清理不常用的缓存
|
||||
setInterval(() => {
|
||||
entity.cleanupComponentCache();
|
||||
}, 5000);
|
||||
```
|
||||
|
||||
### 5. 避免内存泄漏
|
||||
|
||||
```typescript
|
||||
// 确保正确销毁实体
|
||||
if (entity.isDestroyed) {
|
||||
return; // 避免操作已销毁的实体
|
||||
}
|
||||
|
||||
// 在适当的时候销毁不需要的实体
|
||||
if (enemy.getComponent(HealthComponent)?.isDead()) {
|
||||
enemy.destroy();
|
||||
}
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 实体可以在不同场景间移动吗?
|
||||
|
||||
A: 不可以。实体与场景紧密绑定,如果需要在场景间传递数据,应该序列化实体的组件数据,然后在新场景中重新创建。
|
||||
|
||||
### Q: 如何实现实体的位置、旋转、缩放?
|
||||
|
||||
A: 框架本身不提供这些属性,需要通过组件来实现:
|
||||
|
||||
```typescript
|
||||
class TransformComponent extends Component {
|
||||
public position = { x: 0, y: 0 };
|
||||
public rotation = 0;
|
||||
public scale = { x: 1, y: 1 };
|
||||
}
|
||||
|
||||
const transform = entity.addComponent(new TransformComponent());
|
||||
transform.position.x = 100;
|
||||
transform.rotation = Math.PI / 4;
|
||||
```
|
||||
|
||||
### Q: 实体的更新顺序如何影响性能?
|
||||
|
||||
A: 更新顺序主要影响游戏逻辑的执行顺序,对性能影响较小。但合理的更新顺序可以避免一些逻辑问题,比如确保输入处理在移动之前执行。
|
||||
|
||||
### Q: 如何处理大量实体的性能问题?
|
||||
|
||||
A:
|
||||
1. 使用对象池重用实体
|
||||
2. 合理使用组件缓存
|
||||
3. 避免不必要的组件查询
|
||||
4. 使用批量操作
|
||||
5. 定期清理销毁的实体
|
||||
|
||||
```typescript
|
||||
// 使用对象池
|
||||
class EntityPool extends Pool<Entity> {
|
||||
protected createObject(): Entity {
|
||||
return scene.createEntity("PooledEntity");
|
||||
}
|
||||
|
||||
protected resetObject(entity: Entity): void {
|
||||
entity.removeAllComponents();
|
||||
entity.active = true;
|
||||
entity.enabled = true;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -588,6 +588,7 @@ game.start();
|
||||
|
||||
现在您已经掌握了 ECS Framework 的基础用法,可以继续学习:
|
||||
|
||||
- [实体使用指南](entity-guide.md) - 详细了解实体的所有功能和用法
|
||||
- [核心概念](core-concepts.md) - 深入了解 ECS 架构和设计原理
|
||||
- [查询系统使用指南](query-system-usage.md) - 学习高性能查询系统的详细用法
|
||||
|
||||
|
||||
@@ -25,20 +25,54 @@ module.exports = {
|
||||
sourcemap: true
|
||||
},
|
||||
|
||||
// WebAssembly支持配置
|
||||
wasm: {
|
||||
enabled: false, // 暂时禁用,未来启用
|
||||
modules: {
|
||||
// 计划迁移到WebAssembly的模块
|
||||
core: {
|
||||
entry: './src/wasm/core.ts',
|
||||
output: 'ecs-core.wasm',
|
||||
features: ['query-system', 'math-utils']
|
||||
},
|
||||
physics: {
|
||||
entry: './src/wasm/physics.ts',
|
||||
output: 'ecs-physics.wasm',
|
||||
features: ['collision-detection', 'spatial-hash']
|
||||
}
|
||||
},
|
||||
// AssemblyScript配置
|
||||
assemblyscript: {
|
||||
target: 'wasm32',
|
||||
optimize: true,
|
||||
runtime: 'minimal'
|
||||
}
|
||||
},
|
||||
|
||||
// 游戏引擎特定配置
|
||||
engines: {
|
||||
laya: {
|
||||
// Laya引擎特定优化
|
||||
target: 'es5',
|
||||
polyfills: ['Promise', 'Object.assign'],
|
||||
globals: ['Laya']
|
||||
globals: ['Laya'],
|
||||
wasm: {
|
||||
// Laya环境下的WebAssembly配置
|
||||
loader: 'laya-wasm-loader',
|
||||
fallback: true // 支持降级到JavaScript
|
||||
}
|
||||
},
|
||||
|
||||
cocos: {
|
||||
// Cocos引擎特定优化
|
||||
target: 'es6',
|
||||
polyfills: [],
|
||||
globals: ['cc']
|
||||
globals: ['cc'],
|
||||
wasm: {
|
||||
// Cocos环境下的WebAssembly配置
|
||||
loader: 'cocos-wasm-loader',
|
||||
fallback: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -48,19 +82,33 @@ module.exports = {
|
||||
// 微信小游戏优化
|
||||
maxSize: '4MB',
|
||||
treeshaking: true,
|
||||
compression: 'gzip'
|
||||
compression: 'gzip',
|
||||
wasm: {
|
||||
// 微信小游戏WebAssembly支持
|
||||
enabled: true,
|
||||
maxWasmSize: '2MB', // WebAssembly模块大小限制
|
||||
preload: ['ecs-core.wasm'] // 预加载核心模块
|
||||
}
|
||||
},
|
||||
alipay: {
|
||||
// 支付宝小游戏优化
|
||||
maxSize: '4MB',
|
||||
treeshaking: true,
|
||||
compression: 'gzip'
|
||||
compression: 'gzip',
|
||||
wasm: {
|
||||
enabled: true,
|
||||
maxWasmSize: '2MB'
|
||||
}
|
||||
},
|
||||
bytedance: {
|
||||
// 字节跳动小游戏优化
|
||||
maxSize: '4MB',
|
||||
treeshaking: true,
|
||||
compression: 'gzip'
|
||||
compression: 'gzip',
|
||||
wasm: {
|
||||
enabled: true,
|
||||
maxWasmSize: '2MB'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -77,7 +125,23 @@ module.exports = {
|
||||
removeDebugger: true
|
||||
},
|
||||
// 内联小文件
|
||||
inlineThreshold: 1024
|
||||
inlineThreshold: 1024,
|
||||
// WebAssembly优化
|
||||
wasm: {
|
||||
// 启用WebAssembly优化
|
||||
optimize: true,
|
||||
// 内存配置
|
||||
memory: {
|
||||
initial: 1, // 初始内存页数 (64KB per page)
|
||||
maximum: 16, // 最大内存页数
|
||||
shared: false // 是否共享内存
|
||||
},
|
||||
// 导出配置
|
||||
exports: {
|
||||
memory: true,
|
||||
table: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 开发配置
|
||||
@@ -93,5 +157,22 @@ module.exports = {
|
||||
minify: true,
|
||||
optimization: true,
|
||||
bundleAnalyzer: true
|
||||
},
|
||||
|
||||
// 实验性功能
|
||||
experimental: {
|
||||
// 混合架构支持
|
||||
hybrid: {
|
||||
enabled: true,
|
||||
// 自动检测WebAssembly支持
|
||||
autoDetect: true,
|
||||
// 性能基准测试
|
||||
benchmark: true,
|
||||
// 降级策略
|
||||
fallback: {
|
||||
strategy: 'graceful', // 优雅降级
|
||||
modules: ['core', 'physics'] // 支持降级的模块
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
409
source/src/Utils/AccelerationProvider.ts
Normal file
409
source/src/Utils/AccelerationProvider.ts
Normal file
@@ -0,0 +1,409 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
439
source/src/Utils/WasmBridge.ts
Normal file
439
source/src/Utils/WasmBridge.ts
Normal file
@@ -0,0 +1,439 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2018",
|
||||
"target": "ES2020",
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "node",
|
||||
"lib": ["ES2020", "DOM"],
|
||||
|
||||
Reference in New Issue
Block a user